Skip to content

Instantly share code, notes, and snippets.

@jasikpark
Created February 12, 2026 17:57
Show Gist options
  • Select an option

  • Save jasikpark/41551000aba7ce3d86f3a5bf5bc50789 to your computer and use it in GitHub Desktop.

Select an option

Save jasikpark/41551000aba7ce3d86f3a5bf5bc50789 to your computer and use it in GitHub Desktop.
implementing a links page w/ atproto (coded w/ Claude Sonnet 4.5)
import { AtpAgent } from "@atproto/api";
const DID = "did:plc:3tkrsjzdao4vqjrxwzynbfnu";
export type LinkItem = {
url: string;
title: string;
description?: string;
siteName?: string;
contentType?: string;
author?: string;
tags?: string[];
createdAt: Date;
source: "semble" | "margin";
};
function parseRecordDate(val: Record<string, unknown>, recordUri: string): Date {
const createdAt = val.createdAt as string | undefined;
if (createdAt) {
return new Date(createdAt);
}
const rkey = recordUri.split("/").pop();
return new Date(rkey ?? 0);
}
async function fetchSembleCards(
agent: AtpAgent,
did: string,
limit: number,
signal: AbortSignal
): Promise<LinkItem[]> {
const res = await agent.com.atproto.repo.listRecords(
{
repo: did,
collection: "network.cosmik.card",
limit,
},
{ signal }
);
const items: LinkItem[] = [];
for (const record of res.data.records) {
const val = record.value as Record<string, unknown>;
const content = val.content as Record<string, unknown> | undefined;
const url = content?.url as string | undefined;
if (!url) {
continue;
}
const metadata = content?.metadata as Record<string, unknown> | undefined;
items.push({
url,
title: (metadata?.title as string) || url,
description: metadata?.description as string | undefined,
siteName: metadata?.siteName as string | undefined,
contentType: metadata?.type as string | undefined,
author: metadata?.author as string | undefined,
createdAt: parseRecordDate(val, record.uri),
source: "semble",
});
}
return items;
}
async function fetchMarginBookmarks(
agent: AtpAgent,
did: string,
limit: number,
signal: AbortSignal
): Promise<LinkItem[]> {
const res = await agent.com.atproto.repo.listRecords(
{
repo: did,
collection: "at.margin.bookmark",
limit,
},
{ signal }
);
const items: LinkItem[] = [];
for (const record of res.data.records) {
const val = record.value as Record<string, unknown>;
const url = val.source as string | undefined;
if (!url) {
continue;
}
items.push({
url,
title: (val.title as string) || url,
description: val.description as string | undefined,
tags: val.tags as string[] | undefined,
createdAt: parseRecordDate(val, record.uri),
source: "margin",
});
}
return items;
}
export const LINKS_PAGE_SIZE = 20;
export async function fetchAllLinks(limit = 100): Promise<{ links: LinkItem[]; errors: string[] }> {
const agent = new AtpAgent({ service: "https://bsky.social" });
const controller = new AbortController();
const timeout = setTimeout(() => {
controller.abort();
}, 10_000);
try {
const [sembleResult, marginResult] = await Promise.allSettled([
fetchSembleCards(agent, DID, limit, controller.signal),
fetchMarginBookmarks(agent, DID, limit, controller.signal),
]);
const links: LinkItem[] = [];
const errors: string[] = [];
if (sembleResult.status === "fulfilled") {
links.push(...sembleResult.value);
} else {
errors.push(`Semble: ${String(sembleResult.reason)}`);
}
if (marginResult.status === "fulfilled") {
links.push(...marginResult.value);
} else {
errors.push(`margin.at: ${String(marginResult.reason)}`);
}
// Deduplicate by URL, preferring the first occurrence
const seen = new Set<string>();
const deduped: LinkItem[] = [];
for (const link of links) {
const normalized = link.url.replace(/\/+$/, "");
if (!seen.has(normalized)) {
seen.add(normalized);
deduped.push(link);
}
}
// Sort newest first
deduped.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
return { links: deduped, errors };
} finally {
clearTimeout(timeout);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment