Skip to content

feat(web): add a feed of recent publications to the home page - #6

Merged
greymag merged 4 commits into
masterfrom
feature/46.recent-activity
Sep 9, 2026
Merged

greymag merged 4 commits into
masterfrom
feature/46.recent-activity

Conversation

@sonny-ns5

Copy link
Copy Markdown

Что изменилось

Главная страница ранжировала пакеты только по загрузкам, поэтому по ней нельзя было понять, что publish'илось в последнее время. Теперь она открывается лентой из девяти последних публикаций, над прежним блоком Top packages.

Лента — это события, а не пакеты: пакет, опубликованный дважды, даёт две записи, каждая называет ту версию, что реально вышла, и ведёт на страницу именно этой версии. Сортировка пакетов по updatedAt не могла сказать ни того, ни другого — список пакетов несёт highest stable версию, так что фикс, выпущенный в старую линию, показывался бы рядом со свежей датой под номером версии, который не менялся.

Как устроено

  • Новый GET /webapi/recent — отдельный эндпоинт, потому что отвечает другой сущностью ({publications: [{name, version, description, publishedAt}]}).
  • MetaStore.queryRecentPublications({size}); в Mongo — агрегация $unwind по versions$sort по versions.createdAt$limit. Сортировать документы пакетов нельзя в принципе: версии лежат массивом внутри документа, и порядок документов может назвать только одну версию на пакет.
  • Ничего нового при публикации не пишется: лента считается по уже хранимым версиям и дате, которую каждая и так несёт.
  • size ограничен сотней: в отличие от страницы списка пакетов, объём работы здесь растёт со всем репозиторием.
  • Любой из двух списков на главной пропускается, если сервер по нему не ответил, вместо того чтобы утащить с собой второй.

Проверено

  • unpub: dart test — 556 тестов зелёные (нужен локальный MongoDB из .dev/docker-compose.yml), dart analyze чисто.
  • unpub_web: dart analyze — только предсуществующие info про dart:html.
  • Вручную на локальном сервере с историей, где один пакет публиковался трижды (2.0.0, затем фикс 1.9.1 в старую линию): три отдельные записи, сверху 1.9.1.

Новые тесты — unpub/test/recent_publications_test.dart: одна запись на версию с сортировкой по дате, запись называет опубликованную версию, проброс и ограничение size.

The home page ranked packages by downloads only, so nothing on it said what
had gone up lately. It now opens with the nine most recent publications,
served by a new `/webapi/recent`.

A feed of publications rather than of packages: a package released twice is
two entries, and each names the version that actually went up and links to
that version's page. Ordering packages by `updatedAt` could say neither —
the package list carries a package's highest stable version, so a fix
released on an older line would sit next to a fresh timestamp under a
version number that had not moved.

Nothing new is written on publish: the feed is aggregated over the stored
versions, ordered by the date each already carries. Capped at a hundred
entries, since unlike a page of the package list its work grows with the
whole repository.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It introduces a new public endpoint and a new MongoDB aggregation spanning server, shared API models, and web UI, and the aggregation's ordering logic has no integration test coverage, so final human review is warranted.

Pull request overview

This PR adds a "Recently published" feed to the home page of the in_pub private Dart pub server. The home page previously showed only packages ranked by popularity; it now leads with a feed of the most recent publications (one entry per published version, newest first), rendered above the existing "Top Dart packages" block. The feed is served by a new GET /webapi/recent endpoint backed by a MongoDB $unwind/$sort/$limit aggregation over stored versions, so nothing new is written on publish. The two home lists are fetched concurrently and each degrades independently if the server fails to answer for it.

Changes:

  • New /webapi/recent endpoint plus MetaStore.queryRecentPublications({size}) (Mongo aggregation), capped at 100 and defaulting to 10.
  • New shared API models (RecentApi, RecentApiPublication) and internal UnpubRecentPublication, with generated JSON code.
  • Web UI: home page renders both lists (recent + top) via guarded concurrent fetches; each list is omitted if it can't be loaded.
