BlueSky & more on desktop
lazurite.stormlightlabs.org/
tauri
rust
typescript
bluesky
appview
atproto
solid
1import { beforeEach, describe, expect, it } from "vitest";
2import {
3 applyFeedPreferences,
4 buildPublicPostUrl,
5 buildThreadOverlayRoute,
6 decodeThreadRouteUri,
7 getFeedCommand,
8 getQuotedPresentation,
9 getThreadOverlayUri,
10 getUnknownEmbedTelemetryForTests,
11 type NormalizedEmbed,
12 normalizeEmbed,
13 parseFeedResponse,
14 parseThreadResponse,
15 resetUnknownEmbedTelemetryForTests,
16} from "../feeds";
17import type { FeedViewPost, FeedViewPrefItem, SavedFeedItem } from "../types";
18
19function createFeedItem(overrides: Partial<FeedViewPost> = {}): FeedViewPost {
20 return {
21 post: {
22 author: { did: "did:plc:alice", handle: "alice.test" },
23 cid: "cid-1",
24 indexedAt: "2026-03-28T12:00:00.000Z",
25 likeCount: 10,
26 record: { createdAt: "2026-03-28T12:00:00.000Z", text: "hello world" },
27 uri: "at://did:plc:alice/app.bsky.feed.post/1",
28 },
29 ...overrides,
30 };
31}
32
33function createPref(overrides: Partial<FeedViewPrefItem> = {}): FeedViewPrefItem {
34 return {
35 feed: "following",
36 hideQuotePosts: false,
37 hideReplies: false,
38 hideRepliesByLikeCount: null,
39 hideRepliesByUnfollowed: false,
40 hideReposts: false,
41 ...overrides,
42 };
43}
44
45function walkNormalizedEmbeds(root: NormalizedEmbed, visit: (embed: NormalizedEmbed) => void) {
46 visit(root);
47 if (root.kind === "record") {
48 for (const nested of root.quoted.normalizedEmbeds) {
49 walkNormalizedEmbeds(nested, visit);
50 }
51 }
52 if (root.kind === "recordWithMedia") {
53 if (root.media) {
54 walkNormalizedEmbeds(root.media, visit);
55 }
56 for (const nested of root.quoted.normalizedEmbeds) {
57 walkNormalizedEmbeds(nested, visit);
58 }
59 }
60}
61
62describe("feed helpers", () => {
63 beforeEach(() => {
64 resetUnknownEmbedTelemetryForTests();
65 });
66
67 it("filters reposts, replies, quote posts, and low-like replies", () => {
68 const base = createFeedItem();
69 const repost = createFeedItem({
70 post: { ...base.post, uri: "at://did:plc:alice/app.bsky.feed.post/2" },
71 reason: {
72 $type: "app.bsky.feed.defs#reasonRepost",
73 by: { did: "did:plc:bob", handle: "bob.test" },
74 indexedAt: "2026-03-28T12:10:00.000Z",
75 },
76 });
77 const reply = createFeedItem({
78 post: { ...base.post, likeCount: 2, uri: "at://did:plc:alice/app.bsky.feed.post/3" },
79 reply: {
80 parent: { $type: "app.bsky.feed.defs#postView", ...base.post },
81 root: { $type: "app.bsky.feed.defs#postView", ...base.post },
82 },
83 });
84 const quote = createFeedItem({
85 post: {
86 ...base.post,
87 embed: { $type: "app.bsky.embed.record#view", record: { uri: "at://did:plc:bob/app.bsky.feed.post/9" } },
88 uri: "at://did:plc:alice/app.bsky.feed.post/4",
89 },
90 });
91
92 const filtered = applyFeedPreferences(
93 [base, repost, reply, quote],
94 createPref({ hideQuotePosts: true, hideReplies: true, hideReposts: true }),
95 );
96
97 expect(filtered).toEqual([base]);
98 expect(applyFeedPreferences([reply], createPref({ hideRepliesByLikeCount: 5 }))).toEqual([]);
99 });
100
101 it("treats zero as an active reply-like threshold", () => {
102 const reply = createFeedItem({
103 post: { ...createFeedItem().post, likeCount: 0, uri: "at://did:plc:alice/app.bsky.feed.post/zero" },
104 reply: {
105 parent: { $type: "app.bsky.feed.defs#postView", ...createFeedItem().post },
106 root: { $type: "app.bsky.feed.defs#postView", ...createFeedItem().post },
107 },
108 });
109
110 expect(applyFeedPreferences([reply], createPref({ hideRepliesByLikeCount: 0 }))).toEqual([reply]);
111 expect(applyFeedPreferences([reply], createPref({ hideRepliesByLikeCount: 1 }))).toEqual([]);
112 });
113
114 it("detects replies from the embedded record and respects the unfollowed reply filter", () => {
115 const base = createFeedItem();
116 const unfollowedReply = createFeedItem({
117 post: {
118 ...base.post,
119 author: { did: "did:plc:bob", handle: "bob.test", viewer: { following: null } },
120 record: {
121 createdAt: "2026-03-28T12:00:00.000Z",
122 reply: { parent: { uri: "at://did:plc:alice/app.bsky.feed.post/1" } },
123 text: "reply from unfollowed author",
124 },
125 uri: "at://did:plc:bob/app.bsky.feed.post/2",
126 },
127 });
128
129 expect(applyFeedPreferences([unfollowedReply], createPref({ hideReplies: true }))).toEqual([]);
130 expect(applyFeedPreferences([unfollowedReply], createPref({ hideRepliesByUnfollowed: true }))).toEqual([]);
131 expect(applyFeedPreferences([unfollowedReply], createPref({ hideRepliesByUnfollowed: false }))).toEqual([
132 unfollowedReply,
133 ]);
134 });
135
136 it("builds feed commands per saved feed type", () => {
137 const timeline: SavedFeedItem = { id: "following", pinned: true, type: "timeline", value: "following" };
138 const feed: SavedFeedItem = {
139 id: "custom",
140 pinned: true,
141 type: "feed",
142 value: "at://did:plc:alice/app.bsky.feed.generator/custom",
143 };
144 const list: SavedFeedItem = {
145 id: "list",
146 pinned: false,
147 type: "list",
148 value: "at://did:plc:alice/app.bsky.graph.list/list",
149 };
150
151 expect(getFeedCommand(timeline)).toEqual({ args: expect.any(Function), name: "get_timeline" });
152 expect(getFeedCommand(feed).name).toBe("get_feed");
153 expect(getFeedCommand(list).name).toBe("get_list_feed");
154 expect(getFeedCommand(list).args("cursor-1", 30)).toEqual({ cursor: "cursor-1", limit: 30, uri: list.value });
155 });
156
157 it("encodes and decodes thread overlays", () => {
158 const uri = "at://did:plc:alice/app.bsky.feed.post/abc123";
159
160 expect(buildThreadOverlayRoute("/profile/alice", "", uri)).toBe(
161 "/profile/alice?thread=at%3A%2F%2Fdid%3Aplc%3Aalice%2Fapp.bsky.feed.post%2Fabc123",
162 );
163 expect(buildThreadOverlayRoute("/profile/alice", "?foo=bar", uri)).toBe(
164 "/profile/alice?foo=bar&thread=at%3A%2F%2Fdid%3Aplc%3Aalice%2Fapp.bsky.feed.post%2Fabc123",
165 );
166 expect(buildThreadOverlayRoute("/profile/alice", "?foo=bar&thread=old", null)).toBe("/profile/alice?foo=bar");
167 expect(getThreadOverlayUri("?thread=at%3A%2F%2Fdid%3Aplc%3Aalice%2Fapp.bsky.feed.post%2Fabc123")).toBe(uri);
168 expect(decodeThreadRouteUri("at%3A%2F%2Fdid%3Aplc%3Aalice%2Fapp.bsky.feed.post%2Fabc123")).toBe(uri);
169 expect(decodeThreadRouteUri(uri)).toBe(uri);
170 expect(decodeThreadRouteUri("https%3A%2F%2Fexample.com")).toBeNull();
171 });
172
173 it("builds public post urls from handles and post rkeys", () => {
174 expect(buildPublicPostUrl(createFeedItem().post)).toBe("https://bsky.app/profile/alice.test/post/1");
175 });
176
177 it("builds feed/list quoted-record presentations without thread URIs", () => {
178 const feedPresentation = getQuotedPresentation({
179 $type: "app.bsky.embed.record#view",
180 record: {
181 $type: "app.bsky.feed.defs#generatorView",
182 creator: { did: "did:plc:alice", handle: "alice.test" },
183 displayName: "For You",
184 uri: "at://did:plc:alice/app.bsky.feed.generator/for-you",
185 },
186 });
187 const listPresentation = getQuotedPresentation({
188 $type: "app.bsky.embed.record#view",
189 record: {
190 $type: "app.bsky.graph.defs#listView",
191 creator: { did: "did:plc:alice", handle: "alice.test" },
192 name: "Reading List",
193 uri: "at://did:plc:alice/app.bsky.graph.list/reading-list",
194 },
195 });
196
197 expect(feedPresentation).toMatchObject({
198 href: "https://bsky.app/profile/alice.test/feed/for-you",
199 kind: "feed",
200 title: "Embedded feed",
201 uri: null,
202 });
203 expect(listPresentation).toMatchObject({
204 href: "https://bsky.app/profile/alice.test/lists/reading-list",
205 kind: "list",
206 title: "Embedded list",
207 uri: null,
208 });
209 });
210
211 it("keeps post quoted-record presentations thread-openable", () => {
212 const postPresentation = getQuotedPresentation({
213 $type: "app.bsky.embed.record#view",
214 record: {
215 $type: "app.bsky.embed.record#viewRecord",
216 author: { did: "did:plc:bob", handle: "bob.test" },
217 uri: "at://did:plc:bob/app.bsky.feed.post/123",
218 value: { text: "quoted body" },
219 },
220 });
221
222 expect(postPresentation).toMatchObject({
223 href: "https://bsky.app/profile/bob.test/post/123",
224 kind: "post",
225 text: "quoted body",
226 title: "Quoted post",
227 uri: "at://did:plc:bob/app.bsky.feed.post/123",
228 });
229 });
230
231 it("extracts text, facets, and embed media from quoted postView records", () => {
232 const presentation = getQuotedPresentation({
233 $type: "app.bsky.embed.record#view",
234 record: {
235 $type: "app.bsky.feed.defs#postView",
236 author: { did: "did:plc:bob", handle: "bob.test" },
237 record: {
238 text: "quoted postView body",
239 facets: [{
240 features: [{ $type: "app.bsky.richtext.facet#link", uri: "https://example.com" }],
241 index: { byteEnd: 20, byteStart: 0 },
242 }],
243 embed: {
244 $type: "app.bsky.embed.images#view",
245 images: [{ fullsize: "https://cdn.example.com/postview-image.png" }],
246 },
247 },
248 uri: "at://did:plc:bob/app.bsky.feed.post/postview",
249 },
250 });
251
252 expect(presentation).toMatchObject({
253 href: "https://bsky.app/profile/bob.test/post/postview",
254 kind: "post",
255 text: "quoted postView body",
256 uri: "at://did:plc:bob/app.bsky.feed.post/postview",
257 });
258 expect(presentation.facets).toHaveLength(1);
259 expect(presentation.normalizedEmbeds.map((embed) => embed.kind)).toEqual(["images"]);
260 });
261
262 it("hydrates quoted record image blobs into renderable CDN URLs", () => {
263 const presentation = getQuotedPresentation({
264 $type: "app.bsky.embed.record#view",
265 record: {
266 $type: "app.bsky.feed.defs#postView",
267 author: { did: "did:plc:bob", handle: "bob.test" },
268 record: {
269 embed: {
270 $type: "app.bsky.embed.images",
271 images: [{ alt: "Blob image", image: { mimeType: "image/jpeg", ref: { $link: "bafyblobimg" } } }],
272 },
273 text: "",
274 },
275 uri: "at://did:plc:bob/app.bsky.feed.post/blob-post",
276 },
277 });
278
279 expect(presentation.normalizedEmbeds).toHaveLength(1);
280 expect(presentation.normalizedEmbeds[0]?.kind).toBe("images");
281 if (presentation.normalizedEmbeds[0]?.kind === "images") {
282 expect(presentation.normalizedEmbeds[0].embed.images[0]).toMatchObject({
283 fullsize: "https://cdn.bsky.app/img/feed_fullsize/plain/did%3Aplc%3Abob/bafyblobimg@jpeg",
284 thumb: "https://cdn.bsky.app/img/feed_thumbnail/plain/did%3Aplc%3Abob/bafyblobimg@jpeg",
285 });
286 }
287 });
288
289 it("extracts quoted post embeds and keeps unknown custom embeds in the unknown list", () => {
290 const presentation = getQuotedPresentation({
291 $type: "app.bsky.embed.record#view",
292 record: {
293 $type: "app.bsky.embed.record#viewRecord",
294 author: { did: "did:plc:bob", handle: "bob.test" },
295 embeds: [
296 { $type: "app.bsky.embed.images#view", images: [{ fullsize: "https://cdn.example.com/quoted-image.png" }] },
297 { $type: "app.bsky.embed.video#view", playlist: "https://cdn.example.com/quoted-video.m3u8" },
298 { $type: "app.bsky.embed.external#view", external: { uri: "https://example.com", title: "External card" } },
299 { $type: "app.bsky.embed.unsupported#view" },
300 ],
301 uri: "at://did:plc:bob/app.bsky.feed.post/123",
302 value: { text: "quoted body" },
303 },
304 });
305
306 expect(presentation.normalizedEmbeds).toHaveLength(4);
307 expect(presentation.normalizedEmbeds.map((embed) => embed.kind)).toEqual([
308 "images",
309 "video",
310 "external",
311 "unknown",
312 ]);
313 expect(presentation.unknownEmbeds).toHaveLength(1);
314 });
315
316 it("normalizes every official top-level embed kind without treating them as unknown", () => {
317 const fixtures = [{
318 expectedKind: "images",
319 value: { $type: "app.bsky.embed.images#view", images: [{ fullsize: "https://cdn.example.com/top-image.png" }] },
320 }, {
321 expectedKind: "video",
322 value: { $type: "app.bsky.embed.video#view", playlist: "https://cdn.example.com/top-video.m3u8" },
323 }, {
324 expectedKind: "external",
325 value: { $type: "app.bsky.embed.external#view", external: { title: "External", uri: "https://example.com" } },
326 }, {
327 expectedKind: "record",
328 value: {
329 $type: "app.bsky.embed.record#view",
330 record: {
331 $type: "app.bsky.embed.record#viewRecord",
332 author: { did: "did:plc:bob", handle: "bob.test" },
333 uri: "at://did:plc:bob/app.bsky.feed.post/quoted-a",
334 value: { text: "quoted a" },
335 },
336 },
337 }, {
338 expectedKind: "recordWithMedia",
339 value: {
340 $type: "app.bsky.embed.recordWithMedia#view",
341 media: {
342 $type: "app.bsky.embed.images#view",
343 images: [{ fullsize: "https://cdn.example.com/top-rwm-image.png" }],
344 },
345 record: {
346 $type: "app.bsky.embed.record#view",
347 record: {
348 $type: "app.bsky.embed.record#viewRecord",
349 author: { did: "did:plc:bob", handle: "bob.test" },
350 uri: "at://did:plc:bob/app.bsky.feed.post/quoted-b",
351 value: { text: "quoted b" },
352 },
353 },
354 },
355 }] as const;
356
357 for (const fixture of fixtures) {
358 const normalized = normalizeEmbed(fixture.value, { source: "top" });
359 expect(normalized.kind).toBe(fixture.expectedKind);
360 expect(normalized.kind).not.toBe("unknown");
361 if (normalized.kind === "record" || normalized.kind === "recordWithMedia") {
362 expect(normalized.quoted.unknownEmbeds).toHaveLength(0);
363 }
364 }
365 });
366
367 it("covers official quoted record union variants without emitting unknown embeds", () => {
368 const fixtures = [{
369 expectedKind: "post",
370 record: {
371 $type: "app.bsky.embed.record#viewRecord",
372 author: { did: "did:plc:bob", handle: "bob.test" },
373 uri: "at://did:plc:bob/app.bsky.feed.post/1",
374 value: { text: "post record" },
375 },
376 }, {
377 expectedKind: "not-found",
378 record: {
379 $type: "app.bsky.embed.record#viewNotFound",
380 notFound: true,
381 uri: "at://did:plc:bob/app.bsky.feed.post/2",
382 },
383 }, {
384 expectedKind: "blocked",
385 record: {
386 $type: "app.bsky.embed.record#viewBlocked",
387 blocked: true,
388 uri: "at://did:plc:bob/app.bsky.feed.post/3",
389 },
390 }, {
391 expectedKind: "detached",
392 record: {
393 $type: "app.bsky.embed.record#viewDetached",
394 detached: true,
395 uri: "at://did:plc:bob/app.bsky.feed.post/4",
396 },
397 }, {
398 expectedKind: "feed",
399 record: {
400 $type: "app.bsky.feed.defs#generatorView",
401 creator: { did: "did:plc:bob", handle: "bob.test" },
402 uri: "at://did:plc:bob/app.bsky.feed.generator/following",
403 },
404 }, {
405 expectedKind: "list",
406 record: {
407 $type: "app.bsky.graph.defs#listView",
408 creator: { did: "did:plc:bob", handle: "bob.test" },
409 uri: "at://did:plc:bob/app.bsky.graph.list/curated",
410 },
411 }, {
412 expectedKind: "labeler",
413 record: {
414 $type: "app.bsky.labeler.defs#labelerView",
415 creator: { did: "did:plc:bob", handle: "bob.test" },
416 uri: "at://did:plc:bob/app.bsky.labeler.service/self",
417 },
418 }, {
419 expectedKind: "starter-pack",
420 record: {
421 $type: "app.bsky.graph.defs#starterPackViewBasic",
422 creator: { did: "did:plc:bob", handle: "bob.test" },
423 record: { name: "Starter Pack" },
424 uri: "at://did:plc:bob/app.bsky.graph.starterpack/abc123",
425 },
426 }] as const;
427
428 for (const fixture of fixtures) {
429 const presentation = getQuotedPresentation({ $type: "app.bsky.embed.record#view", record: fixture.record });
430
431 expect(presentation.kind).toBe(fixture.expectedKind);
432 expect(presentation.unknownEmbeds).toHaveLength(0);
433 }
434 });
435
436 it("infers malformed but recognizable embed shapes without adding unknown embeds", () => {
437 const inferredImages = normalizeEmbed({ images: [{ fullsize: "https://cdn.example.com/inferred-image.png" }] }, {
438 source: "quoted",
439 });
440 const inferredVideo = normalizeEmbed({ playlist: "https://cdn.example.com/inferred-video.m3u8" }, {
441 source: "quoted",
442 });
443 const inferredExternal = normalizeEmbed({
444 external: { title: "Inferred external", uri: "https://example.com/inferred" },
445 }, { source: "quoted" });
446 const inferredRecord = normalizeEmbed({ record: { uri: "at://did:plc:bob/app.bsky.feed.post/inferred" } }, {
447 source: "quoted",
448 });
449 const inferredRecordWithMedia = normalizeEmbed({
450 media: { images: [{ fullsize: "https://cdn.example.com/inferred-rwm.png" }] },
451 record: { uri: "at://did:plc:bob/app.bsky.feed.post/inferred-rwm" },
452 }, { source: "quoted" });
453
454 expect(inferredImages.kind).toBe("images");
455 expect(inferredVideo.kind).toBe("video");
456 expect(inferredExternal.kind).toBe("external");
457 expect(inferredRecord.kind).toBe("record");
458 expect(inferredRecordWithMedia.kind).toBe("recordWithMedia");
459 });
460
461 it("uses quoted embed extraction precedence: embeds > value.embed > value.embeds", () => {
462 const fromEmbeds = getQuotedPresentation({
463 $type: "app.bsky.embed.record#view",
464 record: {
465 $type: "app.bsky.embed.record#viewRecord",
466 embeds: [{ $type: "app.bsky.embed.images#view", images: [{ fullsize: "https://cdn.example.com/a.png" }] }],
467 uri: "at://did:plc:bob/app.bsky.feed.post/a",
468 value: {
469 embed: { $type: "app.bsky.embed.video#view", playlist: "https://cdn.example.com/a.m3u8" },
470 embeds: [{ $type: "app.bsky.embed.external#view", external: { title: "A", uri: "https://example.com/a" } }],
471 text: "a",
472 },
473 },
474 });
475 const fromValueEmbed = getQuotedPresentation({
476 $type: "app.bsky.embed.record#view",
477 record: {
478 $type: "app.bsky.embed.record#viewRecord",
479 uri: "at://did:plc:bob/app.bsky.feed.post/b",
480 value: {
481 embed: { $type: "app.bsky.embed.video#view", playlist: "https://cdn.example.com/b.m3u8" },
482 embeds: [{ $type: "app.bsky.embed.external#view", external: { title: "B", uri: "https://example.com/b" } }],
483 text: "b",
484 },
485 },
486 });
487 const fromValueEmbeds = getQuotedPresentation({
488 $type: "app.bsky.embed.record#view",
489 record: {
490 $type: "app.bsky.embed.record#viewRecord",
491 uri: "at://did:plc:bob/app.bsky.feed.post/c",
492 value: {
493 embeds: [{ $type: "app.bsky.embed.external#view", external: { title: "C", uri: "https://example.com/c" } }],
494 text: "c",
495 },
496 },
497 });
498
499 expect(fromEmbeds.normalizedEmbeds).toHaveLength(1);
500 expect(fromEmbeds.normalizedEmbeds[0]?.kind).toBe("images");
501 expect(fromEmbeds.normalizedEmbeds[0]?.meta.source).toBe("viewRecord.embeds");
502
503 expect(fromValueEmbed.normalizedEmbeds).toHaveLength(1);
504 expect(fromValueEmbed.normalizedEmbeds[0]?.kind).toBe("video");
505 expect(fromValueEmbed.normalizedEmbeds[0]?.meta.source).toBe("value.embed");
506
507 expect(fromValueEmbeds.normalizedEmbeds).toHaveLength(1);
508 expect(fromValueEmbeds.normalizedEmbeds[0]?.kind).toBe("external");
509 expect(fromValueEmbeds.normalizedEmbeds[0]?.meta.source).toBe("value.embeds");
510 });
511
512 it("prefers postView.record.embed before value embed fallbacks", () => {
513 const fromPostViewEmbed = getQuotedPresentation({
514 $type: "app.bsky.embed.record#view",
515 record: {
516 $type: "app.bsky.feed.defs#postView",
517 author: { did: "did:plc:bob", handle: "bob.test" },
518 record: {
519 embed: {
520 $type: "app.bsky.embed.images#view",
521 images: [{ fullsize: "https://cdn.example.com/postview.png" }],
522 },
523 text: "postview",
524 },
525 uri: "at://did:plc:bob/app.bsky.feed.post/postview-priority",
526 value: {
527 embed: { $type: "app.bsky.embed.video#view", playlist: "https://cdn.example.com/fallback.m3u8" },
528 embeds: [{
529 $type: "app.bsky.embed.external#view",
530 external: { title: "fallback", uri: "https://example.com/fallback" },
531 }],
532 text: "fallback text",
533 },
534 },
535 });
536
537 expect(fromPostViewEmbed.normalizedEmbeds).toHaveLength(1);
538 expect(fromPostViewEmbed.normalizedEmbeds[0]?.kind).toBe("images");
539 expect(fromPostViewEmbed.normalizedEmbeds[0]?.meta.source).toBe("value.embed");
540
541 const fromFallbackValueEmbed = getQuotedPresentation({
542 $type: "app.bsky.embed.record#view",
543 record: {
544 $type: "app.bsky.feed.defs#postView",
545 author: { did: "did:plc:bob", handle: "bob.test" },
546 record: { text: "postview without hydrated embed" },
547 uri: "at://did:plc:bob/app.bsky.feed.post/postview-fallback",
548 value: {
549 embed: { $type: "app.bsky.embed.video#view", playlist: "https://cdn.example.com/value-only.m3u8" },
550 text: "value-only",
551 },
552 },
553 });
554
555 expect(fromFallbackValueEmbed.normalizedEmbeds).toHaveLength(1);
556 expect(fromFallbackValueEmbed.normalizedEmbeds[0]?.kind).toBe("video");
557 expect(fromFallbackValueEmbed.normalizedEmbeds[0]?.meta.source).toBe("value.embed");
558 });
559
560 it("keeps unknown custom embeds visible and aggregates telemetry by fingerprint", () => {
561 const custom = { $type: "dev.example.embed#view", payload: { nested: { key: "value" } } };
562 const topUnknownA = normalizeEmbed(custom, { source: "top" });
563 const topUnknownB = normalizeEmbed(custom, { source: "top" });
564 const quoted = getQuotedPresentation({
565 $type: "app.bsky.embed.record#view",
566 record: {
567 $type: "app.bsky.embed.record#viewRecord",
568 embeds: [
569 { $type: "app.bsky.embed.images#view", images: [{ fullsize: "https://cdn.example.com/known.png" }] },
570 custom,
571 ],
572 uri: "at://did:plc:bob/app.bsky.feed.post/custom",
573 value: { text: "custom quote" },
574 },
575 });
576
577 expect(topUnknownA.kind).toBe("unknown");
578 expect(topUnknownB.kind).toBe("unknown");
579 expect(quoted.normalizedEmbeds.map((embed) => embed.kind)).toEqual(["images", "unknown"]);
580 expect(quoted.unknownEmbeds).toHaveLength(1);
581
582 const telemetry = [...getUnknownEmbedTelemetryForTests().values()];
583 expect(telemetry.length).toBeGreaterThan(0);
584 expect(Math.max(...telemetry)).toBeGreaterThanOrEqual(2);
585 });
586
587 it("guards against deep nesting and embed cycles", () => {
588 const deep = {
589 $type: "app.bsky.embed.record#view",
590 record: {
591 $type: "app.bsky.embed.record#viewRecord",
592 embeds: [{
593 $type: "app.bsky.embed.record#view",
594 record: {
595 $type: "app.bsky.embed.record#viewRecord",
596 embeds: [{
597 $type: "app.bsky.embed.record#view",
598 record: {
599 $type: "app.bsky.embed.record#viewRecord",
600 embeds: [{
601 $type: "app.bsky.embed.record#view",
602 record: {
603 $type: "app.bsky.embed.record#viewRecord",
604 embeds: [{
605 $type: "app.bsky.embed.record#view",
606 record: {
607 $type: "app.bsky.embed.record#viewRecord",
608 uri: "at://did:plc:bob/app.bsky.feed.post/deep-leaf",
609 value: { text: "leaf" },
610 },
611 }],
612 uri: "at://did:plc:bob/app.bsky.feed.post/deep-4",
613 value: { text: "deep-4" },
614 },
615 }],
616 uri: "at://did:plc:bob/app.bsky.feed.post/deep-3",
617 value: { text: "deep-3" },
618 },
619 }],
620 uri: "at://did:plc:bob/app.bsky.feed.post/deep-2",
621 value: { text: "deep-2" },
622 },
623 }],
624 uri: "at://did:plc:bob/app.bsky.feed.post/deep-root",
625 value: { text: "deep-root" },
626 },
627 };
628 const depthLimited = normalizeEmbed(deep, { maxDepth: 3, source: "top" });
629 const seen: NormalizedEmbed[] = [];
630 walkNormalizedEmbeds(depthLimited, (embed) => seen.push(embed));
631
632 expect(seen.some((embed) => embed.meta.depthLimited)).toBe(true);
633
634 const cycleRoot: Record<string, unknown> = {
635 $type: "app.bsky.embed.record#view",
636 record: {
637 $type: "app.bsky.embed.record#viewRecord",
638 uri: "at://did:plc:bob/app.bsky.feed.post/cycle-root",
639 value: { text: "cycle root" },
640 },
641 };
642 const cycleRecord = cycleRoot.record as Record<string, unknown>;
643 cycleRecord.embeds = [cycleRoot];
644
645 const cycleNormalized = normalizeEmbed(cycleRoot, { source: "top" });
646 expect(cycleNormalized.kind).toBe("record");
647 if (cycleNormalized.kind === "record") {
648 expect(cycleNormalized.quoted.normalizedEmbeds).toHaveLength(1);
649 expect(cycleNormalized.quoted.normalizedEmbeds[0]?.kind).toBe("recognized-unrenderable");
650 expect(cycleNormalized.quoted.normalizedEmbeds[0]?.meta.cycle).toBe(true);
651 }
652 });
653
654 it("builds starter-pack and labeler external links", () => {
655 const starterPack = getQuotedPresentation({
656 $type: "app.bsky.embed.record#view",
657 record: {
658 $type: "app.bsky.graph.defs#starterPackViewBasic",
659 creator: { did: "did:plc:alice", handle: "alice.test" },
660 uri: "at://did:plc:alice/app.bsky.graph.starterpack/3lxyx7z7p2f2u",
661 },
662 });
663 const labeler = getQuotedPresentation({
664 $type: "app.bsky.embed.record#view",
665 record: {
666 $type: "app.bsky.labeler.defs#labelerView",
667 creator: { did: "did:plc:labeler123", handle: "labeler.example" },
668 uri: "at://did:plc:labeler123/app.bsky.labeler.service/self",
669 },
670 });
671
672 expect(starterPack.href).toBe("https://bsky.app/starter-pack/did%3Aplc%3Aalice/3lxyx7z7p2f2u");
673 expect(labeler.href).toBe("https://bsky.app/profile/labeler.example");
674 });
675
676 it("falls back to did-based post urls when handle is missing", () => {
677 const postWithoutHandle = {
678 ...createFeedItem().post,
679 author: { did: "did:plc:alice", handle: undefined as unknown as string },
680 };
681
682 expect(buildPublicPostUrl(postWithoutHandle)).toBe("https://bsky.app/profile/did%3Aplc%3Aalice/post/1");
683 });
684
685 it("rejects malformed feed payloads", () => {
686 expect(() => parseFeedResponse({ cursor: null, feed: {} })).toThrow("feed response payload is invalid");
687 expect(() => parseFeedResponse({ cursor: 42, feed: [] })).toThrow("feed response cursor is invalid");
688 });
689
690 it("rejects malformed thread payloads", () => {
691 expect(() => parseThreadResponse({ thread: { nope: true } })).toThrow("thread response payload is invalid");
692 });
693});