@tanstack/db 0.1.12 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/errors.cjs +18 -6
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +9 -3
- package/dist/cjs/index.cjs +3 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +4 -1
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +38 -21
- package/dist/cjs/query/builder/index.cjs +25 -16
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +8 -8
- package/dist/cjs/query/builder/ref-proxy.cjs +12 -8
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +2 -1
- package/dist/cjs/query/builder/types.d.cts +493 -28
- package/dist/cjs/query/compiler/evaluators.cjs +29 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +4 -2
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.cjs +13 -4
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.cjs +70 -60
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/select.cjs +131 -42
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/compiler/select.d.cts +1 -5
- package/dist/cjs/query/ir.cjs +4 -0
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +6 -1
- package/dist/cjs/query/optimizer.cjs +61 -20
- package/dist/cjs/query/optimizer.cjs.map +1 -1
- package/dist/esm/errors.d.ts +9 -3
- package/dist/esm/errors.js +18 -6
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.js +4 -2
- package/dist/esm/query/builder/functions.d.ts +38 -21
- package/dist/esm/query/builder/functions.js +4 -1
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +8 -8
- package/dist/esm/query/builder/index.js +27 -18
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +2 -1
- package/dist/esm/query/builder/ref-proxy.js +12 -8
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +493 -28
- package/dist/esm/query/compiler/evaluators.js +29 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.js +4 -2
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.js +15 -6
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.js +71 -61
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/select.d.ts +1 -5
- package/dist/esm/query/compiler/select.js +131 -42
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/ir.d.ts +6 -1
- package/dist/esm/query/ir.js +4 -0
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/optimizer.js +62 -21
- package/dist/esm/query/optimizer.js.map +1 -1
- package/package.json +2 -2
- package/src/errors.ts +17 -10
- package/src/query/builder/functions.ts +176 -108
- package/src/query/builder/index.ts +68 -48
- package/src/query/builder/ref-proxy.ts +14 -20
- package/src/query/builder/types.ts +622 -110
- package/src/query/compiler/evaluators.ts +30 -0
- package/src/query/compiler/group-by.ts +6 -1
- package/src/query/compiler/index.ts +23 -6
- package/src/query/compiler/joins.ts +132 -101
- package/src/query/compiler/select.ts +206 -113
- package/src/query/ir.ts +14 -1
- package/src/query/optimizer.ts +131 -59
|
@@ -1,8 +1,37 @@
|
|
|
1
1
|
import type { CollectionImpl } from "../../collection.js"
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
Aggregate,
|
|
4
|
+
BasicExpression,
|
|
5
|
+
Func,
|
|
6
|
+
OrderByDirection,
|
|
7
|
+
PropRef,
|
|
8
|
+
Value,
|
|
9
|
+
} from "../ir.js"
|
|
3
10
|
import type { QueryBuilder } from "./index.js"
|
|
4
11
|
import type { ResolveType } from "../../types.js"
|
|
5
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Context - The central state container for query builder operations
|
|
15
|
+
*
|
|
16
|
+
* This interface tracks all the information needed to build and type-check queries:
|
|
17
|
+
*
|
|
18
|
+
* **Schema Management**:
|
|
19
|
+
* - `baseSchema`: The original tables/collections from the `from()` clause
|
|
20
|
+
* - `schema`: Current available tables (expands with joins, contracts with subqueries)
|
|
21
|
+
*
|
|
22
|
+
* **Query State**:
|
|
23
|
+
* - `fromSourceName`: Which table was used in `from()` - needed for optionality logic
|
|
24
|
+
* - `hasJoins`: Whether any joins have been added (affects result type inference)
|
|
25
|
+
* - `joinTypes`: Maps table aliases to their join types for optionality calculations
|
|
26
|
+
*
|
|
27
|
+
* **Result Tracking**:
|
|
28
|
+
* - `result`: The final shape after `select()` - undefined until select is called
|
|
29
|
+
*
|
|
30
|
+
* The context evolves through the query builder chain:
|
|
31
|
+
* 1. `from()` sets baseSchema and schema to the same thing
|
|
32
|
+
* 2. `join()` expands schema and sets hasJoins/joinTypes
|
|
33
|
+
* 3. `select()` sets result to the projected shape
|
|
34
|
+
*/
|
|
6
35
|
export interface Context {
|
|
7
36
|
// The collections available in the base schema
|
|
8
37
|
baseSchema: ContextSchema
|
|
@@ -21,20 +50,57 @@ export interface Context {
|
|
|
21
50
|
result?: any
|
|
22
51
|
}
|
|
23
52
|
|
|
53
|
+
/**
|
|
54
|
+
* ContextSchema - The shape of available tables/collections in a query context
|
|
55
|
+
*
|
|
56
|
+
* This is simply a record mapping table aliases to their TypeScript types.
|
|
57
|
+
* It evolves as the query progresses:
|
|
58
|
+
* - Initial: Just the `from()` table
|
|
59
|
+
* - After joins: Includes all joined tables with proper optionality
|
|
60
|
+
* - In subqueries: May be a subset of the outer query's schema
|
|
61
|
+
*/
|
|
24
62
|
export type ContextSchema = Record<string, unknown>
|
|
25
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Source - Input definition for query builder `from()` clause
|
|
66
|
+
*
|
|
67
|
+
* Maps table aliases to either:
|
|
68
|
+
* - `CollectionImpl`: A database collection/table
|
|
69
|
+
* - `QueryBuilder`: A subquery that can be used as a table
|
|
70
|
+
*
|
|
71
|
+
* Example: `{ users: usersCollection, orders: ordersCollection }`
|
|
72
|
+
*/
|
|
26
73
|
export type Source = {
|
|
27
74
|
[alias: string]: CollectionImpl<any, any> | QueryBuilder<Context>
|
|
28
75
|
}
|
|
29
76
|
|
|
30
|
-
|
|
31
|
-
|
|
77
|
+
/**
|
|
78
|
+
* InferCollectionType - Extracts the TypeScript type from a CollectionImpl
|
|
79
|
+
*
|
|
80
|
+
* This helper ensures we get the same type that would be used when creating
|
|
81
|
+
* the collection itself. It uses the internal `ResolveType` logic to maintain
|
|
82
|
+
* consistency between collection creation and query type inference.
|
|
83
|
+
*
|
|
84
|
+
* The complex generic parameters extract:
|
|
85
|
+
* - U: The base document type
|
|
86
|
+
* - TSchema: The schema definition
|
|
87
|
+
* - The resolved type combines these with any transforms
|
|
88
|
+
*/
|
|
32
89
|
export type InferCollectionType<T> =
|
|
33
90
|
T extends CollectionImpl<infer U, any, any, infer TSchema, any>
|
|
34
91
|
? ResolveType<U, TSchema, U>
|
|
35
92
|
: never
|
|
36
93
|
|
|
37
|
-
|
|
94
|
+
/**
|
|
95
|
+
* SchemaFromSource - Converts a Source definition into a ContextSchema
|
|
96
|
+
*
|
|
97
|
+
* This transforms the input to `from()` into the schema format used throughout
|
|
98
|
+
* the query builder. For each alias in the source:
|
|
99
|
+
* - Collections → their inferred TypeScript type
|
|
100
|
+
* - Subqueries → their result type (what they would return if executed)
|
|
101
|
+
*
|
|
102
|
+
* The `Prettify` wrapper ensures clean type display in IDEs.
|
|
103
|
+
*/
|
|
38
104
|
export type SchemaFromSource<T extends Source> = Prettify<{
|
|
39
105
|
[K in keyof T]: T[K] extends CollectionImpl<any, any, any, any, any>
|
|
40
106
|
? InferCollectionType<T[K]>
|
|
@@ -43,49 +109,217 @@ export type SchemaFromSource<T extends Source> = Prettify<{
|
|
|
43
109
|
: never
|
|
44
110
|
}>
|
|
45
111
|
|
|
46
|
-
|
|
112
|
+
/**
|
|
113
|
+
* GetAliases - Extracts all table aliases available in a query context
|
|
114
|
+
*
|
|
115
|
+
* Simple utility type that returns the keys of the schema, representing
|
|
116
|
+
* all table/collection aliases that can be referenced in the current query.
|
|
117
|
+
*/
|
|
47
118
|
export type GetAliases<TContext extends Context> = keyof TContext[`schema`]
|
|
48
119
|
|
|
49
|
-
|
|
120
|
+
/**
|
|
121
|
+
* WhereCallback - Type for where/having clause callback functions
|
|
122
|
+
*
|
|
123
|
+
* These callbacks receive a `refs` object containing RefProxy instances for
|
|
124
|
+
* all available tables. The callback should return a boolean expression
|
|
125
|
+
* that will be used to filter query results.
|
|
126
|
+
*
|
|
127
|
+
* Example: `(refs) => eq(refs.users.age, 25)`
|
|
128
|
+
*/
|
|
50
129
|
export type WhereCallback<TContext extends Context> = (
|
|
51
|
-
refs:
|
|
130
|
+
refs: RefsForContext<TContext>
|
|
52
131
|
) => any
|
|
53
132
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
133
|
+
/**
|
|
134
|
+
* SelectValue - Union of all valid values in a select clause
|
|
135
|
+
*
|
|
136
|
+
* This type defines what can be used as values in the object passed to `select()`.
|
|
137
|
+
*
|
|
138
|
+
* **Core Expression Types**:
|
|
139
|
+
* - `BasicExpression`: Function calls like `upper(users.name)`
|
|
140
|
+
* - `Aggregate`: Aggregations like `count()`, `avg()`
|
|
141
|
+
* - `RefProxy/Ref`: Direct field references like `users.name`
|
|
142
|
+
*
|
|
143
|
+
* **JavaScript Literals** (for constant values in projections):
|
|
144
|
+
* - `string`: String literals like `'active'`, `'N/A'`
|
|
145
|
+
* - `number`: Numeric literals like `0`, `42`, `3.14`
|
|
146
|
+
* - `boolean`: Boolean literals `true`, `false`
|
|
147
|
+
* - `null`: Explicit null values
|
|
148
|
+
*
|
|
149
|
+
* **Advanced Features**:
|
|
150
|
+
* - `undefined`: Allows optional projection values
|
|
151
|
+
* - `{ [key: string]: SelectValue }`: Nested object projection
|
|
152
|
+
*
|
|
153
|
+
* The clean Ref type ensures no internal properties are visible to users.
|
|
154
|
+
*
|
|
155
|
+
* Examples:
|
|
156
|
+
* ```typescript
|
|
157
|
+
* select({
|
|
158
|
+
* id: users.id,
|
|
159
|
+
* name: users.name,
|
|
160
|
+
* status: 'active', // string literal
|
|
161
|
+
* priority: 1, // number literal
|
|
162
|
+
* verified: true, // boolean literal
|
|
163
|
+
* notes: null, // explicit null
|
|
164
|
+
* profile: {
|
|
165
|
+
* name: users.name,
|
|
166
|
+
* email: users.email
|
|
167
|
+
* }
|
|
168
|
+
* })
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
type SelectValue =
|
|
172
|
+
| BasicExpression
|
|
173
|
+
| Aggregate
|
|
174
|
+
| Ref
|
|
175
|
+
| RefLeaf<any>
|
|
176
|
+
| string // String literals
|
|
177
|
+
| number // Numeric literals
|
|
178
|
+
| boolean // Boolean literals
|
|
179
|
+
| null // Explicit null
|
|
180
|
+
| undefined // Optional values
|
|
181
|
+
| { [key: string]: SelectValue }
|
|
182
|
+
| Array<RefLeaf<any>>
|
|
183
|
+
|
|
184
|
+
// Recursive shape for select objects allowing nested projections
|
|
185
|
+
type SelectShape = { [key: string]: SelectValue | SelectShape }
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* SelectObject - Wrapper type for select clause objects
|
|
189
|
+
*
|
|
190
|
+
* This ensures that objects passed to `select()` have valid SelectValue types
|
|
191
|
+
* for all their properties. It's a simple wrapper that provides better error
|
|
192
|
+
* messages when invalid selections are attempted.
|
|
193
|
+
*/
|
|
194
|
+
export type SelectObject<T extends SelectShape = SelectShape> = T
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* ResultTypeFromSelect - Infers the result type from a select object
|
|
198
|
+
*
|
|
199
|
+
* This complex type transforms the input to `select()` into the actual TypeScript
|
|
200
|
+
* type that the query will return. It handles all the different kinds of values
|
|
201
|
+
* that can appear in a select clause:
|
|
202
|
+
*
|
|
203
|
+
* **Ref/RefProxy Extraction**:
|
|
204
|
+
* - `RefProxy<T>` → `T`: Extracts the underlying type
|
|
205
|
+
* - `Ref<T> | undefined` → `T | undefined`: Preserves optionality
|
|
206
|
+
* - `Ref<T> | null` → `T | null`: Preserves nullability
|
|
207
|
+
*
|
|
208
|
+
* **Expression Types**:
|
|
209
|
+
* - `BasicExpression<T>` → `T`: Function results like `upper()` → `string`
|
|
210
|
+
* - `Aggregate<T>` → `T`: Aggregation results like `count()` → `number`
|
|
211
|
+
*
|
|
212
|
+
* **JavaScript Literals** (pass through as-is):
|
|
213
|
+
* - `string` → `string`: String literals remain strings
|
|
214
|
+
* - `number` → `number`: Numeric literals remain numbers
|
|
215
|
+
* - `boolean` → `boolean`: Boolean literals remain booleans
|
|
216
|
+
* - `null` → `null`: Explicit null remains null
|
|
217
|
+
* - `undefined` → `undefined`: Direct undefined values
|
|
218
|
+
*
|
|
219
|
+
* **Nested Objects** (recursive):
|
|
220
|
+
* - Plain objects are recursively processed to handle nested projections
|
|
221
|
+
* - RefProxy objects are detected and their types extracted
|
|
222
|
+
*
|
|
223
|
+
* Example transformation:
|
|
224
|
+
* ```typescript
|
|
225
|
+
* // Input:
|
|
226
|
+
* { id: Ref<number>, name: Ref<string>, status: 'active', count: 42, profile: { bio: Ref<string> } }
|
|
227
|
+
*
|
|
228
|
+
* // Output:
|
|
229
|
+
* { id: number, name: string, status: 'active', count: 42, profile: { bio: string } }
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
export type ResultTypeFromSelect<TSelectObject> = WithoutRefBrand<
|
|
233
|
+
Prettify<{
|
|
234
|
+
[K in keyof TSelectObject]: NeedsExtraction<TSelectObject[K]> extends true
|
|
235
|
+
? ExtractExpressionType<TSelectObject[K]>
|
|
236
|
+
: TSelectObject[K] extends Ref<infer _T>
|
|
237
|
+
? ExtractRef<TSelectObject[K]>
|
|
238
|
+
: TSelectObject[K] extends RefLeaf<infer T>
|
|
71
239
|
? T
|
|
72
|
-
: TSelectObject[K] extends undefined
|
|
73
|
-
? undefined
|
|
74
|
-
: TSelectObject[K] extends
|
|
75
|
-
?
|
|
76
|
-
:
|
|
77
|
-
|
|
240
|
+
: TSelectObject[K] extends RefLeaf<infer T> | undefined
|
|
241
|
+
? T | undefined
|
|
242
|
+
: TSelectObject[K] extends RefLeaf<infer T> | null
|
|
243
|
+
? T | null
|
|
244
|
+
: TSelectObject[K] extends Ref<infer _T> | undefined
|
|
245
|
+
? ExtractRef<TSelectObject[K]> | undefined
|
|
246
|
+
: TSelectObject[K] extends Ref<infer _T> | null
|
|
247
|
+
? ExtractRef<TSelectObject[K]> | null
|
|
248
|
+
: TSelectObject[K] extends Aggregate<infer T>
|
|
249
|
+
? T
|
|
250
|
+
: TSelectObject[K] extends
|
|
251
|
+
| string
|
|
252
|
+
| number
|
|
253
|
+
| boolean
|
|
254
|
+
| null
|
|
255
|
+
| undefined
|
|
256
|
+
? TSelectObject[K]
|
|
257
|
+
: TSelectObject[K] extends Record<string, any>
|
|
258
|
+
? ResultTypeFromSelect<TSelectObject[K]>
|
|
259
|
+
: never
|
|
260
|
+
}>
|
|
261
|
+
>
|
|
262
|
+
|
|
263
|
+
// Extract Ref or subobject with a spread or a Ref
|
|
264
|
+
type ExtractRef<T> = Prettify<ResultTypeFromSelect<WithoutRefBrand<T>>>
|
|
265
|
+
|
|
266
|
+
// Helper type to extract the underlying type from various expression types
|
|
267
|
+
type ExtractExpressionType<T> =
|
|
268
|
+
T extends PropRef<infer U>
|
|
269
|
+
? U
|
|
270
|
+
: T extends Value<infer U>
|
|
271
|
+
? U
|
|
272
|
+
: T extends Func<infer U>
|
|
273
|
+
? U
|
|
274
|
+
: T extends Aggregate<infer U>
|
|
275
|
+
? U
|
|
276
|
+
: T extends BasicExpression<infer U>
|
|
277
|
+
? U
|
|
278
|
+
: T
|
|
279
|
+
|
|
280
|
+
// Helper type to check if a type needs expression type extraction
|
|
281
|
+
type NeedsExtraction<T> = T extends
|
|
282
|
+
| PropRef<any>
|
|
283
|
+
| Value<any>
|
|
284
|
+
| Func<any>
|
|
285
|
+
| Aggregate<any>
|
|
286
|
+
| BasicExpression<any>
|
|
287
|
+
? true
|
|
288
|
+
: false
|
|
78
289
|
|
|
79
|
-
|
|
290
|
+
/**
|
|
291
|
+
* OrderByCallback - Type for orderBy clause callback functions
|
|
292
|
+
*
|
|
293
|
+
* Similar to WhereCallback, these receive refs for all available tables
|
|
294
|
+
* and should return expressions that will be used for sorting.
|
|
295
|
+
*
|
|
296
|
+
* Example: `(refs) => refs.users.createdAt`
|
|
297
|
+
*/
|
|
80
298
|
export type OrderByCallback<TContext extends Context> = (
|
|
81
|
-
refs:
|
|
299
|
+
refs: RefsForContext<TContext>
|
|
82
300
|
) => any
|
|
83
301
|
|
|
302
|
+
/**
|
|
303
|
+
* OrderByOptions - Configuration for orderBy operations
|
|
304
|
+
*
|
|
305
|
+
* Combines direction and null handling with string-specific sorting options.
|
|
306
|
+
* The intersection with StringSortOpts allows for either simple lexical sorting
|
|
307
|
+
* or locale-aware sorting with customizable options.
|
|
308
|
+
*/
|
|
84
309
|
export type OrderByOptions = {
|
|
85
310
|
direction?: OrderByDirection
|
|
86
311
|
nulls?: `first` | `last`
|
|
87
312
|
} & StringSortOpts
|
|
88
313
|
|
|
314
|
+
/**
|
|
315
|
+
* StringSortOpts - Options for string sorting behavior
|
|
316
|
+
*
|
|
317
|
+
* This discriminated union allows for two types of string sorting:
|
|
318
|
+
* - **Lexical**: Simple character-by-character comparison (default)
|
|
319
|
+
* - **Locale**: Locale-aware sorting with optional customization
|
|
320
|
+
*
|
|
321
|
+
* The union ensures that locale options are only available when locale sorting is selected.
|
|
322
|
+
*/
|
|
89
323
|
export type StringSortOpts =
|
|
90
324
|
| {
|
|
91
325
|
stringSort?: `lexical`
|
|
@@ -96,6 +330,13 @@ export type StringSortOpts =
|
|
|
96
330
|
localeOptions?: object
|
|
97
331
|
}
|
|
98
332
|
|
|
333
|
+
/**
|
|
334
|
+
* CompareOptions - Final resolved options for comparison operations
|
|
335
|
+
*
|
|
336
|
+
* This is the internal type used after all orderBy options have been resolved
|
|
337
|
+
* to their concrete values. Unlike OrderByOptions, all fields are required
|
|
338
|
+
* since defaults have been applied.
|
|
339
|
+
*/
|
|
99
340
|
export type CompareOptions = {
|
|
100
341
|
direction: OrderByDirection
|
|
101
342
|
nulls: `first` | `last`
|
|
@@ -104,91 +345,219 @@ export type CompareOptions = {
|
|
|
104
345
|
localeOptions?: object
|
|
105
346
|
}
|
|
106
347
|
|
|
107
|
-
|
|
348
|
+
/**
|
|
349
|
+
* GroupByCallback - Type for groupBy clause callback functions
|
|
350
|
+
*
|
|
351
|
+
* These callbacks receive refs for all available tables and should return
|
|
352
|
+
* expressions that will be used for grouping query results.
|
|
353
|
+
*
|
|
354
|
+
* Example: `(refs) => refs.orders.status`
|
|
355
|
+
*/
|
|
108
356
|
export type GroupByCallback<TContext extends Context> = (
|
|
109
|
-
refs:
|
|
357
|
+
refs: RefsForContext<TContext>
|
|
110
358
|
) => any
|
|
111
359
|
|
|
112
|
-
|
|
360
|
+
/**
|
|
361
|
+
* JoinOnCallback - Type for join condition callback functions
|
|
362
|
+
*
|
|
363
|
+
* These callbacks receive refs for all available tables (including the newly
|
|
364
|
+
* joined table) and should return a boolean expression defining the join condition.
|
|
365
|
+
*
|
|
366
|
+
* Important: The newly joined table is NOT marked as optional in this callback,
|
|
367
|
+
* even for left/right/full joins, because optionality is applied AFTER the join
|
|
368
|
+
* condition is evaluated.
|
|
369
|
+
*
|
|
370
|
+
* Example: `(refs) => eq(refs.users.id, refs.orders.userId)`
|
|
371
|
+
*/
|
|
113
372
|
export type JoinOnCallback<TContext extends Context> = (
|
|
114
|
-
refs:
|
|
373
|
+
refs: RefsForContext<TContext>
|
|
115
374
|
) => any
|
|
116
375
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
376
|
+
/**
|
|
377
|
+
* RefProxyForContext - Creates ref proxies for all tables/collections in a query context
|
|
378
|
+
*
|
|
379
|
+
* This is the main entry point for creating ref objects in query builder callbacks.
|
|
380
|
+
* It handles optionality by placing undefined/null OUTSIDE the RefProxy to enable
|
|
381
|
+
* JavaScript's optional chaining operator (?.):
|
|
382
|
+
*
|
|
383
|
+
* Examples:
|
|
384
|
+
* - Required field: `RefProxy<User>` → user.name works
|
|
385
|
+
* - Optional field: `RefProxy<User> | undefined` → user?.name works
|
|
386
|
+
* - Nullable field: `RefProxy<User> | null` → user?.name works
|
|
387
|
+
* - Both optional and nullable: `RefProxy<User> | undefined` → user?.name works
|
|
388
|
+
*
|
|
389
|
+
* The key insight is that `RefProxy<User | undefined>` would NOT allow `user?.name`
|
|
390
|
+
* because the undefined is "inside" the proxy, but `RefProxy<User> | undefined`
|
|
391
|
+
* does allow it because the undefined is "outside" the proxy.
|
|
392
|
+
*
|
|
393
|
+
* The logic prioritizes optional chaining by always placing `undefined` outside when
|
|
394
|
+
* a type is both optional and nullable (e.g., `string | null | undefined`).
|
|
395
|
+
*/
|
|
396
|
+
export type RefsForContext<TContext extends Context> = {
|
|
397
|
+
[K in keyof TContext[`schema`]]: IsNonExactOptional<
|
|
398
|
+
TContext[`schema`][K]
|
|
399
|
+
> extends true
|
|
400
|
+
? IsNonExactNullable<TContext[`schema`][K]> extends true
|
|
401
|
+
? // T is both non-exact optional and non-exact nullable (e.g., string | null | undefined)
|
|
402
|
+
// Extract the non-undefined and non-null part and place undefined outside
|
|
403
|
+
Ref<NonNullable<TContext[`schema`][K]>> | undefined
|
|
404
|
+
: // T is optional (T | undefined) but not exactly undefined, and not nullable
|
|
405
|
+
// Extract the non-undefined part and place undefined outside
|
|
406
|
+
Ref<NonUndefined<TContext[`schema`][K]>> | undefined
|
|
407
|
+
: IsNonExactNullable<TContext[`schema`][K]> extends true
|
|
408
|
+
? // T is nullable (T | null) but not exactly null, and not optional
|
|
409
|
+
// Extract the non-null part and place null outside
|
|
410
|
+
Ref<NonNull<TContext[`schema`][K]>> | null
|
|
411
|
+
: // T is exactly undefined, exactly null, or neither optional nor nullable
|
|
412
|
+
// Wrap in RefProxy as-is (includes exact undefined, exact null, and normal types)
|
|
413
|
+
Ref<TContext[`schema`][K]>
|
|
120
414
|
}
|
|
121
415
|
|
|
416
|
+
/**
|
|
417
|
+
* Type Detection Helpers
|
|
418
|
+
*
|
|
419
|
+
* These helpers distinguish between different kinds of optionality/nullability:
|
|
420
|
+
* - IsExactlyUndefined: T is literally `undefined` (not `string | undefined`)
|
|
421
|
+
* - IsOptional: T includes undefined (like `string | undefined`)
|
|
422
|
+
* - IsExactlyNull: T is literally `null` (not `string | null`)
|
|
423
|
+
* - IsNullable: T includes null (like `string | null`)
|
|
424
|
+
* - IsNonExactOptional: T includes undefined but is not exactly undefined
|
|
425
|
+
* - IsNonExactNullable: T includes null but is not exactly null
|
|
426
|
+
*
|
|
427
|
+
* The [T] extends [undefined] pattern prevents distributive conditional types,
|
|
428
|
+
* ensuring we check the exact type rather than distributing over union members.
|
|
429
|
+
*/
|
|
430
|
+
|
|
122
431
|
// Helper type to check if T is exactly undefined
|
|
123
432
|
type IsExactlyUndefined<T> = [T] extends [undefined] ? true : false
|
|
124
433
|
|
|
434
|
+
// Helper type to check if T is exactly null
|
|
435
|
+
type IsExactlyNull<T> = [T] extends [null] ? true : false
|
|
436
|
+
|
|
125
437
|
// Helper type to check if T includes undefined (is optional)
|
|
126
438
|
type IsOptional<T> = undefined extends T ? true : false
|
|
127
439
|
|
|
440
|
+
// Helper type to check if T includes null (is nullable)
|
|
441
|
+
type IsNullable<T> = null extends T ? true : false
|
|
442
|
+
|
|
443
|
+
// Helper type to check if T is optional but not exactly undefined
|
|
444
|
+
type IsNonExactOptional<T> =
|
|
445
|
+
IsOptional<T> extends true
|
|
446
|
+
? IsExactlyUndefined<T> extends false
|
|
447
|
+
? true
|
|
448
|
+
: false
|
|
449
|
+
: false
|
|
450
|
+
|
|
451
|
+
// Helper type to check if T is nullable but not exactly null
|
|
452
|
+
type IsNonExactNullable<T> =
|
|
453
|
+
IsNullable<T> extends true
|
|
454
|
+
? IsExactlyNull<T> extends false
|
|
455
|
+
? true
|
|
456
|
+
: false
|
|
457
|
+
: false
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Type Extraction Helpers
|
|
461
|
+
*
|
|
462
|
+
* These helpers extract the "useful" part of a type by removing null/undefined:
|
|
463
|
+
* - NonUndefined: `string | undefined` → `string` (preserves null if present)
|
|
464
|
+
* - NonNull: `string | null` → `string` (preserves undefined if present)
|
|
465
|
+
*
|
|
466
|
+
* These are used when we need to handle optional and nullable types separately.
|
|
467
|
+
* For cases where both null and undefined should be removed, use TypeScript's
|
|
468
|
+
* built-in NonNullable<T> instead.
|
|
469
|
+
*/
|
|
470
|
+
|
|
128
471
|
// Helper type to extract non-undefined type
|
|
129
472
|
type NonUndefined<T> = T extends undefined ? never : T
|
|
130
473
|
|
|
131
|
-
// Helper type to
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
T
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
>
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
474
|
+
// Helper type to extract non-null type
|
|
475
|
+
type NonNull<T> = T extends null ? never : T
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Ref - The user-facing ref interface for the query builder
|
|
479
|
+
*
|
|
480
|
+
* This is a clean type that represents a reference to a value in the query,
|
|
481
|
+
* designed for optimal IDE experience without internal implementation details.
|
|
482
|
+
* It provides a recursive interface that allows nested property access while
|
|
483
|
+
* preserving optionality and nullability correctly.
|
|
484
|
+
*
|
|
485
|
+
* When spread in select clauses, it correctly produces the underlying data type
|
|
486
|
+
* without Ref wrappers, enabling clean spread operations.
|
|
487
|
+
*
|
|
488
|
+
* Example usage:
|
|
489
|
+
* ```typescript
|
|
490
|
+
* // Clean interface - no internal properties visible
|
|
491
|
+
* const users: Ref<{ id: number; profile?: { bio: string } }> = { ... }
|
|
492
|
+
* users.id // Ref<number> - clean display
|
|
493
|
+
* users.profile?.bio // Ref<string> - nested optional access works
|
|
494
|
+
*
|
|
495
|
+
* // Spread operations work cleanly:
|
|
496
|
+
* select(({ user }) => ({ ...user })) // Returns User type, not Ref types
|
|
497
|
+
* ```
|
|
498
|
+
*/
|
|
499
|
+
export type Ref<T = any> = {
|
|
500
|
+
[K in keyof T]: IsNonExactOptional<T[K]> extends true
|
|
501
|
+
? IsNonExactNullable<T[K]> extends true
|
|
502
|
+
? // Both optional and nullable
|
|
503
|
+
NonNullable<T[K]> extends Record<string, any>
|
|
504
|
+
? Ref<NonNullable<T[K]>> | undefined
|
|
505
|
+
: RefLeaf<NonNullable<T[K]>> | undefined
|
|
506
|
+
: // Optional only
|
|
507
|
+
NonUndefined<T[K]> extends Record<string, any>
|
|
508
|
+
? Ref<NonUndefined<T[K]>> | undefined
|
|
509
|
+
: RefLeaf<NonUndefined<T[K]>> | undefined
|
|
510
|
+
: IsNonExactNullable<T[K]> extends true
|
|
511
|
+
? // Nullable only
|
|
512
|
+
NonNull<T[K]> extends Record<string, any>
|
|
513
|
+
? Ref<NonNull<T[K]>> | null
|
|
514
|
+
: RefLeaf<NonNull<T[K]>> | null
|
|
515
|
+
: // Required
|
|
516
|
+
T[K] extends Record<string, any>
|
|
517
|
+
? Ref<T[K]>
|
|
518
|
+
: RefLeaf<T[K]>
|
|
519
|
+
} & RefLeaf<T>
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Ref - The user-facing ref type with clean IDE display
|
|
523
|
+
*
|
|
524
|
+
* An opaque branded type that represents a reference to a value in a query.
|
|
525
|
+
* This shows as `Ref<T>` in the IDE without exposing internal structure.
|
|
526
|
+
*
|
|
527
|
+
* Example usage:
|
|
528
|
+
* - Ref<number> displays as `Ref<number>` in IDE
|
|
529
|
+
* - Ref<string> displays as `Ref<string>` in IDE
|
|
530
|
+
* - No internal properties like __refProxy, __path, __type are visible
|
|
531
|
+
*/
|
|
532
|
+
declare const RefBrand: unique symbol
|
|
533
|
+
export type RefLeaf<T = any> = { readonly [RefBrand]?: T }
|
|
534
|
+
|
|
535
|
+
// Helper type to remove RefBrand from objects
|
|
536
|
+
type WithoutRefBrand<T> =
|
|
537
|
+
T extends Record<string, any> ? Omit<T, typeof RefBrand> : T
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* MergeContextWithJoinType - Creates a new context after a join operation
|
|
541
|
+
*
|
|
542
|
+
* This is the core type that handles the complex logic of merging schemas
|
|
543
|
+
* when tables are joined, applying the correct optionality based on join type.
|
|
544
|
+
*
|
|
545
|
+
* **Key Responsibilities**:
|
|
546
|
+
* 1. **Schema Merging**: Combines existing schema with newly joined tables
|
|
547
|
+
* 2. **Optionality Logic**: Applies join-specific optionality rules:
|
|
548
|
+
* - `LEFT JOIN`: New table becomes optional
|
|
549
|
+
* - `RIGHT JOIN`: Existing tables become optional
|
|
550
|
+
* - `FULL JOIN`: Both existing and new become optional
|
|
551
|
+
* - `INNER JOIN`: No tables become optional
|
|
552
|
+
* 3. **State Tracking**: Updates hasJoins and joinTypes for future operations
|
|
553
|
+
*
|
|
554
|
+
* **Context Evolution**:
|
|
555
|
+
* - `baseSchema`: Unchanged (always the original `from()` tables)
|
|
556
|
+
* - `schema`: Expanded with new tables and proper optionality
|
|
557
|
+
* - `hasJoins`: Set to true
|
|
558
|
+
* - `joinTypes`: Updated to track this join type
|
|
559
|
+
* - `result`: Preserved from previous operations
|
|
560
|
+
*/
|
|
192
561
|
export type MergeContextWithJoinType<
|
|
193
562
|
TContext extends Context,
|
|
194
563
|
TNewSchema extends ContextSchema,
|
|
@@ -213,7 +582,31 @@ export type MergeContextWithJoinType<
|
|
|
213
582
|
result: TContext[`result`]
|
|
214
583
|
}
|
|
215
584
|
|
|
216
|
-
|
|
585
|
+
/**
|
|
586
|
+
* ApplyJoinOptionalityToMergedSchema - Applies optionality rules when merging schemas
|
|
587
|
+
*
|
|
588
|
+
* This type implements the SQL join optionality semantics:
|
|
589
|
+
*
|
|
590
|
+
* **For Existing Tables**:
|
|
591
|
+
* - `RIGHT JOIN` or `FULL JOIN`: Main table (from fromSourceName) becomes optional
|
|
592
|
+
* - Other join types: Existing tables keep their current optionality
|
|
593
|
+
* - Previously joined tables: Keep their already-applied optionality
|
|
594
|
+
*
|
|
595
|
+
* **For New Tables**:
|
|
596
|
+
* - `LEFT JOIN` or `FULL JOIN`: New table becomes optional
|
|
597
|
+
* - `INNER JOIN` or `RIGHT JOIN`: New table remains required
|
|
598
|
+
*
|
|
599
|
+
* **Examples**:
|
|
600
|
+
* ```sql
|
|
601
|
+
* FROM users LEFT JOIN orders -- orders becomes optional
|
|
602
|
+
* FROM users RIGHT JOIN orders -- users becomes optional
|
|
603
|
+
* FROM users FULL JOIN orders -- both become optional
|
|
604
|
+
* FROM users INNER JOIN orders -- both remain required
|
|
605
|
+
* ```
|
|
606
|
+
*
|
|
607
|
+
* The intersection (&) ensures both existing and new schemas are merged
|
|
608
|
+
* into a single type while preserving all table references.
|
|
609
|
+
*/
|
|
217
610
|
export type ApplyJoinOptionalityToMergedSchema<
|
|
218
611
|
TExistingSchema extends ContextSchema,
|
|
219
612
|
TNewSchema extends ContextSchema,
|
|
@@ -237,7 +630,31 @@ export type ApplyJoinOptionalityToMergedSchema<
|
|
|
237
630
|
TNewSchema[K]
|
|
238
631
|
}
|
|
239
632
|
|
|
240
|
-
|
|
633
|
+
/**
|
|
634
|
+
* GetResult - Determines the final result type of a query
|
|
635
|
+
*
|
|
636
|
+
* This type implements the logic for what a query returns based on its current state:
|
|
637
|
+
*
|
|
638
|
+
* **Priority Order**:
|
|
639
|
+
* 1. **Explicit Result**: If `select()` was called, use the projected type
|
|
640
|
+
* 2. **Join Query**: If joins exist, return all tables with proper optionality
|
|
641
|
+
* 3. **Single Table**: Return just the main table from `from()`
|
|
642
|
+
*
|
|
643
|
+
* **Examples**:
|
|
644
|
+
* ```typescript
|
|
645
|
+
* // Single table query:
|
|
646
|
+
* from({ users }).where(...) // → User[]
|
|
647
|
+
*
|
|
648
|
+
* // Join query without select:
|
|
649
|
+
* from({ users }).leftJoin({ orders }, ...) // → { users: User, orders: Order | undefined }[]
|
|
650
|
+
*
|
|
651
|
+
* // Query with select:
|
|
652
|
+
* from({ users }).select({ id: users.id, name: users.name }) // → { id: number, name: string }[]
|
|
653
|
+
* ```
|
|
654
|
+
*
|
|
655
|
+
* The `Prettify` wrapper ensures clean type display in IDEs by flattening
|
|
656
|
+
* complex intersection types into readable object types.
|
|
657
|
+
*/
|
|
241
658
|
export type GetResult<TContext extends Context> = Prettify<
|
|
242
659
|
TContext[`result`] extends object
|
|
243
660
|
? TContext[`result`]
|
|
@@ -248,7 +665,22 @@ export type GetResult<TContext extends Context> = Prettify<
|
|
|
248
665
|
TContext[`schema`][TContext[`fromSourceName`]]
|
|
249
666
|
>
|
|
250
667
|
|
|
251
|
-
|
|
668
|
+
/**
|
|
669
|
+
* ApplyJoinOptionalityToSchema - Legacy helper for complex join scenarios
|
|
670
|
+
*
|
|
671
|
+
* This type was designed to handle complex scenarios with multiple joins
|
|
672
|
+
* where the optionality of tables might be affected by subsequent joins.
|
|
673
|
+
* Currently used in advanced join logic, but most cases are handled by
|
|
674
|
+
* the simpler `ApplyJoinOptionalityToMergedSchema`.
|
|
675
|
+
*
|
|
676
|
+
* **Logic**:
|
|
677
|
+
* 1. **Main Table**: Becomes optional if ANY right or full join exists in the chain
|
|
678
|
+
* 2. **Joined Tables**: Check their specific join type for optionality
|
|
679
|
+
* 3. **Complex Cases**: Handle scenarios where subsequent joins affect earlier tables
|
|
680
|
+
*
|
|
681
|
+
* This is primarily used for edge cases and may be simplified in future versions
|
|
682
|
+
* as the simpler merge-based approach covers most real-world scenarios.
|
|
683
|
+
*/
|
|
252
684
|
export type ApplyJoinOptionalityToSchema<
|
|
253
685
|
TSchema extends ContextSchema,
|
|
254
686
|
TJoinTypes extends Record<string, string>,
|
|
@@ -275,7 +707,19 @@ export type ApplyJoinOptionalityToSchema<
|
|
|
275
707
|
: TSchema[K]
|
|
276
708
|
}
|
|
277
709
|
|
|
278
|
-
|
|
710
|
+
/**
|
|
711
|
+
* IsTableMadeOptionalBySubsequentJoins - Checks if later joins affect table optionality
|
|
712
|
+
*
|
|
713
|
+
* This helper determines if a table that was initially required becomes optional
|
|
714
|
+
* due to joins that happen later in the query chain.
|
|
715
|
+
*
|
|
716
|
+
* **Current Implementation**:
|
|
717
|
+
* - Main table: Becomes optional if any right/full joins exist
|
|
718
|
+
* - Joined tables: Not affected by subsequent joins (simplified model)
|
|
719
|
+
*
|
|
720
|
+
* This is a conservative approach that may be extended in the future to handle
|
|
721
|
+
* more complex join interaction scenarios.
|
|
722
|
+
*/
|
|
279
723
|
type IsTableMadeOptionalBySubsequentJoins<
|
|
280
724
|
TTableAlias extends string | number | symbol,
|
|
281
725
|
TJoinTypes extends Record<string, string>,
|
|
@@ -286,7 +730,24 @@ type IsTableMadeOptionalBySubsequentJoins<
|
|
|
286
730
|
: // Joined tables are not affected by subsequent joins in our current implementation
|
|
287
731
|
false
|
|
288
732
|
|
|
289
|
-
|
|
733
|
+
/**
|
|
734
|
+
* HasJoinType - Utility to check if any join in a chain matches target types
|
|
735
|
+
*
|
|
736
|
+
* This type searches through all recorded join types to see if any match
|
|
737
|
+
* the specified target types. It's used to implement logic like "becomes optional
|
|
738
|
+
* if ANY right or full join exists in the chain".
|
|
739
|
+
*
|
|
740
|
+
* **How it works**:
|
|
741
|
+
* 1. Maps over all join types, checking each against target types
|
|
742
|
+
* 2. Creates a union of boolean results
|
|
743
|
+
* 3. Uses `true extends Union` pattern to check if any were true
|
|
744
|
+
*
|
|
745
|
+
* **Example**:
|
|
746
|
+
* ```typescript
|
|
747
|
+
* HasJoinType<{ orders: 'left', products: 'right' }, 'right' | 'full'>
|
|
748
|
+
* // → true (because products is a right join)
|
|
749
|
+
* ```
|
|
750
|
+
*/
|
|
290
751
|
export type HasJoinType<
|
|
291
752
|
TJoinTypes extends Record<string, string>,
|
|
292
753
|
TTargetTypes extends string,
|
|
@@ -296,20 +757,71 @@ export type HasJoinType<
|
|
|
296
757
|
? true
|
|
297
758
|
: false
|
|
298
759
|
|
|
299
|
-
|
|
300
|
-
|
|
760
|
+
/**
|
|
761
|
+
* MergeContextForJoinCallback - Special context for join condition callbacks
|
|
762
|
+
*
|
|
763
|
+
* This type creates a context specifically for the `onCallback` parameter of join operations.
|
|
764
|
+
* The key difference from `MergeContextWithJoinType` is that NO optionality is applied here.
|
|
765
|
+
*
|
|
766
|
+
* **Why No Optionality?**
|
|
767
|
+
* In SQL, join conditions are evaluated BEFORE optionality is determined. Both tables
|
|
768
|
+
* must be treated as available (non-optional) within the join condition itself.
|
|
769
|
+
* Optionality is only applied to the result AFTER the join logic executes.
|
|
770
|
+
*
|
|
771
|
+
* **Example**:
|
|
772
|
+
* ```typescript
|
|
773
|
+
* .from({ users })
|
|
774
|
+
* .leftJoin({ orders }, ({ users, orders }) => {
|
|
775
|
+
* // users is NOT optional here - we can access users.id directly
|
|
776
|
+
* // orders is NOT optional here - we can access orders.userId directly
|
|
777
|
+
* return eq(users.id, orders.userId)
|
|
778
|
+
* })
|
|
779
|
+
* .where(({ orders }) => {
|
|
780
|
+
* // NOW orders is optional because it's after the LEFT JOIN
|
|
781
|
+
* return orders?.status === 'pending'
|
|
782
|
+
* })
|
|
783
|
+
* ```
|
|
784
|
+
*
|
|
785
|
+
* The simple intersection (&) merges schemas without any optionality transformation.
|
|
786
|
+
*/
|
|
787
|
+
export type MergeContextForJoinCallback<
|
|
301
788
|
TContext extends Context,
|
|
302
789
|
TNewSchema extends ContextSchema,
|
|
303
|
-
> =
|
|
790
|
+
> = {
|
|
791
|
+
baseSchema: TContext[`baseSchema`]
|
|
792
|
+
// Merge schemas without applying join optionality - both are non-optional in join condition
|
|
793
|
+
schema: TContext[`schema`] & TNewSchema
|
|
794
|
+
fromSourceName: TContext[`fromSourceName`]
|
|
795
|
+
hasJoins: true
|
|
796
|
+
joinTypes: TContext[`joinTypes`] extends Record<string, any>
|
|
797
|
+
? TContext[`joinTypes`]
|
|
798
|
+
: {}
|
|
799
|
+
result: TContext[`result`]
|
|
800
|
+
}
|
|
304
801
|
|
|
305
|
-
|
|
802
|
+
/**
|
|
803
|
+
* WithResult - Updates a context with a new result type after select()
|
|
804
|
+
*
|
|
805
|
+
* This utility type is used internally when the `select()` method is called
|
|
806
|
+
* to update the context with the projected result type. It preserves all
|
|
807
|
+
* other context properties while replacing the `result` field.
|
|
808
|
+
*
|
|
809
|
+
* **Usage**:
|
|
810
|
+
* When `select()` is called, the query builder uses this type to create
|
|
811
|
+
* a new context where `result` contains the shape of the selected fields.
|
|
812
|
+
*
|
|
813
|
+
* The double `Prettify` ensures both the overall context and the nested
|
|
814
|
+
* result type display cleanly in IDEs.
|
|
815
|
+
*/
|
|
306
816
|
export type WithResult<TContext extends Context, TResult> = Prettify<
|
|
307
817
|
Omit<TContext, `result`> & {
|
|
308
818
|
result: Prettify<TResult>
|
|
309
819
|
}
|
|
310
820
|
>
|
|
311
821
|
|
|
312
|
-
|
|
822
|
+
/**
|
|
823
|
+
* Prettify - Utility type for clean IDE display
|
|
824
|
+
*/
|
|
313
825
|
export type Prettify<T> = {
|
|
314
826
|
[K in keyof T]: T[K]
|
|
315
827
|
} & {}
|