File summaries
File Description
unpub/lib/src/app.dart Adds getRecentPublications handler with size clamping and maps store results to RecentApi.
unpub/lib/src/app.g.dart Registers the GET /webapi/recent route.
unpub/lib/src/meta_store.dart Declares abstract queryRecentPublications({size}).
unpub/lib/src/mongo_store.dart Implements the feed via $unwind/$sort/$limit aggregation.
unpub/lib/src/models.dart Adds internal UnpubRecentPublication model.
unpub/lib/unpub_api/lib/models.dart Adds shared RecentApi/RecentApiPublication API models.
unpub/lib/unpub_api/lib/models.g.dart Generated JSON (de)serialization for the new models.
unpub_web/lib/app_service.dart Adds fetchRecentPublications.
unpub_web/lib/src/home_component.dart Fetches both lists concurrently, guards failures, adds publication URL/date helpers.
unpub_web/lib/src/home_component.html Renders the recent feed above the top-packages list, each guarded by null checks.
unpub/test/recent_publications_test.dart New endpoint tests (ordering, version naming, size passthrough/cap) via a fake store.
unpub/test/*.dart (7 fakes) Add the new override to existing MetaStore test fakes.
unpub/CHANGELOG.md Documents the feature under 3.6.0.
Review details

Files not reviewed (2)

  • unpub/lib/src/app.g.dart: Generated file
  • unpub/lib/unpub_api/lib/models.g.dart: Generated file
  • Files reviewed: 17/20 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +127 to +136
final rows = await db.collection(packageCollection).aggregateToStream([
{r'$unwind': r'$versions'},
{
r'$sort': {'versions.createdAt': -1}
},
{r'$limit': size},
{
r'$project': {'name': 1, 'versions': 1}
},
]).toList();

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point — covered in 67dde1f, as a recent publications group in unpub/test/unpub_test.dart, against the real database alongside the other MongoStore cases.

One package holds 2.0.0 published on the 6th, 1.9.1 on the 8th (a fix on the older line, so the newest publication is not the newest version) and 1.9.0 back in August, with a second package in between; the feed is expected as 1.9.1, 1.0.9, 2.0.0, 1.9.0. Two more cases assert that an entry carries the version that was published together with its date, and that size cuts the feed at the newest end.

The rows are written straight into the collection rather than published through the API: the ordering only means anything when the publication dates are days apart and out of version order, which publishing here and now cannot produce.

Checked that the cases actually bite by flipping the sort direction to 1 — all three fail.

sonny-ns5 and others added 3 commits September 9, 2026 09:27
The endpoint's tests run against a fake store, so nothing exercised the
`$unwind`/`$sort`/`$limit` pipeline itself — the piece that fails silently,
a flipped sort direction or a wrong field path returning a plausible list in
the wrong order.

Written straight into the collection rather than published through the API:
the ordering only means something when the publication dates are days apart
and out of version order, with the newest publication sitting on an older
release line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The only conflict was `unpub/lib/src/static/main.dart.js.dart`, the
committed web bundle: both sides had rebuilt it, so the text cannot be
merged. Resolved by rebuilding it from the merged sources, which is what
that file is — the browser tab titles from master and this branch's
publication feed are both in it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It spans a new public API endpoint, an unindexed full-collection MongoDB aggregation with operational/scaling implications, and web UI changes whose runtime behavior cannot be fully verified here, warranting human sign-off.

Review details

Files not reviewed (2)

  • unpub/lib/src/app.g.dart: Generated file
  • unpub/lib/unpub_api/lib/models.g.dart: Generated file
  • Files reviewed: 18/21 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@greymag
greymag merged commit 0ba858c into master Sep 9, 2026
4 checks passed
@greymag
greymag deleted the feature/46.recent-activity branch September 9, 2026 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants