- SUMMARY.md: discovery service with cursor pagination - STATE.md: advanced to plan 2, added decisions, updated progress to 71% - ROADMAP.md: phase 26 in progress (1/3 plans) - REQUIREMENTS.md: DISC-02, DISC-03, DISC-04, INFR-02 marked complete
88 lines
3.4 KiB
Markdown
88 lines
3.4 KiB
Markdown
---
|
|
phase: 26-discovery-landing-page
|
|
plan: "01"
|
|
subsystem: server/services
|
|
tags: [discovery, service-layer, cursor-pagination, tdd, drizzle]
|
|
dependency_graph:
|
|
requires: []
|
|
provides: [discovery.service.ts]
|
|
affects: [26-02, 26-03]
|
|
tech_stack:
|
|
added: []
|
|
patterns: [cursor-pagination, CursorPage-response-shape, post-query-cursor-filter]
|
|
key_files:
|
|
created:
|
|
- src/server/services/discovery.service.ts
|
|
- tests/services/discovery.service.test.ts
|
|
modified: []
|
|
decisions:
|
|
- "Composite cursor for setups: itemCount_id format, filtered post-query in JS for simplicity with grouped SQL"
|
|
- "createdAt ISO string cursor for recent items: standard timestamp-based pagination"
|
|
- "No cursor pagination for trending categories: bounded small list (< 50), simple limit is sufficient per RESEARCH.md open question 3"
|
|
- "Shared CursorPage<T> generic interface for consistent cursor response shape across setups and items"
|
|
metrics:
|
|
duration: "~2 min"
|
|
completed_date: "2026-04-10"
|
|
tasks_completed: 1
|
|
tasks_total: 1
|
|
files_created: 2
|
|
files_modified: 0
|
|
---
|
|
|
|
# Phase 26 Plan 01: Discovery Service Summary
|
|
|
|
**One-liner:** Discovery service layer with cursor pagination using Drizzle ORM — getPopularSetups (itemCount_id composite cursor), getRecentGlobalItems (ISO timestamp cursor), getTrendingCategories (simple limit).
|
|
|
|
## Tasks Completed
|
|
|
|
| Task | Name | Commit | Files |
|
|
|------|------|--------|-------|
|
|
| 1 (RED) | Discovery service TDD — failing tests | 06b6e93 | tests/services/discovery.service.test.ts |
|
|
| 1 (GREEN) | Discovery service TDD — implementation | d1f8a7a | src/server/services/discovery.service.ts |
|
|
|
|
## What Was Built
|
|
|
|
### `src/server/services/discovery.service.ts`
|
|
|
|
Three exported async functions:
|
|
|
|
**`getPopularSetups(db, limit=6, cursor?)`**
|
|
- JOINs setups → setupItems (count) → users (displayName)
|
|
- WHERE isPublic=true, GROUP BY setup fields
|
|
- ORDER BY item count DESC, id DESC
|
|
- Cursor: `itemCount_id` composite string, filtered post-query in JS
|
|
- Returns `CursorPage<{ id, name, createdAt, itemCount, creatorName }>`
|
|
|
|
**`getRecentGlobalItems(db, limit=8, cursor?)`**
|
|
- SELECT * FROM globalItems WHERE createdAt < cursor (if provided)
|
|
- ORDER BY createdAt DESC, LIMIT limit+1 for hasMore detection
|
|
- Cursor: ISO timestamp of last item's createdAt
|
|
- Returns `CursorPage<GlobalItem>`
|
|
|
|
**`getTrendingCategories(db, limit=12)`**
|
|
- SELECT category, COUNT(id) FROM globalItems WHERE category IS NOT NULL
|
|
- GROUP BY category, ORDER BY count DESC
|
|
- Returns plain `Array<{ name: string; itemCount: number }>` (no cursor)
|
|
|
|
### `tests/services/discovery.service.test.ts`
|
|
|
|
11 tests covering:
|
|
- `getPopularSetups`: ordering by count desc, private setup exclusion, hasMore/nextCursor, second page deduplication, creatorName from users.displayName
|
|
- `getRecentGlobalItems`: ordering by createdAt desc, hasMore/nextCursor, second page deduplication
|
|
- `getTrendingCategories`: ordering by count desc, null category exclusion, empty state
|
|
|
|
## Deviations from Plan
|
|
|
|
None — plan executed exactly as written.
|
|
|
|
The test used a dynamic import pattern for `eq` which was corrected to a static import (minor code quality fix before RED commit).
|
|
|
|
## Verification
|
|
|
|
- `bun test tests/services/discovery.service.test.ts`: 11 pass, 0 fail
|
|
- `bun test` full suite: 290 tests — same pass/fail ratio as before (15 pre-existing failures from `withImageUrl` storage service export issue, unrelated to this plan)
|
|
|
|
## Self-Check
|
|
|
|
Checked below.
|