@tanstack/db 0.0.14 → 0.0.16

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.
Files changed (197) hide show
  1. package/dist/cjs/collection.cjs +117 -104
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +18 -21
  4. package/dist/cjs/index.cjs +31 -13
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/index.d.cts +0 -1
  7. package/dist/cjs/query/builder/functions.cjs +107 -0
  8. package/dist/cjs/query/builder/functions.cjs.map +1 -0
  9. package/dist/cjs/query/builder/functions.d.cts +38 -0
  10. package/dist/cjs/query/builder/index.cjs +499 -0
  11. package/dist/cjs/query/builder/index.cjs.map +1 -0
  12. package/dist/cjs/query/builder/index.d.cts +324 -0
  13. package/dist/cjs/query/builder/ref-proxy.cjs +92 -0
  14. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -0
  15. package/dist/cjs/query/builder/ref-proxy.d.cts +28 -0
  16. package/dist/cjs/query/builder/types.d.cts +81 -0
  17. package/dist/cjs/query/compiler/evaluators.cjs +261 -0
  18. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -0
  19. package/dist/cjs/query/compiler/evaluators.d.cts +11 -0
  20. package/dist/cjs/query/compiler/group-by.cjs +271 -0
  21. package/dist/cjs/query/compiler/group-by.cjs.map +1 -0
  22. package/dist/cjs/query/compiler/group-by.d.cts +7 -0
  23. package/dist/cjs/query/compiler/index.cjs +181 -0
  24. package/dist/cjs/query/compiler/index.cjs.map +1 -0
  25. package/dist/cjs/query/compiler/index.d.cts +15 -0
  26. package/dist/cjs/query/compiler/joins.cjs +116 -0
  27. package/dist/cjs/query/compiler/joins.cjs.map +1 -0
  28. package/dist/cjs/query/compiler/joins.d.cts +11 -0
  29. package/dist/cjs/query/compiler/order-by.cjs +89 -0
  30. package/dist/cjs/query/compiler/order-by.cjs.map +1 -0
  31. package/dist/cjs/query/compiler/order-by.d.cts +9 -0
  32. package/dist/cjs/query/compiler/select.cjs +57 -0
  33. package/dist/cjs/query/compiler/select.cjs.map +1 -0
  34. package/dist/cjs/query/compiler/select.d.cts +15 -0
  35. package/dist/cjs/query/index.d.cts +5 -5
  36. package/dist/cjs/query/ir.cjs +57 -0
  37. package/dist/cjs/query/ir.cjs.map +1 -0
  38. package/dist/cjs/query/ir.d.cts +81 -0
  39. package/dist/cjs/query/live-query-collection.cjs +224 -0
  40. package/dist/cjs/query/live-query-collection.cjs.map +1 -0
  41. package/dist/cjs/query/live-query-collection.d.cts +124 -0
  42. package/dist/cjs/transactions.cjs +20 -13
  43. package/dist/cjs/transactions.cjs.map +1 -1
  44. package/dist/cjs/transactions.d.cts +10 -1
  45. package/dist/cjs/types.d.cts +13 -0
  46. package/dist/esm/collection.d.ts +18 -21
  47. package/dist/esm/collection.js +118 -105
  48. package/dist/esm/collection.js.map +1 -1
  49. package/dist/esm/index.d.ts +0 -1
  50. package/dist/esm/index.js +30 -12
  51. package/dist/esm/query/builder/functions.d.ts +38 -0
  52. package/dist/esm/query/builder/functions.js +107 -0
  53. package/dist/esm/query/builder/functions.js.map +1 -0
  54. package/dist/esm/query/builder/index.d.ts +324 -0
  55. package/dist/esm/query/builder/index.js +499 -0
  56. package/dist/esm/query/builder/index.js.map +1 -0
  57. package/dist/esm/query/builder/ref-proxy.d.ts +28 -0
  58. package/dist/esm/query/builder/ref-proxy.js +92 -0
  59. package/dist/esm/query/builder/ref-proxy.js.map +1 -0
  60. package/dist/esm/query/builder/types.d.ts +81 -0
  61. package/dist/esm/query/compiler/evaluators.d.ts +11 -0
  62. package/dist/esm/query/compiler/evaluators.js +261 -0
  63. package/dist/esm/query/compiler/evaluators.js.map +1 -0
  64. package/dist/esm/query/compiler/group-by.d.ts +7 -0
  65. package/dist/esm/query/compiler/group-by.js +271 -0
  66. package/dist/esm/query/compiler/group-by.js.map +1 -0
  67. package/dist/esm/query/compiler/index.d.ts +15 -0
  68. package/dist/esm/query/compiler/index.js +181 -0
  69. package/dist/esm/query/compiler/index.js.map +1 -0
  70. package/dist/esm/query/compiler/joins.d.ts +11 -0
  71. package/dist/esm/query/compiler/joins.js +116 -0
  72. package/dist/esm/query/compiler/joins.js.map +1 -0
  73. package/dist/esm/query/compiler/order-by.d.ts +9 -0
  74. package/dist/esm/query/compiler/order-by.js +89 -0
  75. package/dist/esm/query/compiler/order-by.js.map +1 -0
  76. package/dist/esm/query/compiler/select.d.ts +15 -0
  77. package/dist/esm/query/compiler/select.js +57 -0
  78. package/dist/esm/query/compiler/select.js.map +1 -0
  79. package/dist/esm/query/index.d.ts +5 -5
  80. package/dist/esm/query/ir.d.ts +81 -0
  81. package/dist/esm/query/ir.js +57 -0
  82. package/dist/esm/query/ir.js.map +1 -0
  83. package/dist/esm/query/live-query-collection.d.ts +124 -0
  84. package/dist/esm/query/live-query-collection.js +224 -0
  85. package/dist/esm/query/live-query-collection.js.map +1 -0
  86. package/dist/esm/transactions.d.ts +10 -1
  87. package/dist/esm/transactions.js +20 -13
  88. package/dist/esm/transactions.js.map +1 -1
  89. package/dist/esm/types.d.ts +13 -0
  90. package/package.json +3 -4
  91. package/src/collection.ts +152 -129
  92. package/src/index.ts +0 -1
  93. package/src/query/builder/functions.ts +267 -0
  94. package/src/query/builder/index.ts +648 -0
  95. package/src/query/builder/ref-proxy.ts +156 -0
  96. package/src/query/builder/types.ts +282 -0
  97. package/src/query/compiler/evaluators.ts +315 -0
  98. package/src/query/compiler/group-by.ts +428 -0
  99. package/src/query/compiler/index.ts +276 -0
  100. package/src/query/compiler/joins.ts +228 -0
  101. package/src/query/compiler/order-by.ts +139 -0
  102. package/src/query/compiler/select.ts +173 -0
  103. package/src/query/index.ts +54 -5
  104. package/src/query/ir.ts +128 -0
  105. package/src/query/live-query-collection.ts +512 -0
  106. package/src/transactions.ts +27 -16
  107. package/src/types.ts +15 -0
  108. package/dist/cjs/query/compiled-query.cjs +0 -160
  109. package/dist/cjs/query/compiled-query.cjs.map +0 -1
  110. package/dist/cjs/query/compiled-query.d.cts +0 -20
  111. package/dist/cjs/query/evaluators.cjs +0 -161
  112. package/dist/cjs/query/evaluators.cjs.map +0 -1
  113. package/dist/cjs/query/evaluators.d.cts +0 -14
  114. package/dist/cjs/query/extractors.cjs +0 -122
  115. package/dist/cjs/query/extractors.cjs.map +0 -1
  116. package/dist/cjs/query/extractors.d.cts +0 -22
  117. package/dist/cjs/query/functions.cjs +0 -152
  118. package/dist/cjs/query/functions.cjs.map +0 -1
  119. package/dist/cjs/query/functions.d.cts +0 -21
  120. package/dist/cjs/query/group-by.cjs +0 -88
  121. package/dist/cjs/query/group-by.cjs.map +0 -1
  122. package/dist/cjs/query/group-by.d.cts +0 -40
  123. package/dist/cjs/query/joins.cjs +0 -141
  124. package/dist/cjs/query/joins.cjs.map +0 -1
  125. package/dist/cjs/query/joins.d.cts +0 -14
  126. package/dist/cjs/query/order-by.cjs +0 -185
  127. package/dist/cjs/query/order-by.cjs.map +0 -1
  128. package/dist/cjs/query/order-by.d.cts +0 -3
  129. package/dist/cjs/query/pipeline-compiler.cjs +0 -89
  130. package/dist/cjs/query/pipeline-compiler.cjs.map +0 -1
  131. package/dist/cjs/query/pipeline-compiler.d.cts +0 -10
  132. package/dist/cjs/query/query-builder.cjs +0 -307
  133. package/dist/cjs/query/query-builder.cjs.map +0 -1
  134. package/dist/cjs/query/query-builder.d.cts +0 -225
  135. package/dist/cjs/query/schema.d.cts +0 -100
  136. package/dist/cjs/query/select.cjs +0 -130
  137. package/dist/cjs/query/select.cjs.map +0 -1
  138. package/dist/cjs/query/select.d.cts +0 -3
  139. package/dist/cjs/query/types.d.cts +0 -189
  140. package/dist/cjs/query/utils.cjs +0 -154
  141. package/dist/cjs/query/utils.cjs.map +0 -1
  142. package/dist/cjs/query/utils.d.cts +0 -37
  143. package/dist/cjs/utils.cjs +0 -17
  144. package/dist/cjs/utils.cjs.map +0 -1
  145. package/dist/cjs/utils.d.cts +0 -3
  146. package/dist/esm/query/compiled-query.d.ts +0 -20
  147. package/dist/esm/query/compiled-query.js +0 -160
  148. package/dist/esm/query/compiled-query.js.map +0 -1
  149. package/dist/esm/query/evaluators.d.ts +0 -14
  150. package/dist/esm/query/evaluators.js +0 -161
  151. package/dist/esm/query/evaluators.js.map +0 -1
  152. package/dist/esm/query/extractors.d.ts +0 -22
  153. package/dist/esm/query/extractors.js +0 -122
  154. package/dist/esm/query/extractors.js.map +0 -1
  155. package/dist/esm/query/functions.d.ts +0 -21
  156. package/dist/esm/query/functions.js +0 -152
  157. package/dist/esm/query/functions.js.map +0 -1
  158. package/dist/esm/query/group-by.d.ts +0 -40
  159. package/dist/esm/query/group-by.js +0 -88
  160. package/dist/esm/query/group-by.js.map +0 -1
  161. package/dist/esm/query/joins.d.ts +0 -14
  162. package/dist/esm/query/joins.js +0 -141
  163. package/dist/esm/query/joins.js.map +0 -1
  164. package/dist/esm/query/order-by.d.ts +0 -3
  165. package/dist/esm/query/order-by.js +0 -185
  166. package/dist/esm/query/order-by.js.map +0 -1
  167. package/dist/esm/query/pipeline-compiler.d.ts +0 -10
  168. package/dist/esm/query/pipeline-compiler.js +0 -89
  169. package/dist/esm/query/pipeline-compiler.js.map +0 -1
  170. package/dist/esm/query/query-builder.d.ts +0 -225
  171. package/dist/esm/query/query-builder.js +0 -307
  172. package/dist/esm/query/query-builder.js.map +0 -1
  173. package/dist/esm/query/schema.d.ts +0 -100
  174. package/dist/esm/query/select.d.ts +0 -3
  175. package/dist/esm/query/select.js +0 -130
  176. package/dist/esm/query/select.js.map +0 -1
  177. package/dist/esm/query/types.d.ts +0 -189
  178. package/dist/esm/query/utils.d.ts +0 -37
  179. package/dist/esm/query/utils.js +0 -154
  180. package/dist/esm/query/utils.js.map +0 -1
  181. package/dist/esm/utils.d.ts +0 -3
  182. package/dist/esm/utils.js +0 -17
  183. package/dist/esm/utils.js.map +0 -1
  184. package/src/query/compiled-query.ts +0 -234
  185. package/src/query/evaluators.ts +0 -250
  186. package/src/query/extractors.ts +0 -214
  187. package/src/query/functions.ts +0 -297
  188. package/src/query/group-by.ts +0 -139
  189. package/src/query/joins.ts +0 -260
  190. package/src/query/order-by.ts +0 -264
  191. package/src/query/pipeline-compiler.ts +0 -149
  192. package/src/query/query-builder.ts +0 -902
  193. package/src/query/schema.ts +0 -268
  194. package/src/query/select.ts +0 -208
  195. package/src/query/types.ts +0 -418
  196. package/src/query/utils.ts +0 -245
  197. package/src/utils.ts +0 -15
