Cover AT Protocol boundary validation with tests
New atproto-schemas.test.ts directly exercises the schema unit:
publicHttpsUrlSchema rejects every SSRF target we care about (http,
javascript:, file:, localhost, 127.x, RFC1918, 169.254 AWS IMDS, IPv6
::1, oversized strings), plcDocumentSchema reflects that on
serviceEndpoint, and atProtoRecordSchema requires the fields the
adapter actually reads (uri, cid, value.title, value.publishedAt).
bluesky.test.ts gains AppView boundary coverage: malformed responses
(missing field, wrong type, non-DID string) return null instead of
propagating garbage, fetch throws return null, and the DID-passthrough
in resolveHandle still skips the network call.
atproto-feed.test.ts gains the end-to-end SSRF assertion — a PLC
document with `http://`, `localhost`, or `169.254.169.254` as
serviceEndpoint lands the feed on its static fallback — and the
per-record skip assertion: a malformed record is dropped with a warn,
the well-formed records in the same response are kept.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cover AT Protocol boundary validation with tests
New atproto-schemas.test.ts directly exercises the schema unit:
publicHttpsUrlSchema rejects every SSRF target we care about (http,
javascript:, file:, localhost, 127.x, RFC1918, 169.254 AWS IMDS, IPv6
::1, oversized strings), plcDocumentSchema reflects that on
serviceEndpoint, and atProtoRecordSchema requires the fields the
adapter actually reads (uri, cid, value.title, value.publishedAt).
bluesky.test.ts gains AppView boundary coverage: malformed responses
(missing field, wrong type, non-DID string) return null instead of
propagating garbage, fetch throws return null, and the DID-passthrough
in resolveHandle still skips the network call.
atproto-feed.test.ts gains the end-to-end SSRF assertion — a PLC
document with `http://`, `localhost`, or `169.254.169.254` as
serviceEndpoint lands the feed on its static fallback — and the
per-record skip assertion: a malformed record is dropped with a warn,
the well-formed records in the same response are kept.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
port pure lib primitives + vitest coverage
- category-utils: getCategorySlug + makeCategoryTitleResolver (resolver
is config-bound, was a module-level map keyed off the consumer's
categories source)
- post-filtering-utils: calculateMatchScore, filterPostsByCategories
(OR-logic), sortPostsWithMatchScore (match-score tiebreaker only
when >1 selected), getAllCategoriesFromPosts
- client-post-filters: paginatePosts + filterSortAndPaginatePosts
- bluesky: URL parsing, handle resolution, post fetch (unchanged
surface from website)
- markdown-cache: loadMarkdownContent({contentDirPath}) — was a
baked-in path in the website
- 33 tests across the pure-function set (slug/title resolver edge,
match scoring, OR-filter, sort tiebreaker, pagination boundaries,
URL regex acceptance/rejection); skipping component renders and
fetch-glue per testing-philosophy.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
port pure lib primitives + vitest coverage
- category-utils: getCategorySlug + makeCategoryTitleResolver (resolver
is config-bound, was a module-level map keyed off the consumer's
categories source)
- post-filtering-utils: calculateMatchScore, filterPostsByCategories
(OR-logic), sortPostsWithMatchScore (match-score tiebreaker only
when >1 selected), getAllCategoriesFromPosts
- client-post-filters: paginatePosts + filterSortAndPaginatePosts
- bluesky: URL parsing, handle resolution, post fetch (unchanged
surface from website)
- markdown-cache: loadMarkdownContent({contentDirPath}) — was a
baked-in path in the website
- 33 tests across the pure-function set (slug/title resolver edge,
match scoring, OR-filter, sort tiebreaker, pagination boundaries,
URL regex acceptance/rejection); skipping component renders and
fetch-glue per testing-philosophy.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add Next-flavored BlogPostPage that emits blog.post.view telemetry
Wraps BlogPostLayout with a fire-and-forget after() call from next/server
that logs the post view via config.telemetry.logServerEvent. The
framework-agnostic version in src/routes/blog-post-page.tsx stays
slot-aware (all the new passthrough props: meta, cover, title, body,
sidebar, comments, beforeArticle, afterArticle, className, classNames)
but does not emit telemetry — consumers on @blog/rsc are expected to
emit themselves or live without blog.post.view.
src/next.tsx now imports BlogPostPage from ./next-blog-post-page while
BlogListPage continues to come from ./rsc; the other re-exports
(nextImageAdapter, NextBlogProvider, createBlogOpenGraphImage,
generateBlogPostMetadata) are unchanged.
Tests mock next/server's after, then assert the deferred callback fires
config.telemetry.logServerEvent with the expected payload when the post
resolves, that after is not scheduled when fetchDocument returns
undefined, and that a missing config.telemetry does not crash the
deferred callback.
port pure lib primitives + vitest coverage
- category-utils: getCategorySlug + makeCategoryTitleResolver (resolver
is config-bound, was a module-level map keyed off the consumer's
categories source)
- post-filtering-utils: calculateMatchScore, filterPostsByCategories
(OR-logic), sortPostsWithMatchScore (match-score tiebreaker only
when >1 selected), getAllCategoriesFromPosts
- client-post-filters: paginatePosts + filterSortAndPaginatePosts
- bluesky: URL parsing, handle resolution, post fetch (unchanged
surface from website)
- markdown-cache: loadMarkdownContent({contentDirPath}) — was a
baked-in path in the website
- 33 tests across the pure-function set (slug/title resolver edge,
match scoring, OR-filter, sort tiebreaker, pagination boundaries,
URL regex acceptance/rejection); skipping component renders and
fetch-glue per testing-philosophy.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>