Skip to content

Remove 2-part cursor fallback in decodeCursor() - #164

Merged
cuibonobo merged 1 commit into
mainfrom
claude/issue-152-om4cjm
Aug 14, 2026
Merged

Remove 2-part cursor fallback in decodeCursor()#164
cuibonobo merged 1 commit into
mainfrom
claude/issue-152-om4cjm

Conversation

@cuibonobo

Copy link
Copy Markdown
Member

Summary

  • decodeCursor() in packages/sqlite-shared/src/cursor.ts accepted a legacy 2-part (value|id) cursor shape and coerced it to createdAt. encodeCursor() has only ever emitted 3-part cursors, so this branch was unreachable by construction and, in practice, only served to weaken validation — a corrupt or truncated cursor that happened to decode to two |-separated parts was silently accepted with a fabricated sort field instead of being rejected.
  • The decoder now requires exactly three parts and throws StackQueryError otherwise. The now-dead field === undefined || value === undefined || id === undefined guard was removed along with it.
  • Updated packages/sqlite-shared/tests/cursor.test.ts: replaced accepts the legacy 2-part (value|id) format as createdAt with a test asserting a 2-part cursor throws StackQueryError.

Closes #152

Spec

No behavior change relative to the documented contract — docs/spec/data-model.md § Sorting and pagination already states that a cursor which can't be decoded is a structurally malformed request that throws StackQueryError → 400. This change brings the implementation in line with that existing spec text rather than changing it.

Verification

pnpm run format:check && pnpm run lint && pnpm test && pnpm run build && pnpm run typecheck

All green across the monorepo (sqlite-shared, record-adapter-sqlite, adapter-api, adapter-local, blob-adapter-disk, wire-types, core).

Notes for reviewers

Both SQLite-backed adapters share this codec via @haverstack/sqlite-shared, so the fix lands in one place and applies to both. Cursors are opaque and single-session by contract, so tightening the decoder has no data-migration implications.


Generated by Claude Code

decodeCursor() silently accepted a legacy 2-part (value|id) cursor
shape and coerced it to createdAt, which meant a corrupt or truncated
cursor could be accepted with a fabricated sort field instead of
being rejected. encodeCursor() has only ever emitted 3-part cursors,
so the fallback was unreachable by construction and contradicted the
documented contract that malformed cursors throw StackQueryError.

Fixes #152
@cuibonobo
cuibonobo merged commit 4dc47e8 into main Aug 14, 2026
5 checks passed
@cuibonobo
cuibonobo deleted the claude/issue-152-om4cjm branch August 14, 2026 13:26
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.

Remove the 2-part cursor fallback in decodeCursor()

2 participants