a tool for shared writing and social publishing
0

Configure Feed

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

handle publishng newsletters programatically

Jared Pereira (May 7, 2026, 3:27 PM EDT) e91aa069 4156a3e4

+52 -1
+1 -1
app/api/inngest/client.ts
··· 87 87 schema: staticSchema<{ 88 88 publication_uri: string; 89 89 document_uri: string; 90 - root_entity: string; 90 + root_entity?: string; 91 91 }>(), 92 92 }), 93 93 };
+51
app/api/inngest/functions/sync_document_metadata.ts
··· 57 57 .select(); 58 58 }); 59 59 60 + const broadcast = await step.run( 61 + "maybe-claim-newsletter-broadcast", 62 + async () => { 63 + const { data: docInPub } = await supabaseServerClient 64 + .from("documents_in_publications") 65 + .select("publication") 66 + .eq("document", document_uri) 67 + .maybeSingle(); 68 + const publication_uri = docInPub?.publication; 69 + if (!publication_uri) return { skipped: "no_publication" as const }; 70 + 71 + const { data: settings } = await supabaseServerClient 72 + .from("publication_newsletter_settings") 73 + .select("enabled") 74 + .eq("publication", publication_uri) 75 + .maybeSingle(); 76 + if (!settings?.enabled) { 77 + return { skipped: "newsletter_not_enabled" as const }; 78 + } 79 + 80 + // Composite PK on (publication, document) is the idempotency guard: 81 + // re-runs on document updates and races with publishToPublication 82 + // both no-op here. 83 + const { data: inserted } = await supabaseServerClient 84 + .from("publication_post_sends") 85 + .upsert( 86 + { 87 + publication: publication_uri, 88 + document: document_uri, 89 + status: "pending", 90 + }, 91 + { onConflict: "publication,document", ignoreDuplicates: true }, 92 + ) 93 + .select(); 94 + if (!inserted || inserted.length === 0) { 95 + return { skipped: "already_sent_or_pending" as const }; 96 + } 97 + return { claimed: true as const, publication_uri }; 98 + }, 99 + ); 100 + 101 + if ("claimed" in broadcast) { 102 + await step.sendEvent("send-newsletter-broadcast", { 103 + name: "newsletter/post.send.requested", 104 + data: { 105 + publication_uri: broadcast.publication_uri, 106 + document_uri, 107 + }, 108 + }); 109 + } 110 + 60 111 if (!bsky_post_uri) { 61 112 return { handle: handleResult.handle }; 62 113 }