Commit sur main · b6a74a5 · @rmdes
…imeline * feat(endpoint-microsub): add core Microsub server with channels and timeline This PR adds the foundational Microsub endpoint with: Microsub API: - GET/POST ?action=channels - list, create, update, delete, reorder channels - GET/POST ?action=timeline - list items, mark read/unread, remove Storage: - MongoDB collections for channels and items - Cursor-based pagination for timeline - Per-user channel ordering and read state tracking Features: - Follows Microsub spec for channel and timeline actions - Testable with existing Microsub clients (Monocle, Indigenous, etc.) - Multi-user support via userId from session/token This is PR 1 of 6 for the Microsub implementation. Future PRs will add: - PR 2: Feed discovery and subscription - PR 3: Feed fetching and parsing - PR 4: Reader UI - PR 5: Compose and Micropub integration - PR 6: Settings and filtering Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com * ci: add microsub endpoint to development config * feat(endpoint-microsub): add plug-in icon * fix(endpoint-microsub): use same mongodb version as indiekit The plug-in declared mongodb ^6.0.0 while indiekit declares ^7.4.0, so npm installed a nested copy of the driver. ObjectId values created by the plug-in came from bson 6 but were passed to collections served by bson 7, which threw BSONVersionError in markItemsRead, markItemsUnread and removeItems. * style(endpoint-microsub): fix eslint errors Fixes unicorn/prefer-await, unicorn/prefer-number-coercion, unicorn/consistent-boolean-name, unicorn/no-computed-property-existence-check and jsdoc/reject-function-type, and removes unused eslint-disable directives. Satisfying unicorn/prefer-await means init() now awaits index creation rather than leaving it to run in the background, so plug-in initialisation waits for indexes to be created. Errors are still caught and warned about, and the plug-in loader already awaits init(). * test(endpoint-microsub): add unit and integration tests Unit tests cover lib/utils and lib/storage, mirroring the structure of lib/. Controllers are covered by integration tests, as in other endpoint plug-ins. * refactor(endpoint-microsub): use getObjectId from @indiekit/util Replaces the direct mongodb import with @indiekit/util’s getObjectId, as suggested in review. The plug-in no longer declares mongodb at all, so its driver version can’t drift from the host’s — @indiekit/util owns that pin. This supersedes the earlier version bump, which fixed the same mismatch by matching the pin by hand and would have needed maintaining. * chore: update lockfile for endpoint-microsub dependency change Co-Authored-By: Claude Opus 5 noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01WGHR7MuyvBaDbAFfAGUxeT * refactor(endpoint-microsub): use utility methods for uid and logging generateChannelUid built its own string from Math.random(). randomString from @indiekit/util does the same job with randomBytes, which is what a channel identifier should be using. That changes the alphabet from [a-z0-9] to base64url, so the tests asserting lowercase now assert URL-safe characters instead — that was the actual requirement, since a uid appears in Microsub request URLs. Replaces the one console.info in the package with debug, matching endpoint-micropub and endpoint-media, and declares the dependency. Co-Authored-By: Claude Opus 5 noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01WGHR7MuyvBaDbAFfAGUxeT * test(endpoint-microsub): drop the constant parameter eslint rejects * chore(endpoint-microsub): satisfy strictNullChecks and the new lint rules * refactor(endpoint-microsub): use mongodb’s ObjectId now that util no longer exports getObjectId * feat(endpoint-microsub): page the timeline with the shared cursor Timeline items get an id, a UUIDv7 stamped with their publication date, listed and paged through util’s getCursor like posts and media are since #975. The cursor gains two options for it: a filter every page is confined to (the channel) and the key path to order on. Clients see that id as the Microsub _id and can mark or remove items by it; the Mongo _id no longer leaves the storage layer. The endpoint’s own cursor encoding and query builders go. * refactor(endpoint-microsub): reference channels by uid, not by Mongo id Items point at their channel by the channel’s uid, the string clients already use, so nothing in the package needs an ObjectId any more: no casting, no mongodb dependency, and indexes on strings only, in line with paginating and indexing on properties.uid elsewhere. * refactor(endpoint-microsub): address review - Errors use the shared localisable strings (BadRequestError.missingParameter, invalidValue, missingProperty) through the localiser the validators now take; channel not found and the name limit are package-scoped locale keys, and the unused title keys are gone. - uuidv7At moves to @indiekit/util, shared by the uid backfill and the Microsub timeline; the channel uid wrapper is a direct randomString(24). - One collections module replaces the two duplicated collection getters. - The user is publication.me: a session carries only a token and a scope, so the fallback chain never found anything else. * refactor(endpoint-microsub): type the localiser, drop the uid retry, name the channel variables * chore(endpoint-microsub): no init logging, README and docs page, engines like its siblings Other endpoints announce nothing at init and core already logs each collection it adds under debug, so the console lines go; index creation throws like core’s does rather than warn and carry on. --------- Co-authored-by: Claude Opus 4.5 noreply@anthropic.com Co-authored-by: Paul Robert Lloyd me+git@paulrobertlloyd.com