allow setting `post-uri` as well to skip resolving that 4191e6b0
I think this works with both bsky.app urls and at uris
Pascal Hertleif · 2026-04-22 13:58 1 file(s) · +59 −19
packages/cli/src/components/sequoia-comments.js +59 −19
12 12
 *   2. A <link rel="site.standard.document" href="at://..."> tag in the document head
13 13
 *
14 14
 * Attributes:
15 +
 *   - post-uri: Bluesky post as AT-URI (at://...) or bsky.app URL — skips PDS document lookup
15 16
 *   - document-uri: AT Protocol URI for the document (optional if link tag exists)
16 17
 *   - depth: Maximum depth of nested replies to fetch (default: 6)
17 18
 *   - hide: Set to "auto" to hide if no document link is detected
614 615
 * @param {string} postUri - AT Protocol URI for the post
615 616
 * @returns {Promise<Array>} Array of PostView objects
616 617
 */
618 +
/**
619 +
 * Normalise a user-supplied post reference to an AT-URI.
620 +
 * Accepts:
621 +
 *   - AT-URIs as-is:          at://did:plc:.../app.bsky.feed.post/rkey
622 +
 *   - bsky.app post URLs:     https://bsky.app/profile/<handle-or-did>/post/<rkey>
623 +
 * When the profile segment is already a DID no network request is made.
624 +
 * @param {string} uriOrUrl
625 +
 * @returns {Promise<string>} AT-URI
626 +
 */
627 +
async function resolvePostUri(uriOrUrl) {
628 +
	if (uriOrUrl.startsWith("at://")) return uriOrUrl;
629 +
630 +
	const match = uriOrUrl.match(
631 +
		/bsky\.app\/profile\/([^/?#]+)\/post\/([^/?#]+)/,
632 +
	);
633 +
	if (!match) throw new Error(`Cannot parse Bluesky URL: ${uriOrUrl}`);
634 +
635 +
	const [, handleOrDid, rkey] = match;
636 +
637 +
	let did = handleOrDid;
638 +
	if (!handleOrDid.startsWith("did:")) {
639 +
		const url = new URL(
640 +
			"https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle",
641 +
		);
642 +
		url.searchParams.set("handle", handleOrDid);
643 +
		const response = await fetch(url.toString());
644 +
		if (!response.ok)
645 +
			throw new Error(`Failed to resolve handle: ${response.status}`);
646 +
		did = (await response.json()).did;
647 +
	}
648 +
649 +
	return `at://${did}/app.bsky.feed.post/${rkey}`;
650 +
}
651 +
617 652
async function getQuotes(postUri) {
618 653
	const quotes = [];
619 654
	let cursor;
676 711
	}
677 712
678 713
	static get observedAttributes() {
679 -
		return ["document-uri", "depth", "hide"];
714 +
		return ["post-uri", "document-uri", "depth", "hide"];
680 715
	}
681 716
682 717
	connectedCallback() {
726 761
		this.state = { type: "loading" };
727 762
		this.render();
728 763
729 -
		const docUri = this.documentUri;
730 -
		if (!docUri) {
731 -
			this.state = { type: "no-document" };
732 -
			this.render();
733 -
			return;
734 -
		}
764 +
		try {
765 +
			// Resolve the post URI — either directly from the attribute or via the
766 +
			// document record (which requires a PDS roundtrip)
767 +
			const rawPostUri = this.getAttribute("post-uri");
768 +
			let postUri = rawPostUri ? await resolvePostUri(rawPostUri) : null;
769 +
			if (!postUri) {
770 +
				const docUri = this.documentUri;
771 +
				if (!docUri) {
772 +
					this.state = { type: "no-document" };
773 +
					this.render();
774 +
					return;
775 +
				}
735 776
736 -
		try {
737 -
			// Fetch the document record
738 -
			const document = await getDocument(docUri);
777 +
				const document = await getDocument(docUri);
778 +
				if (!document.bskyPostRef) {
779 +
					this.state = { type: "no-comments-enabled" };
780 +
					this.render();
781 +
					return;
782 +
				}
739 783
740 -
			// Check if document has a Bluesky post reference
741 -
			if (!document.bskyPostRef) {
742 -
				this.state = { type: "no-comments-enabled" };
743 -
				this.render();
744 -
				return;
784 +
				postUri = document.bskyPostRef.uri;
745 785
			}
746 786
747 -
			const postUrl = buildBskyAppUrl(document.bskyPostRef.uri);
748 -
			const blackskyPostUrl = buildBlackskyAppUrl(document.bskyPostRef.uri);
787 +
			const postUrl = buildBskyAppUrl(postUri);
788 +
			const blackskyPostUrl = buildBlackskyAppUrl(postUri);
749 789
750 790
			// Fetch thread and quotes in parallel; quote failures degrade gracefully
751 791
			const [threadResult, quotesResult] = await Promise.allSettled([
752 -
				getPostThread(document.bskyPostRef.uri, this.depth),
753 -
				getQuotes(document.bskyPostRef.uri),
792 +
				getPostThread(postUri, this.depth),
793 +
				getQuotes(postUri),
754 794
			]);
755 795
756 796
			if (threadResult.status === "rejected") {