@@ -0,0 +1,156 @@
1
+ import { PropRef, Value } from "../ir.js"
2
+ import type { BasicExpression } from "../ir.js"
3
+
4
+ export interface RefProxy<T = any> {
5
+ /** @internal */
6
+ readonly __refProxy: true
7
+ /** @internal */
8
+ readonly __path: Array<string>
9
+ /** @internal */
10
+ readonly __type: T
11
+ }
12
+
13
+ /**
14
+ * Creates a proxy object that records property access paths
15
+ * Used in callbacks like where, select, etc. to create type-safe references
16
+ */
17
+ export function createRefProxy<T extends Record<string, any>>(
18
+ aliases: Array<string>
19
+ ): RefProxy<T> & T {
20
+ const cache = new Map<string, any>()
21
+ const spreadSentinels = new Set<string>() // Track which aliases have been spread
22
+
23
+ function createProxy(path: Array<string>): any {
24
+ const pathKey = path.join(`.`)
25
+ if (cache.has(pathKey)) {
26
+ return cache.get(pathKey)
27
+ }
28
+
29
+ const proxy = new Proxy({} as any, {
30
+ get(target, prop, receiver) {
31
+ if (prop === `__refProxy`) return true
32
+ if (prop === `__path`) return path
33
+ if (prop === `__type`) return undefined // Type is only for TypeScript inference
34
+ if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver)
35
+
36
+ const newPath = [...path, String(prop)]
37
+ return createProxy(newPath)
38
+ },
39
+
40
+ has(target, prop) {
41
+ if (prop === `__refProxy` || prop === `__path` || prop === `__type`)
42
+ return true
43
+ return Reflect.has(target, prop)
44
+ },
45
+
46
+ ownKeys(target) {
47
+ // If this is a table-level proxy (path length 1), mark it as spread
48
+ if (path.length === 1) {
49
+ const aliasName = path[0]!
50
+ spreadSentinels.add(aliasName)
51
+ }
52
+ return Reflect.ownKeys(target)
53
+ },
54
+
55
+ getOwnPropertyDescriptor(target, prop) {
56
+ if (prop === `__refProxy` || prop === `__path` || prop === `__type`) {
57
+ return { enumerable: false, configurable: true }
58
+ }
59
+ return Reflect.getOwnPropertyDescriptor(target, prop)
60
+ },
61
+ })
62
+
63
+ cache.set(pathKey, proxy)
64
+ return proxy
65
+ }
66
+
67
+ // Create the root proxy with all aliases as top-level properties
68
+ const rootProxy = new Proxy({} as any, {
69
+ get(target, prop, receiver) {
70
+ if (prop === `__refProxy`) return true
71
+ if (prop === `__path`) return []
72
+ if (prop === `__type`) return undefined // Type is only for TypeScript inference
73
+ if (prop === `__spreadSentinels`) return spreadSentinels // Expose spread sentinels
74
+ if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver)
75
+
76
+ const propStr = String(prop)
77
+ if (aliases.includes(propStr)) {
78
+ return createProxy([propStr])
79
+ }
80
+
81
+ return undefined
82
+ },
83
+
84
+ has(target, prop) {
85
+ if (
86
+ prop === `__refProxy` ||
87
+ prop === `__path` ||
88
+ prop === `__type` ||
89
+ prop === `__spreadSentinels`
90
+ )
91
+ return true
92
+ if (typeof prop === `string` && aliases.includes(prop)) return true
93
+ return Reflect.has(target, prop)
94
+ },
95
+
96
+ ownKeys(_target) {
97
+ return [...aliases, `__refProxy`, `__path`, `__type`, `__spreadSentinels`]
98
+ },
99
+
100
+ getOwnPropertyDescriptor(target, prop) {
101
+ if (
102
+ prop === `__refProxy` ||
103
+ prop === `__path` ||
104
+ prop === `__type` ||
105
+ prop === `__spreadSentinels`
106
+ ) {
107
+ return { enumerable: false, configurable: true }
108
+ }
109
+ if (typeof prop === `string` && aliases.includes(prop)) {
110
+ return { enumerable: true, configurable: true }
111
+ }
112
+ return undefined
113
+ },
114
+ })
115
+
116
+ return rootProxy
117
+ }
118
+
119
+ /**
120
+ * Converts a value to an Expression
121
+ * If it's a RefProxy, creates a Ref, otherwise creates a Value
122
+ */
123
+ export function toExpression<T = any>(value: T): BasicExpression<T>
124
+ export function toExpression(value: RefProxy<any>): BasicExpression<any>
125
+ export function toExpression(value: any): BasicExpression<any> {
126
+ if (isRefProxy(value)) {
127
+ return new PropRef(value.__path)
128
+ }
129
+ // If it's already an Expression (Func, Ref, Value) or Agg, return it directly
130
+ if (
131
+ value &&
132
+ typeof value === `object` &&
133
+ `type` in value &&
134
+ (value.type === `func` ||
135
+ value.type === `ref` ||
136
+ value.type === `val` ||
137
+ value.type === `agg`)
138
+ ) {
139
+ return value
140
+ }
141
+ return new Value(value)
142
+ }
143
+
144
+ /**
145
+ * Type guard to check if a value is a RefProxy
146
+ */
147
+ export function isRefProxy(value: any): value is RefProxy {
148
+ return value && typeof value === `object` && value.__refProxy === true
149
+ }
150
+
151
+ /**
152
+ * Helper to create a Value expression from a literal
153
+ */
154
+ export function val<T>(value: T): BasicExpression<T> {
155
+ return new Value(value)
156
+ }
@@ -0,0 +1,282 @@
1
+ import type { CollectionImpl } from "../../collection.js"
2
+ import type { Aggregate, BasicExpression } from "../ir.js"
3
+ import type { QueryBuilder } from "./index.js"
4
+
5
+ export interface Context {
6
+ // The collections available in the base schema
7
+ baseSchema: ContextSchema
8
+ // The current schema available (includes joined collections)
9
+ schema: ContextSchema
10
+ // the name of the source that was used in the from clause
11
+ fromSourceName: string
12
+ // Whether this query has joins
13
+ hasJoins?: boolean
14
+ // Mapping of table alias to join type for easy lookup
15
+ joinTypes?: Record<
16
+ string,
17
+ `inner` | `left` | `right` | `full` | `outer` | `cross`
18
+ >
19
+ // The result type after select (if select has been called)
20
+ result?: any
21
+ }
22
+
23
+ export type ContextSchema = Record<string, unknown>
24
+
25
+ export type Source = {
26
+ [alias: string]: CollectionImpl<any, any> | QueryBuilder<Context>
27
+ }
28
+
29
+ // Helper type to infer collection type from CollectionImpl
30
+ export type InferCollectionType<T> =
31
+ T extends CollectionImpl<infer U> ? U : never
32
+
33
+ // Helper type to create schema from source
34
+ export type SchemaFromSource<T extends Source> = Prettify<{
35
+ [K in keyof T]: T[K] extends CollectionImpl<infer U>
36
+ ? U
37
+ : T[K] extends QueryBuilder<infer TContext>
38
+ ? GetResult<TContext>
39
+ : never
40
+ }>
41
+
42
+ // Helper type to get all aliases from a context
43
+ export type GetAliases<TContext extends Context> = keyof TContext[`schema`]
44
+
45
+ // Callback type for where/having clauses
46
+ export type WhereCallback<TContext extends Context> = (
47
+ refs: RefProxyForContext<TContext>
48
+ ) => any
49
+
50
+ // Callback return type for select clauses
51
+ export type SelectObject<
52
+ T extends Record<
53
+ string,
54
+ BasicExpression | Aggregate | RefProxy | RefProxyFor<any>
55
+ > = Record<string, BasicExpression | Aggregate | RefProxy | RefProxyFor<any>>,
56
+ > = T
57
+
58
+ // Helper type to get the result type from a select object
59
+ export type ResultTypeFromSelect<TSelectObject> = {
60
+ [K in keyof TSelectObject]: TSelectObject[K] extends RefProxy<infer T>
61
+ ? // For RefProxy, preserve the type as-is (including optionality from joins)
62
+ T
63
+ : TSelectObject[K] extends BasicExpression<infer T>
64
+ ? T
65
+ : TSelectObject[K] extends Aggregate<infer T>
66
+ ? T
67
+ : TSelectObject[K] extends RefProxyFor<infer T>
68
+ ? // For RefProxyFor, preserve the type as-is (including optionality from joins)
69
+ T
70
+ : never
71
+ }
72
+
73
+ // Callback type for orderBy clauses
74
+ export type OrderByCallback<TContext extends Context> = (
75
+ refs: RefProxyForContext<TContext>
76
+ ) => any
77
+
78
+ // Callback type for groupBy clauses
79
+ export type GroupByCallback<TContext extends Context> = (
80
+ refs: RefProxyForContext<TContext>
81
+ ) => any
82
+
83
+ // Callback type for join on clauses
84
+ export type JoinOnCallback<TContext extends Context> = (
85
+ refs: RefProxyForContext<TContext>
86
+ ) => any
87
+
88
+ // Type for creating RefProxy objects based on context
89
+ export type RefProxyForContext<TContext extends Context> = {
90
+ [K in keyof TContext[`schema`]]: RefProxyFor<TContext[`schema`][K]>
91
+ }
92
+
93
+ // Helper type to check if T is exactly undefined
94
+ type IsExactlyUndefined<T> = [T] extends [undefined] ? true : false
95
+
96
+ // Helper type to check if T includes undefined (is optional)
97
+ type IsOptional<T> = undefined extends T ? true : false
98
+
99
+ // Helper type to extract non-undefined type
100
+ type NonUndefined<T> = T extends undefined ? never : T
101
+
102
+ // Helper type to create RefProxy for a specific type with optionality passthrough
103
+ // This is used to create the RefProxy object that is used in the query builder.
104
+ // Much of the complexity here is due to the fact that we need to handle optionality
105
+ // from joins. A left join will make the joined table optional, a right join will make
106
+ // the main table optional etc. This is applied to the schema, with the new namespaced
107
+ // source being `SourceType | undefined`.
108
+ // We then follow this through the ref proxy system so that accessing a property on
109
+ // and optional source will itsself be optional.
110
+ // If for example we join in `joinedTable` with a left join, then
111
+ // `where(({ joinedTable }) => joinedTable.name === `John`)`
112
+ // we want the the type of `name` to be `RefProxy<string | undefined>` to indicate that
113
+ // the `name` property is optional, as the joinedTable is also optional.
114
+ export type RefProxyFor<T> = OmitRefProxy<
115
+ IsExactlyUndefined<T> extends true
116
+ ? // T is exactly undefined
117
+ RefProxy<T>
118
+ : IsOptional<T> extends true
119
+ ? // T is optional (T | undefined) but not exactly undefined
120
+ NonUndefined<T> extends Record<string, any>
121
+ ? {
122
+ // Properties are accessible and their types become optional
123
+ [K in keyof NonUndefined<T>]: NonUndefined<T>[K] extends Record<
124
+ string,
125
+ any
126
+ >
127
+ ? RefProxyFor<NonUndefined<T>[K] | undefined> &
128
+ RefProxy<NonUndefined<T>[K] | undefined>
129
+ : RefProxy<NonUndefined<T>[K] | undefined>
130
+ } & RefProxy<T>
131
+ : RefProxy<T>
132
+ : // T is not optional
133
+ T extends Record<string, any>
134
+ ? {
135
+ [K in keyof T]: T[K] extends Record<string, any>
136
+ ? RefProxyFor<T[K]> & RefProxy<T[K]>
137
+ : RefProxy<T[K]>
138
+ } & RefProxy<T>
139
+ : RefProxy<T>
140
+ >
141
+
142
+ // This is the public type that is exported from the query builder
143
+ // and is used when constructing reusable query callbacks.
144
+ export type Ref<T> = RefProxyFor<T>
145
+
146
+ type OmitRefProxy<T> = Omit<T, `__refProxy` | `__path` | `__type`>
147
+
148
+ // The core RefProxy interface
149
+ export interface RefProxy<T = any> {
150
+ /** @internal */
151
+ readonly __refProxy: true
152
+ /** @internal */
153
+ readonly __path: Array<string>
154
+ /** @internal */
155
+ readonly __type: T
156
+ }
157
+
158
+ // Helper type to apply join optionality immediately when merging contexts
159
+ export type MergeContextWithJoinType<
160
+ TContext extends Context,
161
+ TNewSchema extends ContextSchema,
162
+ TJoinType extends `inner` | `left` | `right` | `full` | `outer` | `cross`,
163
+ > = {
164
+ baseSchema: TContext[`baseSchema`]
165
+ // Apply optionality immediately to the schema
166
+ schema: ApplyJoinOptionalityToMergedSchema<
167
+ TContext[`schema`],
168
+ TNewSchema,
169
+ TJoinType,
170
+ TContext[`fromSourceName`]
171
+ >
172
+ fromSourceName: TContext[`fromSourceName`]
173
+ hasJoins: true
174
+ // Track join types for reference
175
+ joinTypes: (TContext[`joinTypes`] extends Record<string, any>
176
+ ? TContext[`joinTypes`]
177
+ : {}) & {
178
+ [K in keyof TNewSchema & string]: TJoinType
179
+ }
180
+ result: TContext[`result`]
181
+ }
182
+
183
+ // Helper type to apply join optionality when merging new schema
184
+ export type ApplyJoinOptionalityToMergedSchema<
185
+ TExistingSchema extends ContextSchema,
186
+ TNewSchema extends ContextSchema,
187
+ TJoinType extends `inner` | `left` | `right` | `full` | `outer` | `cross`,
188
+ TFromSourceName extends string,
189
+ > = {
190
+ // Apply optionality to existing schema based on new join type
191
+ [K in keyof TExistingSchema]: K extends TFromSourceName
192
+ ? // Main table becomes optional if the new join is a right or full join
193
+ TJoinType extends `right` | `full`
194
+ ? TExistingSchema[K] | undefined
195
+ : TExistingSchema[K]
196
+ : // Other tables remain as they are (already have their optionality applied)
197
+ TExistingSchema[K]
198
+ } & {
199
+ // Apply optionality to new schema based on join type
200
+ [K in keyof TNewSchema]: TJoinType extends `left` | `full`
201
+ ? // New table becomes optional for left and full joins
202
+ TNewSchema[K] | undefined
203
+ : // New table is required for inner and right joins
204
+ TNewSchema[K]
205
+ }
206
+
207
+ // Helper type to get the result type from a context
208
+ export type GetResult<TContext extends Context> = Prettify<
209
+ TContext[`result`] extends object
210
+ ? TContext[`result`]
211
+ : TContext[`hasJoins`] extends true
212
+ ? // Optionality is already applied in the schema, just return it
213
+ TContext[`schema`]
214
+ : // Single table query - return the specific table
215
+ TContext[`schema`][TContext[`fromSourceName`]]
216
+ >
217
+
218
+ // Helper type to apply join optionality to the schema based on joinTypes
219
+ export type ApplyJoinOptionalityToSchema<
220
+ TSchema extends ContextSchema,
221
+ TJoinTypes extends Record<string, string>,
222
+ TFromSourceName extends string,
223
+ > = {
224
+ [K in keyof TSchema]: K extends TFromSourceName
225
+ ? // Main table (from source) - becomes optional if ANY right or full join exists
226
+ HasJoinType<TJoinTypes, `right` | `full`> extends true
227
+ ? TSchema[K] | undefined
228
+ : TSchema[K]
229
+ : // Joined table - check its specific join type AND if it's affected by subsequent joins
230
+ K extends keyof TJoinTypes
231
+ ? TJoinTypes[K] extends `left` | `full`
232
+ ? TSchema[K] | undefined
233
+ : // For inner/right joins, check if this table becomes optional due to subsequent right/full joins
234
+ // that don't include this table
235
+ IsTableMadeOptionalBySubsequentJoins<
236
+ K,
237
+ TJoinTypes,
238
+ TFromSourceName
239
+ > extends true
240
+ ? TSchema[K] | undefined
241
+ : TSchema[K]
242
+ : TSchema[K]
243
+ }
244
+
245
+ // Helper type to check if a table becomes optional due to subsequent joins
246
+ type IsTableMadeOptionalBySubsequentJoins<
247
+ TTableAlias extends string | number | symbol,
248
+ TJoinTypes extends Record<string, string>,
249
+ TFromSourceName extends string,
250
+ > = TTableAlias extends TFromSourceName
251
+ ? // Main table becomes optional if there are any right or full joins
252
+ HasJoinType<TJoinTypes, `right` | `full`>
253
+ : // Joined tables are not affected by subsequent joins in our current implementation
254
+ false
255
+
256
+ // Helper type to check if any join has one of the specified types
257
+ export type HasJoinType<
258
+ TJoinTypes extends Record<string, string>,
259
+ TTargetTypes extends string,
260
+ > = true extends {
261
+ [K in keyof TJoinTypes]: TJoinTypes[K] extends TTargetTypes ? true : false
262
+ }[keyof TJoinTypes]
263
+ ? true
264
+ : false
265
+
266
+ // Helper type to merge contexts (for joins) - backward compatibility
267
+ export type MergeContext<
268
+ TContext extends Context,
269
+ TNewSchema extends ContextSchema,
270
+ > = MergeContextWithJoinType<TContext, TNewSchema, `left`>
271
+
272
+ // Helper type for updating context with result type
273
+ export type WithResult<TContext extends Context, TResult> = Prettify<
274
+ Omit<TContext, `result`> & {
275
+ result: Prettify<TResult>
276
+ }
277
+ >
278
+
279
+ // Helper type to simplify complex types for better editor hints
280
+ export type Prettify<T> = {
281
+ [K in keyof T]: T[K]
282
+ } & {}