A CLI for publishing standard.site documents to ATProto sequoia.pub
standard site lexicon cli publishing
57
fork

Configure Feed

Select the types of activity you want to include in your feed.

allow setting `post-uri` as well to skip resolving that

I think this works with both bsky.app urls and at uris

authored by

Pascal Hertleif and committed by
Tangled
4191e6b0 582de0b7

+59 -19
+59 -19
packages/cli/src/components/sequoia-comments.js
··· 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") {