feat(04-04): implement item CRUD endpoints + fractional rank (LIST-02)
- Add rank.ts: rankForAppend/rankBetween wrapping fractional-indexing (D-13) - Extend listsRouter: POST /:id/items (fractional rank at active-bottom), GET /:id/items (rank ASC, access-gated) - Add listItemsRouter (mounted /api/list-items): PATCH /:itemId per-field LWW (exactly-one-field zod refine D-08/T-04-07), DELETE /:itemId delete-wins (D-09) - Uncheck recomputes rank to active-bottom in same write (Open Question 2) - All item handlers: access-gate via checkListAccess (T-04-05) - Plan 06 SSE seam comments at each mutation handler - All 48 tests green; typecheck passes
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Fractional-indexing rank helpers (D-13).
|
||||
*
|
||||
* Wraps the `fractional-indexing` library for use in list item ordering.
|
||||
* String-based ranks (e.g. "a0", "a1", "a0V") are stable — a single reorder
|
||||
* writes only the moved item's rank, playing well with SSE live sync (D-13, D-15).
|
||||
*
|
||||
* API:
|
||||
* rankForAppend(lastRank) → rank that sorts after `lastRank` (or "a0" when null)
|
||||
* rankBetween(prev, next) → rank that sorts between `prev` and `next`
|
||||
*
|
||||
* Both are pure functions — no DB access, no side effects.
|
||||
*/
|
||||
|
||||
import { generateKeyBetween } from 'fractional-indexing'
|
||||
|
||||
/**
|
||||
* Generate a rank suitable for appending an item AFTER the last active item.
|
||||
*
|
||||
* @param lastRank - the rank of the current last active item, or null if the
|
||||
* list is empty (first item).
|
||||
* @returns a rank string that sorts after `lastRank` when ordered ASC.
|
||||
* An empty list gets "a0" (generateKeyBetween(null, null)).
|
||||
*/
|
||||
export function rankForAppend(lastRank: string | null): string {
|
||||
return generateKeyBetween(lastRank, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a rank suitable for inserting between two existing ranks.
|
||||
* Also serves as the general case: pass (null, null) for empty list,
|
||||
* (null, next) for prepend, (prev, null) for append.
|
||||
*
|
||||
* @param prev - rank of the item immediately before the insertion point,
|
||||
* or null if inserting at the beginning.
|
||||
* @param next - rank of the item immediately after the insertion point,
|
||||
* or null if inserting at the end.
|
||||
* @returns a rank string that sorts between `prev` and `next` when ordered ASC.
|
||||
*/
|
||||
export function rankBetween(prev: string | null, next: string | null): string {
|
||||
return generateKeyBetween(prev, next)
|
||||
}
|
||||
Reference in New Issue
Block a user