Something went wrong. Try again.
forked from tangled.org/core
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401import type { CommentView } from "#lib/components/comment/comments.ts";import type { CommitSummary } from "#lib/api/repo.ts";import type { Revspec } from "#lib/api/pullRoute.ts";import type { ReactionGroup, Reactor } from "#lib/components/reaction/reactions.ts";import { base, pullAuthor, pullCollaborator, sourceRepoDid, type FixtureFile, type PullFetchDataset} from "./pull.ts";
// worst realistic data for the pull page: every value is something a real pull,// person or repo can carry, spread across rows the way real data mixes it
export type PullDatasetName = "demo" | "worst" | "empty" | "one" | "huge";
export interface PullDataset { title: string; body: string; authorHandle?: string; targetBranch: string; sourceBranch: string; counts: { stars: number; issues: number; pulls: number; forks: number }; commits: CommitSummary[]; versions: (Revspec & { createdAt: string })[]; comments: CommentView[]; reactions: ReactionGroup[]; fetch: PullFetchDataset;}
const oid = (n: number) => Array.from({ length: 5 }, (_, i) => ((n + 1) * 2_654_435_761 + i * 40_503).toString(16).slice(-8).padStart(8, "0") ).join("");
const changeIdOf = (n: number) => Array.from( { length: 32 }, (_, i) => "klmnopqrstuvwxyz"[((n + 7) * 31 + i * 17 + i * n) % 16] ).join("");
const longHandle = "aleksandra-wisniewska-kowalczyk.northwind-industries-holdings.example.com";const jo = { did: "did:plc:jo", handle: "jo.tngl.sh" } satisfies Reactor;const konstantin = { did: "did:plc:konstantin", handle: "konstantin-oberhauser-wettstein.eu.example.org"} satisfies Reactor;const unresolved = { did: "did:plc:ewvi7nxzyoun6zhxrhs64oiz", handle: "handle.invalid"} satisfies Reactor;
const targetBranch = "release/2026-q4/hotfix-notification-preferences-migration-rollback";const sourceBranch = "aleksandra/feature/notifications-sidebar-unread-state-and-mention-fanout-v2-rebased";
const signOffs = [ "Signed-off-by: Aleksandra Wiśniewska-Kowalczyk <aleksandra@northwind-industries-holdings.example.com>", "Signed-off-by: Christopher Alexander Montgomery III <chris.montgomery@example.org>", "Co-authored-by: 王秀英 <xiuying@example.com>", "Reviewed-by: Jo <jo@example.com>"].join("\n");
interface CommitSeed { subject: string; body?: string; authorName: string; authorEmail: string; authorDid?: string; authorHandle?: string; changeId?: boolean; commentCount?: number; stat?: { insertions: number; deletions: number };}
const worstSeeds: CommitSeed[] = [ { subject: "appview/pages/templates/repo/pulls/fragments/pullCompareForksBranches.html: keep the fork picker usable when the source repo has thousands of branches", body: `The picker rendered every branch up front, which locked the tab for several seconds onlarge forks.\n\n${signOffs}`, authorName: "Aleksandra Wiśniewska-Kowalczyk", authorEmail: "aleksandra@northwind-industries-holdings.example.com", authorDid: pullAuthor.did, authorHandle: longHandle, changeId: true, commentCount: 12, stat: { insertions: 1_284, deletions: 967 } }, { subject: "Benachrichtigungseinstellungen", authorName: "Jo", authorEmail: "jo@example.com", authorDid: jo.did, authorHandle: jo.handle, changeId: true, commentCount: 1, stat: { insertions: 1, deletions: 0 } }, { subject: "", authorName: "J", authorEmail: "a@b.co", changeId: false, stat: { insertions: 0, deletions: 0 } }, { subject: "pnpm-lock.yaml: bump every dependency", authorName: "Christopher Alexander Montgomery III", authorEmail: "bartholomew.fitzgerald@northwind-industries-holdings.example.com", changeId: false, stat: { insertions: 128_402, deletions: 97_316 } }, { subject: "🦊 fix the <script> escaping in & around **mentions**", authorName: "🦊 Fox", authorEmail: "first.last+billing-notifications@example.com", changeId: true, commentCount: 3, stat: { insertions: 42, deletions: 7 } }, { subject: "إصلاح ترتيب الإشعارات في الشريط الجانبي", authorName: "نور الهدى عبد الرحمن", authorEmail: "nour@example.org", changeId: true, stat: { insertions: 18, deletions: 18 } }, { subject: "Đặng Thị Ngọc Hân: sửa lỗi hiển thị thông báo", authorName: "Đặng Thị Ngọc Hân", authorEmail: "han@example.com", changeId: true, stat: { insertions: 3, deletions: 1 } }, { subject: "wip", authorName: konstantin.handle, authorEmail: "konstantin@example.org", authorDid: konstantin.did, authorHandle: konstantin.handle, changeId: true }];
const commitsOf = (seeds: CommitSeed[], count = seeds.length): CommitSummary[] => Array.from({ length: count }, (_, index) => { const seed = seeds[index % seeds.length]!; const hash = oid(index); return { hash, shortHash: hash.slice(0, 8), subject: seed.subject, body: seed.body ?? "", authorName: seed.authorName, authorEmail: seed.authorEmail, authorDid: seed.authorDid, authorHandle: seed.authorHandle, when: new Date(Date.UTC(2026, 9, 21, 12) - index * 3_600_000).toISOString(), parent: index === count - 1 ? base : oid(index + 1), changeId: seed.changeId === false ? undefined : changeIdOf(index), commentCount: seed.commentCount ?? 0, stat: seed.stat }; });
// each version brings one more commit, the newest on topconst versionsOf = (commits: CommitSummary[], count: number) => Array.from({ length: count }, (_, index) => ({ base, head: commits[Math.max(count - 1 - index, 0)]!.hash, createdAt: new Date(Date.UTC(2026, 9, 1, 9) + index * 86_400_000).toISOString() }));
const commentUri = (did: string, rkey: string) => `at://${did}/sh.tangled.feed.comment/${rkey}`;
const reactors = (count: number): Reactor[] => Array.from({ length: Math.min(count, 3) }, (_, index) => ({ did: `did:plc:reactor${index}`, handle: `reactor-${index}.example.com` }));
const everyReaction = (scale: number): ReactionGroup[] => (["👍", "👎", "😆", "🎉", "🫤", "❤️", "🚀", "👀"] as const).map((kind, index) => ({ kind, count: Math.max(1, Math.round(scale / (index + 1))), users: reactors(scale), isReacted: index === 0 }));
const longUrl = "https://example.com/workspaces/acme/projects/q3-launch/docs/9f8e7d6c5b4a?tab=comments&filter=unresolved&highlight=notification-preferences-migration";
interface CommentSeed { who: Reactor; body: string; pin?: number; editedAt?: string; deleted?: boolean; reactions?: ReactionGroup[]; createdAt?: string;}
const worstCommentSeeds = (commits: CommitSummary[]): CommentSeed[] => [ { who: konstantin, body: `See ${longUrl}
\`\`\`const preferences = await db.select().from(notificationPreferences).where(and(eq(notificationPreferences.did, did), eq(notificationPreferences.kind, "mention"), isNull(notificationPreferences.deletedAt)));\`\`\``, pin: 0, reactions: everyReaction(1_284) }, { who: jo, body: "lgtm" }, { who: unresolved, body: "<script>alert(1)</script> & **this is not bold**\nline two of a plain comment" }, { who: { did: pullAuthor.did, handle: longHandle }, body: "Benachrichtigungseinstellungsverwaltungsoberfläche", editedAt: "2026-10-21T08:00:00Z" }, { who: konstantin, body: "نعم، هذا يحل المشكلة في الشريط الجانبي. شكراً!", pin: 4 }, { who: jo, body: "", deleted: true }, { who: { did: pullCollaborator.did, handle: pullCollaborator.handle }, body: "Đặng Thị Ngọc Hân đã xem xét thay đổi này.", // a commenter with a fast clock createdAt: new Date(Date.now() + 3 * 60_000).toISOString() }, { who: { did: pullAuthor.did, handle: longHandle }, body: `| file | before | after | p99 render (ms) | notes || --- | --- | --- | --- | --- || \`src/lib/components/notifications/NotificationPreferencesMigrationRollback.svelte\` | 1,284 | 12 | 0.30000000000000004 | first paint only |`, pin: commits.length - 1 }];
const commentsOf = (seeds: CommentSeed[], commits: CommitSummary[], count = seeds.length) => Array.from({ length: count }, (_, index): CommentView => { const seed = seeds[index % seeds.length]!; const rkey = `3mw${index.toString(36).padStart(6, "0")}`; const pinned = seed.pin === undefined ? undefined : commits[seed.pin % commits.length]; return { uri: commentUri(seed.who.did, rkey), rkey, authorDid: seed.who.did, authorHandle: seed.who.handle, createdAt: seed.createdAt ?? new Date(Date.UTC(2026, 9, 2, 9) + index * 7_200_000).toISOString(), editedAt: seed.editedAt, body: seed.body, deleted: seed.deleted, reactions: seed.reactions, embed: pinned ? ({ repo: sourceRepoDid, commit: pinned.hash } as CommentView["embed"]) : undefined }; });
const deepPath = "packages/notifications/src/lib/components/notifications/preferences/migrations/2026-10-21T120000Z_rollback_notification_preferences_mention_fanout.ts";
const minified = `export const a=${JSON.stringify(Array.from({ length: 60 }, (_, i) => `notification-preference-key-${i}`))};\n`;
const worstFiles: FixtureFile[] = [ { path: deepPath, row: 0, old: "export const up = () => {};\n", next: "export const up = () => {};\nexport const down = () => {};\n" }, { path: "src/lib/components/notifications/NotificationPreferencesMigrationRollbackConfirmationDialog.svelte", oldPath: "src/lib/components/notifications/NotificationPreferencesRollbackDialog.svelte", row: 1, old: "<dialog></dialog>\n", next: "<dialog open></dialog>\n" }, { path: "static/IMG_20250914_183022_HDR_portrait_edited_edited.HEIC", row: 2, binary: true }, { path: "build/notifications.min.js", row: 3, old: "", next: minified }, { path: "src/lib/legacy/unread.ts", row: 4, old: "export const unread = 0;\n" }];
const conflictPaths = (count: number) => Array.from( { length: count }, (_, index) => [ deepPath, "appview/pages/templates/repo/pulls/fragments/pullCompareForksBranches.html", "src/lib/components/notifications/NotificationPreferencesMigrationRollbackConfirmationDialog.svelte", "README.md", "docs/ストーリー/通知設定.md" ][index % 5]! + (index < 5 ? "" : `.${index}`) ).map((filename) => ({ filename, reason: "both modified" }));
const body = `Notifications land in the sidebar, and a visit marks them read. The full design discussion is at ${longUrl} and the tracking issue is at https://tangled.example.com/@${longHandle}/core/issues/1284.
\`\`\`pnpm --filter @tangled/web exec vitest --run --project server src/lib/components/notifications/NotificationPreferencesMigrationRollback.test.ts\`\`\`
| key | before | after || --- | --- | --- || \`notificationPreferences.mentionFanoutRollbackEnabled\` | true | false |
${"Lorem ipsum is not what people paste; they paste stack traces. ".repeat(4)}`;
const worstCommits = commitsOf(worstSeeds, 40);const worstVersions = versionsOf(worstCommits, 14);
const worst: PullDataset = { title: "appview/pages/templates/repo/pulls/fragments/pullCompareForksBranches.html: keep the fork picker usable when the source repo has thousands of branches and the target branch name is long", body, authorHandle: longHandle, targetBranch, sourceBranch, counts: { stars: 12_847, issues: 1_284, pulls: 10_402, forks: 1 }, commits: worstCommits, versions: worstVersions, comments: commentsOf(worstCommentSeeds(worstCommits), worstCommits, 16), reactions: everyReaction(1_284), fetch: { commits: worstCommits, head: worstVersions.at(-1)!.head, files: worstFiles, conflicts: conflictPaths(23) }};
const oneCommits = commitsOf([worstSeeds[1]!]);const oneVersions = versionsOf(oneCommits, 1);
const one: PullDataset = { title: "J", body: "x", authorHandle: jo.handle, targetBranch: "a", sourceBranch: "b", counts: { stars: 1, issues: 1, pulls: 1, forks: 1 }, commits: oneCommits, versions: oneVersions, comments: commentsOf([{ who: jo, body: "ok", pin: 0 }], oneCommits), reactions: [{ kind: "👍", count: 1, users: reactors(1), isReacted: false }], fetch: { commits: oneCommits, head: oneVersions[0]!.head, files: [worstFiles[0]!], conflicts: conflictPaths(1) }};
const empty: PullDataset = { title: "", body: "", authorHandle: undefined, targetBranch: "main", sourceBranch: "main", counts: { stars: 0, issues: 0, pulls: 0, forks: 0 }, commits: [], versions: [], comments: [], reactions: [], fetch: { commits: [], head: base, files: [], conflicts: [] }};
const hugeCommits = commitsOf(worstSeeds, 1_000);const hugeVersions = versionsOf(hugeCommits, 60);
const huge: PullDataset = { ...worst, counts: { stars: 1_284_000, issues: 98_765, pulls: 123_456, forks: 54_321 }, commits: hugeCommits, versions: hugeVersions, comments: commentsOf(worstCommentSeeds(hugeCommits), hugeCommits, 400), fetch: { commits: hugeCommits, head: hugeVersions.at(-1)!.head, files: worstFiles, conflicts: conflictPaths(412) }};
export const pullDatasets: Record<Exclude<PullDatasetName, "demo">, PullDataset> = { worst, empty, one, huge};