Skip to content

Commit 9f12edb

Browse files
committed
checkpoint
1 parent efa4b13 commit 9f12edb

File tree

4 files changed

+102
-59
lines changed

4 files changed

+102
-59
lines changed

packages/db/src/query/builder/functions.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -89,64 +89,64 @@ type BinaryNumericReturnType<T1, T2> =
8989
// Operators
9090

9191
export function eq<T>(
92-
left: RefProxy<T> | undefined,
93-
right: T | RefProxy<T> | BasicExpression<T> | undefined
92+
left: RefProxy<T> | Ref<T> | undefined | null,
93+
right: T | RefProxy<T> | Ref<T> | BasicExpression<T> | undefined | null
9494
): BasicExpression<boolean>
9595
export function eq<T extends string | number | boolean>(
96-
left: T | BasicExpression<T> | undefined,
97-
right: T | BasicExpression<T> | undefined
96+
left: T | BasicExpression<T> | undefined | null,
97+
right: T | BasicExpression<T> | undefined | null
9898
): BasicExpression<boolean>
9999
export function eq<T>(left: Aggregate<T>, right: any): BasicExpression<boolean>
100100
export function eq(left: any, right: any): BasicExpression<boolean> {
101101
return new Func(`eq`, [toExpression(left), toExpression(right)])
102102
}
103103

104104
export function gt<T>(
105-
left: RefProxy<T> | undefined,
106-
right: T | RefProxy<T> | BasicExpression<T> | undefined
105+
left: RefProxy<T> | Ref<T> | undefined | null,
106+
right: T | RefProxy<T> | Ref<T> | BasicExpression<T> | undefined | null
107107
): BasicExpression<boolean>
108108
export function gt<T extends string | number>(
109-
left: T | BasicExpression<T> | undefined,
110-
right: T | BasicExpression<T> | undefined
109+
left: T | BasicExpression<T> | undefined | null,
110+
right: T | BasicExpression<T> | undefined | null
111111
): BasicExpression<boolean>
112112
export function gt<T>(left: Aggregate<T>, right: any): BasicExpression<boolean>
113113
export function gt(left: any, right: any): BasicExpression<boolean> {
114114
return new Func(`gt`, [toExpression(left), toExpression(right)])
115115
}
116116

117117
export function gte<T>(
118-
left: RefProxy<T> | undefined,
119-
right: T | RefProxy<T> | BasicExpression<T> | undefined
118+
left: RefProxy<T> | Ref<T> | undefined | null,
119+
right: T | RefProxy<T> | Ref<T> | BasicExpression<T> | undefined | null
120120
): BasicExpression<boolean>
121121
export function gte<T extends string | number>(
122-
left: T | BasicExpression<T> | undefined,
123-
right: T | BasicExpression<T> | undefined
122+
left: T | BasicExpression<T> | undefined | null,
123+
right: T | BasicExpression<T> | undefined | null
124124
): BasicExpression<boolean>
125125
export function gte<T>(left: Aggregate<T>, right: any): BasicExpression<boolean>
126126
export function gte(left: any, right: any): BasicExpression<boolean> {
127127
return new Func(`gte`, [toExpression(left), toExpression(right)])
128128
}
129129

130130
export function lt<T>(
131-
left: RefProxy<T> | undefined,
132-
right: T | RefProxy<T> | BasicExpression<T> | undefined
131+
left: RefProxy<T> | Ref<T> | undefined | null,
132+
right: T | RefProxy<T> | Ref<T> | BasicExpression<T> | undefined | null
133133
): BasicExpression<boolean>
134134
export function lt<T extends string | number>(
135-
left: T | BasicExpression<T> | undefined,
136-
right: T | BasicExpression<T> | undefined
135+
left: T | BasicExpression<T> | undefined | null,
136+
right: T | BasicExpression<T> | undefined | null
137137
): BasicExpression<boolean>
138138
export function lt<T>(left: Aggregate<T>, right: any): BasicExpression<boolean>
139139
export function lt(left: any, right: any): BasicExpression<boolean> {
140140
return new Func(`lt`, [toExpression(left), toExpression(right)])
141141
}
142142

