Skip to content

Latest commit

 

History

History
38 lines (32 loc) · 1.75 KB

File metadata and controls

38 lines (32 loc) · 1.75 KB
@cipherstash/stack-supabase major

single() and maybeSingle() now type data as the ROW, not an array.

Both have always returned one object at runtime, but the builder kept advertising the array shape it was created with, so data was typed T[] | null while holding a single row. Every caller had to launder it:

const { data } = await supabase.from('users').select('id, email').single()
// before: data is `User[] | null` — wrong; a cast was the only way through
const user = data as unknown as User
// after: data is `User | null`
data?.email

single()/maybeSingle() now return EncryptedSingleQueryBuilder<T>, which awaits to EncryptedSupabaseResponse<T> (data: T | null). That covers the zero-row case for maybeSingle() and the error case for both, so no separate null modelling was needed.

Filters and transforms are no longer chainable after single()/maybeSingle(), matching supabase-js — applying one afterwards would change the query the single-row promise was made about. .single().eq(...), .single().limit(...) and friends were previously accepted and are now compile errors. What only re-types or re-configures the pending request is carried over: returns<U>() (preserving the awaited shape, so .single().returns<U>() awaits one row), abortSignal(), throwOnError(), withLockContext() and audit(). EncryptedSingleQueryBuilder<T> is exported so a stored builder can be annotated.

Migration: delete the cast. Code that worked around the old typing with data as unknown as Row (or read data![0]) should now use data directly; the cast still compiles but is no longer needed, and data![0] becomes a type error. Move any filter or transform chained after single()/maybeSingle() to before it.