@@ -33,18 +33,38 @@ export interface TableSpec<App> {
3333 jsonFields ?: readonly string [ ] ;
3434}
3535
36- /** Select shape: `{ id: true, name: true }` — same as the GraphQL ORM. */
36+ /**
37+ * Select shape — same as the GraphQL ORM, stated either way round:
38+ *
39+ * - `{ id: true, name: true }` reads those fields and nothing else;
40+ * - `{ databaseId: false }` reads every field except that one, for the field a
41+ * record has in general but this binding's table does not (a scope's copy of
42+ * a table without the scope key column) or that a caller must not carry (a
43+ * secret's id, row bookkeeping).
44+ *
45+ * Naming a field `true` is a projection and wins outright: the excluded ones
46+ * are simply not in it.
47+ */
3748export type SelectShape < App > = { [ K in keyof App ] ?: boolean } ;
3849
3950type TrueKeys < App , S > = {
4051 [ K in keyof S ] -?: S [ K ] extends true ? K & keyof App : never ;
4152} [ keyof S ] ;
4253
43- /** Rows narrow to the selected fields; no `select` returns the full record. */
54+ type FalseKeys < App , S > = {
55+ [ K in keyof S ] -?: S [ K ] extends false ? K & keyof App : never ;
56+ } [ keyof S ] ;
57+
58+ /**
59+ * Rows narrow to what the select states: the picked fields, else the record
60+ * minus the excluded ones. No `select` returns the full record.
61+ */
4462export type SelectResult < App , S extends SelectShape < App > | undefined > =
4563 S extends SelectShape < App >
4664 ? [ TrueKeys < App , S > ] extends [ never ]
47- ? App
65+ ? [ FalseKeys < App , S > ] extends [ never ]
66+ ? App
67+ : Omit < App , FalseKeys < App , S > >
4868 : Pick < App , TrueKeys < App , S > >
4969 : App ;
5070
@@ -280,7 +300,10 @@ export class TableClient<App> {
280300 const all = Object . keys ( this . spec . columnByField ) as ( keyof App & string ) [ ] ;
281301 if ( ! select ) return all ;
282302 const picked = all . filter ( field => select [ field ] === true ) ;
283- return picked . length > 0 ? picked : all ;
303+ if ( picked . length > 0 ) return picked ;
304+ // Nothing picked: the select either excludes fields or says nothing at all,
305+ // and both read what is left.
306+ return all . filter ( field => select [ field ] !== false ) ;
284307 }
285308
286309 private decodeRow < S extends SelectShape < App > | undefined > (
0 commit comments