143143
export function lte<T>(
144-
left: RefProxy<T> | undefined,
145-
right: T | RefProxy<T> | BasicExpression<T> | undefined
144+
left: RefProxy<T> | Ref<T> | undefined | null,
145+
right: T | RefProxy<T> | Ref<T> | BasicExpression<T> | undefined | null
146146
): BasicExpression<boolean>
147147
export function lte<T extends string | number>(
148-
left: T | BasicExpression<T> | undefined,
149-
right: T | BasicExpression<T> | undefined
148+
left: T | BasicExpression<T> | undefined | null,
149+
right: T | BasicExpression<T> | undefined | null
150150
): BasicExpression<boolean>
151151
export function lte<T>(left: Aggregate<T>, right: any): BasicExpression<boolean>
152152
export function lte(left: any, right: any): BasicExpression<boolean> {

packages/db/src/query/builder/types.ts

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,12 @@ export type ResultTypeFromSelect<TSelectObject> = {
7979
? T
8080
: TSelectObject[K] extends Ref<infer T> | undefined
8181
? T | undefined
82+
: TSelectObject[K] extends Ref<infer T> | null
83+
? T | null
8284
: TSelectObject[K] extends RefProxy<infer T> | undefined
8385
? T | undefined
86+
: TSelectObject[K] extends RefProxy<infer T> | null
87+
? T | null
8488
: TSelectObject[K] extends BasicExpression<infer T>
8589
? T
8690
: TSelectObject[K] extends Aggregate<infer T>
@@ -142,36 +146,63 @@ export type RefProxyForContext<TContext extends Context> = {
142146
[K in keyof TContext[`schema`]]: IsExactlyUndefined<TContext[`schema`][K]> extends true
143147
? // T is exactly undefined
144148
RefProxy<TContext[`schema`][K]>
145-
: IsOptional<TContext[`schema`][K]> extends true
146-
? // T is optional (T | undefined) but not exactly undefined
147-
RefProxy<NonUndefined<TContext[`schema`][K]>> | undefined
148-
: // T is not optional - always wrap in RefProxy for top-level schema types
149+
: IsExactlyNull<TContext[`schema`][K]> extends true
150+
? // T is exactly null
149151
RefProxy<TContext[`schema`][K]>
152+
: IsOptional<TContext[`schema`][K]> extends true
153+
? // T is optional (T | undefined) but not exactly undefined
154+
RefProxy<NonUndefined<TContext[`schema`][K]>> | undefined
155+
: IsNullable<TContext[`schema`][K]> extends true
156+
? // T is nullable (T | null) but not exactly null
157+
RefProxy<NonNull<TContext[`schema`][K]>> | null
158+
: // T is not optional or nullable - always wrap in RefProxy for top-level schema types
159+
RefProxy<TContext[`schema`][K]>
150160
}
151161

152162
// Helper type to check if T is exactly undefined
153163
type IsExactlyUndefined<T> = [T] extends [undefined] ? true : false
154164

165+
// Helper type to check if T is exactly null
166+
type IsExactlyNull<T> = [T] extends [null] ? true : false
167+
155168
// Helper type to check if T includes undefined (is optional)
156169
type IsOptional<T> = undefined extends T ? true : false
157170

171+
// Helper type to check if T includes null (is nullable)
172+
type IsNullable<T> = null extends T ? true : false
173+
174+
// Helper type to check if T includes undefined or null
175+
type IsOptionalOrNullable<T> = IsOptional<T> extends true ? true : IsNullable<T> extends true ? true : false
176+
158177
// Helper type to extract non-undefined type
159178
type NonUndefined<T> = T extends undefined ? never : T
160179

180+
// Helper type to extract non-null type
181+
type NonNull<T> = T extends null ? never : T
182+
183+
// Helper type to extract non-undefined and non-null type
184+
type NonUndefinedNonNull<T> = NonUndefined<NonNull<T>>
185+
161186
// Precompute the ref structure for an object type
162187
// This transforms { bio: string, contact: { email: string } } into
163188
// { bio: Ref<string>, contact: { email: Ref<string> } }
164189
// Only leaf values are wrapped in RefProxy, intermediate objects remain plain
165190
export type PrecomputeRefStructure<T extends Record<string, any>> = {
166191
[K in keyof T]: IsExactlyUndefined<T[K]> extends true
167192
? Ref<T[K]>
168-
: IsOptional<T[K]> extends true
169-
? NonUndefined<T[K]> extends Record<string, any>
170-
? PrecomputeRefStructure<NonUndefined<T[K]>> | undefined
171-
: Ref<NonUndefined<T[K]>> | undefined
172-
: T[K] extends Record<string, any>
173-
? PrecomputeRefStructure<T[K]>
174-
: Ref<T[K]>
193+
: IsExactlyNull<T[K]> extends true
194+
? Ref<T[K]>
195+
: IsOptional<T[K]> extends true
196+
? NonUndefined<T[K]> extends Record<string, any>
197+
? PrecomputeRefStructure<NonUndefined<T[K]>> | undefined
198+
: Ref<NonUndefined<T[K]>> | undefined
199+
: IsNullable<T[K]> extends true
200+
? NonNull<T[K]> extends Record<string, any>
201+
? PrecomputeRefStructure<NonNull<T[K]>> | null
202+
: Ref<NonNull<T[K]>> | null
203+
: T[K] extends Record<string, any>
204+
? PrecomputeRefStructure<T[K]>
205+
: Ref<T[K]>
175206
}
176207

177208
// Helper type for backward compatibility and reusable query callbacks
@@ -202,13 +233,19 @@ export type RefProxy<T = any> = {
202233
? {
203234
[K in keyof T]: IsExactlyUndefined<T[K]> extends true
204235
? Ref<T[K]>
205-
: IsOptional<T[K]> extends true
206-
? NonUndefined<T[K]> extends Record<string, any>
207-
? RefProxy<NonUndefined<T[K]>> | undefined
208-
: Ref<NonUndefined<T[K]>> | undefined
209-
: T[K] extends Record<string, any>
210-
? RefProxy<T[K]>
211-
: Ref<T[K]>
236+
: IsExactlyNull<T[K]> extends true
237+
? Ref<T[K]>
238+
: IsOptional<T[K]> extends true
239+
? NonUndefined<T[K]> extends Record<string, any>
240+
? RefProxy<NonUndefined<T[K]>> | undefined
241+
: Ref<NonUndefined<T[K]>> | undefined
242+
: IsNullable<T[K]> extends true
243+
? NonNull<T[K]> extends Record<string, any>
244+
? RefProxy<NonNull<T[K]>> | null
245+
: Ref<NonNull<T[K]>> | null
246+
: T[K] extends Record<string, any>
247+
? RefProxy<T[K]>
248+
: Ref<T[K]>
212249
}
213250
: {})
214251

@@ -217,13 +254,19 @@ export type SpreadableRefProxy<T> = T extends Record<string, any>
217254
? {
218255
[K in keyof T]: IsExactlyUndefined<T[K]> extends true
219256
? Ref<T[K]>
220-
: IsOptional<T[K]> extends true
221-
? NonUndefined<T[K]> extends Record<string, any>
222-
? RefProxy<NonUndefined<T[K]>> | undefined
223-
: Ref<NonUndefined<T[K]>> | undefined
224-
: T[K] extends Record<string, any>
225-
? RefProxy<T[K]>
226-
: Ref<T[K]>
257+
: IsExactlyNull<T[K]> extends true
258+
? Ref<T[K]>
259+
: IsOptional<T[K]> extends true
260+
? NonUndefined<T[K]> extends Record<string, any>
261+
? RefProxy<NonUndefined<T[K]>> | undefined
262+
: Ref<NonUndefined<T[K]>> | undefined
263+
: IsNullable<T[K]> extends true
264+
? NonNull<T[K]> extends Record<string, any>
265+
? RefProxy<NonNull<T[K]>> | null
266+
: Ref<NonNull<T[K]>> | null
267+
: T[K] extends Record<string, any>
268+
? RefProxy<T[K]>
269+
: Ref<T[K]>
227270
}
228271
: {}
229272

packages/db/tests/query/builder/callback-types.test-d.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ describe(`Query Builder Callback Types`, () => {
100100
expectTypeOf(user.age).toEqualTypeOf<Ref<number>>()
101101
expectTypeOf(user.active).toEqualTypeOf<Ref<boolean>>()
102102
expectTypeOf(user.department_id).toEqualTypeOf<
103-
Ref<number | null>
103+
Ref<number> | null
104104
>()
105105
expectTypeOf(user.salary).toEqualTypeOf<Ref<number>>()
106106
expectTypeOf(user.created_at).toEqualTypeOf<Ref<string>>()
@@ -122,7 +122,7 @@ describe(`Query Builder Callback Types`, () => {
122122
.select(({ user, dept }) => {
123123
// Test cross-table property access
124124
expectTypeOf(user.department_id).toEqualTypeOf<
125-
Ref<number | null>
125+
Ref<number> | null
126126
>()
127127
expectTypeOf(dept?.id).toEqualTypeOf<Ref<number> | undefined>()
128128
expectTypeOf(dept?.name).toEqualTypeOf<Ref<string> | undefined>()
@@ -209,7 +209,7 @@ describe(`Query Builder Callback Types`, () => {
209209
expectTypeOf(user.id).toEqualTypeOf<Ref<number>>()
210210
expectTypeOf(user.active).toEqualTypeOf<Ref<boolean>>()
211211
expectTypeOf(user.department_id).toEqualTypeOf<
212-
Ref<number | null>
212+
Ref<number> | null
213213
>()
214214

215215
return eq(user.active, true)
@@ -298,7 +298,7 @@ describe(`Query Builder Callback Types`, () => {
298298
.join({ dept: departmentsCollection }, ({ user, dept }) => {
299299
// Test property access for join conditions
300300
expectTypeOf(user.department_id).toEqualTypeOf<
301-
Ref<number | null>
301+
Ref<number> | null
302302
>()
303303
expectTypeOf(dept.id).toEqualTypeOf<Ref<number>>()
304304

@@ -394,7 +394,7 @@ describe(`Query Builder Callback Types`, () => {
394394
// Test that user is the correct RefProxy type
395395
// expectTypeOf(user).toEqualTypeOf<RefProxy<User>>()
396396
expectTypeOf(user.department_id).toEqualTypeOf<
397-
Ref<number | null>
397+
Ref<number> | null
398398
>()
399399
expectTypeOf(user.active).toEqualTypeOf<Ref<boolean>>()
400400

@@ -407,7 +407,7 @@ describe(`Query Builder Callback Types`, () => {
407407
// Test array return type for multiple columns
408408
const groupColumns = [user.department_id, user.active]
409409
expectTypeOf(groupColumns).toEqualTypeOf<
410-
Array<Ref<number | null> | Ref<boolean>>
410+
Array<(Ref<number> | null) | Ref<boolean>>
411411
>()
412412

413413
return [user.department_id, user.active]
@@ -438,7 +438,7 @@ describe(`Query Builder Callback Types`, () => {
438438
.having(({ user }) => {
439439
expectTypeOf(user.id).toEqualTypeOf<Ref<number>>()
440440
expectTypeOf(user.department_id).toEqualTypeOf<
441-
Ref<number | null>
441+
Ref<number> | null
442442
>()
443443

444444
return gt(count(user.id), 5)
@@ -452,7 +452,7 @@ describe(`Query Builder Callback Types`, () => {
452452
.having(({ user }) => {
453453
expectTypeOf(user.id).toEqualTypeOf<Ref<number>>()
454454
expectTypeOf(user.department_id).toEqualTypeOf<
455-
Ref<number | null>
455+
Ref<number> | null
456456
>()
457457
// Test aggregate functions in having
458458
expectTypeOf(count(user.id)).toEqualTypeOf<Aggregate<number>>()

packages/db/tests/query/where.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,11 +1411,11 @@ function createWhereTests(autoIndex: `off` | `eager`): void {
14111411
query: (q) =>
14121412
q
14131413
.from({ emp: employeesCollection })
1414-
.where(({ emp }) => eq(emp.contact?.address.city, `San Francisco`))
1414+
.where(({ emp }) => eq(emp.contact?.address?.city, `San Francisco`))
14151415
.select(({ emp }) => ({
14161416
id: emp.id,
14171417
name: emp.name,
1418-
city: emp.contact?.address.city,
1418+
city: emp.contact?.address?.city,
14191419
})),
14201420
})
14211421

@@ -1473,15 +1473,15 @@ function createWhereTests(autoIndex: `off` | `eager`): void {
14731473
.where(({ emp }) =>
14741474
and(
14751475
eq(emp.active, true),
1476-
eq(emp.contact?.address.state, `CA`),
1476+
eq(emp.contact?.address?.state, `CA`),
14771477
gte(emp.profile?.experience.years, 5)
14781478
)
14791479
)
14801480
.select(({ emp }) => ({
14811481
id: emp.id,
14821482
name: emp.name,
14831483
years: emp.profile?.experience.years,
1484-
state: emp.contact?.address.state,
1484+
state: emp.contact?.address?.state,
14851485
})),
14861486
})
14871487

@@ -1633,14 +1633,14 @@ function createWhereTests(autoIndex: `off` | `eager`): void {
16331633
.from({ emp: employeesCollection })
16341634
.where(({ emp }) =>
16351635
or(
1636-
eq(emp.contact?.address.city, `San Francisco`),
1636+
eq(emp.contact?.address?.city, `San Francisco`),
16371637
inArray(`Python`, emp.profile?.skills)
16381638
)
16391639
)
16401640
.select(({ emp }) => ({
16411641
id: emp.id,
16421642
name: emp.name,
1643-
city: emp.contact?.address.city,
1643+
city: emp.contact?.address?.city,
16441644
hasPython: inArray(`Python`, emp.profile?.skills),
16451645
})),
16461646
})

0 commit comments

Comments
 (0)