@contember/bindx 0.1.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.
Files changed (234) hide show
  1. package/dist/adapter/ContemberAdapter.d.ts +40 -0
  2. package/dist/adapter/ContemberAdapter.d.ts.map +1 -0
  3. package/dist/adapter/MockAdapter.d.ts +81 -0
  4. package/dist/adapter/MockAdapter.d.ts.map +1 -0
  5. package/dist/adapter/MockQueryEngine.d.ts +43 -0
  6. package/dist/adapter/MockQueryEngine.d.ts.map +1 -0
  7. package/dist/adapter/index.d.ts +5 -0
  8. package/dist/adapter/index.d.ts.map +1 -0
  9. package/dist/adapter/types.d.ts +184 -0
  10. package/dist/adapter/types.d.ts.map +1 -0
  11. package/dist/brand/ComponentBrand.d.ts +25 -0
  12. package/dist/brand/ComponentBrand.d.ts.map +1 -0
  13. package/dist/contember/index.d.ts +10 -0
  14. package/dist/contember/index.d.ts.map +1 -0
  15. package/dist/core/ActionDispatcher.d.ts +64 -0
  16. package/dist/core/ActionDispatcher.d.ts.map +1 -0
  17. package/dist/core/Disposable.d.ts +57 -0
  18. package/dist/core/Disposable.d.ts.map +1 -0
  19. package/dist/core/EntityLoader.d.ts +80 -0
  20. package/dist/core/EntityLoader.d.ts.map +1 -0
  21. package/dist/core/PersistenceManager.d.ts +126 -0
  22. package/dist/core/PersistenceManager.d.ts.map +1 -0
  23. package/dist/core/SelectionResolver.d.ts +21 -0
  24. package/dist/core/SelectionResolver.d.ts.map +1 -0
  25. package/dist/core/actionClassification.d.ts +48 -0
  26. package/dist/core/actionClassification.d.ts.map +1 -0
  27. package/dist/core/actions.d.ts +301 -0
  28. package/dist/core/actions.d.ts.map +1 -0
  29. package/dist/core/index.d.ts +4 -0
  30. package/dist/core/index.d.ts.map +1 -0
  31. package/dist/dataview/filterHandlers.d.ts +18 -0
  32. package/dist/dataview/filterHandlers.d.ts.map +1 -0
  33. package/dist/dataview/index.d.ts +3 -0
  34. package/dist/dataview/index.d.ts.map +1 -0
  35. package/dist/dataview/types.d.ts +164 -0
  36. package/dist/dataview/types.d.ts.map +1 -0
  37. package/dist/errors/UnfetchedFieldError.d.ts +11 -0
  38. package/dist/errors/UnfetchedFieldError.d.ts.map +1 -0
  39. package/dist/errors/errorPathResolver.d.ts +52 -0
  40. package/dist/errors/errorPathResolver.d.ts.map +1 -0
  41. package/dist/errors/index.d.ts +5 -0
  42. package/dist/errors/index.d.ts.map +1 -0
  43. package/dist/errors/pathMapper.d.ts +42 -0
  44. package/dist/errors/pathMapper.d.ts.map +1 -0
  45. package/dist/errors/types.d.ts +131 -0
  46. package/dist/errors/types.d.ts.map +1 -0
  47. package/dist/events/EventEmitter.d.ts +97 -0
  48. package/dist/events/EventEmitter.d.ts.map +1 -0
  49. package/dist/events/eventFactory.d.ts +40 -0
  50. package/dist/events/eventFactory.d.ts.map +1 -0
  51. package/dist/events/index.d.ts +15 -0
  52. package/dist/events/index.d.ts.map +1 -0
  53. package/dist/events/types.d.ts +264 -0
  54. package/dist/events/types.d.ts.map +1 -0
  55. package/dist/handles/BaseHandle.d.ts +64 -0
  56. package/dist/handles/BaseHandle.d.ts.map +1 -0
  57. package/dist/handles/EntityHandle.d.ts +208 -0
  58. package/dist/handles/EntityHandle.d.ts.map +1 -0
  59. package/dist/handles/FieldHandle.d.ts +111 -0
  60. package/dist/handles/FieldHandle.d.ts.map +1 -0
  61. package/dist/handles/HasManyListHandle.d.ts +166 -0
  62. package/dist/handles/HasManyListHandle.d.ts.map +1 -0
  63. package/dist/handles/HasOneHandle.d.ts +168 -0
  64. package/dist/handles/HasOneHandle.d.ts.map +1 -0
  65. package/dist/handles/PlaceholderHandle.d.ts +117 -0
  66. package/dist/handles/PlaceholderHandle.d.ts.map +1 -0
  67. package/dist/handles/index.d.ts +8 -0
  68. package/dist/handles/index.d.ts.map +1 -0
  69. package/dist/handles/proxyFactory.d.ts +66 -0
  70. package/dist/handles/proxyFactory.d.ts.map +1 -0
  71. package/dist/handles/types.d.ts +430 -0
  72. package/dist/handles/types.d.ts.map +1 -0
  73. package/dist/index.d.ts +53 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/internal.d.ts +11 -0
  76. package/dist/internal.d.ts.map +1 -0
  77. package/dist/persistence/BatchPersister.d.ts +142 -0
  78. package/dist/persistence/BatchPersister.d.ts.map +1 -0
  79. package/dist/persistence/ChangeRegistry.d.ts +87 -0
  80. package/dist/persistence/ChangeRegistry.d.ts.map +1 -0
  81. package/dist/persistence/ContemberSchemaMutationAdapter.d.ts +16 -0
  82. package/dist/persistence/ContemberSchemaMutationAdapter.d.ts.map +1 -0
  83. package/dist/persistence/MutationCollector.d.ts +96 -0
  84. package/dist/persistence/MutationCollector.d.ts.map +1 -0
  85. package/dist/persistence/MutationSchemaProvider.d.ts +30 -0
  86. package/dist/persistence/MutationSchemaProvider.d.ts.map +1 -0
  87. package/dist/persistence/index.d.ts +7 -0
  88. package/dist/persistence/index.d.ts.map +1 -0
  89. package/dist/persistence/types.d.ts +189 -0
  90. package/dist/persistence/types.d.ts.map +1 -0
  91. package/dist/roles/RoleSchemaRegistry.d.ts +77 -0
  92. package/dist/roles/RoleSchemaRegistry.d.ts.map +1 -0
  93. package/dist/roles/index.d.ts +11 -0
  94. package/dist/roles/index.d.ts.map +1 -0
  95. package/dist/roles/types.d.ts +242 -0
  96. package/dist/roles/types.d.ts.map +1 -0
  97. package/dist/schema/ContemberSchema.d.ts +98 -0
  98. package/dist/schema/ContemberSchema.d.ts.map +1 -0
  99. package/dist/schema/SchemaLoader.d.ts +28 -0
  100. package/dist/schema/SchemaLoader.d.ts.map +1 -0
  101. package/dist/schema/SchemaRegistry.d.ts +95 -0
  102. package/dist/schema/SchemaRegistry.d.ts.map +1 -0
  103. package/dist/schema/index.d.ts +6 -0
  104. package/dist/schema/index.d.ts.map +1 -0
  105. package/dist/schema/roles.d.ts +62 -0
  106. package/dist/schema/roles.d.ts.map +1 -0
  107. package/dist/schema/types.d.ts +179 -0
  108. package/dist/schema/types.d.ts.map +1 -0
  109. package/dist/selection/SelectionMetaCollector.d.ts +32 -0
  110. package/dist/selection/SelectionMetaCollector.d.ts.map +1 -0
  111. package/dist/selection/SelectionScope.d.ts +77 -0
  112. package/dist/selection/SelectionScope.d.ts.map +1 -0
  113. package/dist/selection/buildQuery.d.ts +45 -0
  114. package/dist/selection/buildQuery.d.ts.map +1 -0
  115. package/dist/selection/createFragment.d.ts +29 -0
  116. package/dist/selection/createFragment.d.ts.map +1 -0
  117. package/dist/selection/createSelectionBuilder.d.ts +18 -0
  118. package/dist/selection/createSelectionBuilder.d.ts.map +1 -0
  119. package/dist/selection/index.d.ts +8 -0
  120. package/dist/selection/index.d.ts.map +1 -0
  121. package/dist/selection/queryTypes.d.ts +84 -0
  122. package/dist/selection/queryTypes.d.ts.map +1 -0
  123. package/dist/selection/types.d.ts +238 -0
  124. package/dist/selection/types.d.ts.map +1 -0
  125. package/dist/store/DirtyTracker.d.ts +26 -0
  126. package/dist/store/DirtyTracker.d.ts.map +1 -0
  127. package/dist/store/EntityMetaStore.d.ts +52 -0
  128. package/dist/store/EntityMetaStore.d.ts.map +1 -0
  129. package/dist/store/EntitySnapshotStore.d.ts +63 -0
  130. package/dist/store/EntitySnapshotStore.d.ts.map +1 -0
  131. package/dist/store/ErrorStore.d.ts +82 -0
  132. package/dist/store/ErrorStore.d.ts.map +1 -0
  133. package/dist/store/RelationStore.d.ts +175 -0
  134. package/dist/store/RelationStore.d.ts.map +1 -0
  135. package/dist/store/SnapshotStore.d.ts +143 -0
  136. package/dist/store/SnapshotStore.d.ts.map +1 -0
  137. package/dist/store/SubscriptionManager.d.ts +90 -0
  138. package/dist/store/SubscriptionManager.d.ts.map +1 -0
  139. package/dist/store/TouchedStore.d.ts +24 -0
  140. package/dist/store/TouchedStore.d.ts.map +1 -0
  141. package/dist/store/entityId.d.ts +29 -0
  142. package/dist/store/entityId.d.ts.map +1 -0
  143. package/dist/store/index.d.ts +1 -0
  144. package/dist/store/index.d.ts.map +1 -0
  145. package/dist/store/snapshots.d.ts +144 -0
  146. package/dist/store/snapshots.d.ts.map +1 -0
  147. package/dist/undo/UndoManager.d.ts +120 -0
  148. package/dist/undo/UndoManager.d.ts.map +1 -0
  149. package/dist/undo/index.d.ts +3 -0
  150. package/dist/undo/index.d.ts.map +1 -0
  151. package/dist/undo/types.d.ts +74 -0
  152. package/dist/undo/types.d.ts.map +1 -0
  153. package/dist/utils/aliasGenerator.d.ts +25 -0
  154. package/dist/utils/aliasGenerator.d.ts.map +1 -0
  155. package/dist/utils/deepEqual.d.ts +6 -0
  156. package/dist/utils/deepEqual.d.ts.map +1 -0
  157. package/dist/utils/index.d.ts +3 -0
  158. package/dist/utils/index.d.ts.map +1 -0
  159. package/package.json +29 -0
  160. package/src/adapter/ContemberAdapter.ts +239 -0
  161. package/src/adapter/MockAdapter.ts +394 -0
  162. package/src/adapter/MockQueryEngine.ts +236 -0
  163. package/src/adapter/index.ts +21 -0
  164. package/src/adapter/types.ts +199 -0
  165. package/src/brand/ComponentBrand.ts +28 -0
  166. package/src/contember/index.ts +17 -0
  167. package/src/core/ActionDispatcher.ts +450 -0
  168. package/src/core/Disposable.ts +126 -0
  169. package/src/core/EntityLoader.ts +169 -0
  170. package/src/core/SelectionResolver.ts +78 -0
  171. package/src/core/actionClassification.ts +196 -0
  172. package/src/core/actions.ts +491 -0
  173. package/src/core/index.ts +20 -0
  174. package/src/dataview/filterHandlers.ts +443 -0
  175. package/src/dataview/index.ts +36 -0
  176. package/src/dataview/types.ts +220 -0
  177. package/src/errors/UnfetchedFieldError.ts +18 -0
  178. package/src/errors/errorPathResolver.ts +239 -0
  179. package/src/errors/index.ts +40 -0
  180. package/src/errors/pathMapper.ts +40 -0
  181. package/src/errors/types.ts +223 -0
  182. package/src/events/EventEmitter.ts +447 -0
  183. package/src/events/eventFactory.ts +430 -0
  184. package/src/events/index.ts +89 -0
  185. package/src/events/types.ts +374 -0
  186. package/src/handles/BaseHandle.ts +100 -0
  187. package/src/handles/EntityHandle.ts +538 -0
  188. package/src/handles/FieldHandle.ts +268 -0
  189. package/src/handles/HasManyListHandle.ts +500 -0
  190. package/src/handles/HasOneHandle.ts +500 -0
  191. package/src/handles/PlaceholderHandle.ts +321 -0
  192. package/src/handles/index.ts +37 -0
  193. package/src/handles/proxyFactory.ts +218 -0
  194. package/src/handles/types.ts +743 -0
  195. package/src/index.ts +384 -0
  196. package/src/internal.ts +19 -0
  197. package/src/persistence/BatchPersister.ts +911 -0
  198. package/src/persistence/ChangeRegistry.ts +150 -0
  199. package/src/persistence/ContemberSchemaMutationAdapter.ts +57 -0
  200. package/src/persistence/MutationCollector.ts +567 -0
  201. package/src/persistence/MutationSchemaProvider.ts +33 -0
  202. package/src/persistence/index.ts +23 -0
  203. package/src/persistence/types.ts +220 -0
  204. package/src/schema/ContemberSchema.ts +132 -0
  205. package/src/schema/SchemaLoader.ts +93 -0
  206. package/src/schema/SchemaRegistry.ts +286 -0
  207. package/src/schema/index.ts +51 -0
  208. package/src/schema/roles.ts +83 -0
  209. package/src/schema/types.ts +238 -0
  210. package/src/selection/SelectionMetaCollector.ts +122 -0
  211. package/src/selection/SelectionScope.ts +208 -0
  212. package/src/selection/buildQuery.ts +134 -0
  213. package/src/selection/createFragment.ts +50 -0
  214. package/src/selection/createSelectionBuilder.ts +283 -0
  215. package/src/selection/index.ts +31 -0
  216. package/src/selection/queryTypes.ts +135 -0
  217. package/src/selection/types.ts +320 -0
  218. package/src/store/DirtyTracker.ts +117 -0
  219. package/src/store/EntityMetaStore.ts +143 -0
  220. package/src/store/EntitySnapshotStore.ts +260 -0
  221. package/src/store/ErrorStore.ts +251 -0
  222. package/src/store/RelationStore.ts +696 -0
  223. package/src/store/SnapshotStore.ts +765 -0
  224. package/src/store/SubscriptionManager.ts +241 -0
  225. package/src/store/TouchedStore.ts +42 -0
  226. package/src/store/entityId.ts +43 -0
  227. package/src/store/index.ts +2 -0
  228. package/src/store/snapshots.ts +203 -0
  229. package/src/undo/UndoManager.ts +418 -0
  230. package/src/undo/index.ts +9 -0
  231. package/src/undo/types.ts +84 -0
  232. package/src/utils/aliasGenerator.ts +56 -0
  233. package/src/utils/deepEqual.ts +33 -0
  234. package/src/utils/index.ts +2 -0
@@ -0,0 +1,283 @@
1
+ import {
2
+ SELECTION_META,
3
+ type FluentFragment,
4
+ type SelectionBuilder,
5
+ type SelectionFieldMeta,
6
+ type SelectionMeta,
7
+ type HasManyOptions,
8
+ } from './types.js'
9
+ import { mergeSelections, SelectionMetaCollector } from './SelectionMetaCollector.js'
10
+ import { generateHasManyAlias } from '../utils/aliasGenerator.js'
11
+
12
+ /**
13
+ * Creates a fluent selection builder for an entity type.
14
+ * Uses Proxy to intercept method calls and build selection metadata.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const builder = createSelectionBuilder<Article>()
19
+ * const result = builder.id().title().author(a => a.name())
20
+ * const meta = result[SELECTION_META]
21
+ * ```
22
+ */
23
+ export function createSelectionBuilder<TEntity, TSelected extends object = object, THasManyParams extends object = object>(
24
+ initialMeta: SelectionMeta = { fields: new Map() },
25
+ ): SelectionBuilder<TEntity, TSelected, THasManyParams> {
26
+ const handler: ProxyHandler<object> = {
27
+ get(_target, prop: string | symbol) {
28
+ // Return metadata for introspection
29
+ if (prop === SELECTION_META) {
30
+ return initialMeta
31
+ }
32
+
33
+ // Type brand accessor
34
+ if (prop === '__selected') {
35
+ return undefined
36
+ }
37
+
38
+ // Skip internal symbols
39
+ if (typeof prop === 'symbol') {
40
+ return undefined
41
+ }
42
+
43
+ // Return a function that handles the field selection
44
+ return createFieldMethod<TEntity, TSelected, THasManyParams>(prop, initialMeta)
45
+ },
46
+ has(_target, prop: string | symbol) {
47
+ if (prop === SELECTION_META) {
48
+ return true
49
+ }
50
+ return false
51
+ },
52
+ }
53
+
54
+ return new Proxy({}, handler) as SelectionBuilder<TEntity, TSelected, THasManyParams>
55
+ }
56
+
57
+ /**
58
+ * Creates a method for selecting a specific field.
59
+ * The method signature depends on the field type (scalar, has-one, has-many).
60
+ */
61
+ function createFieldMethod<TEntity, TSelected extends object, THasManyParams extends object>(
62
+ fieldName: string,
63
+ currentMeta: SelectionMeta,
64
+ ) {
65
+ return function selectField(...args: unknown[]): SelectionBuilder<TEntity, object, object> {
66
+ // Parse arguments to determine what kind of selection this is
67
+ const { options, selector, fragment, fragments } = parseFieldArgs(args)
68
+
69
+ // Build field metadata
70
+ const fieldMeta: SelectionFieldMeta = {
71
+ fieldName,
72
+ alias: fieldName, // Will be updated below
73
+ path: [fieldName],
74
+ isRelation: false,
75
+ isArray: false,
76
+ nested: undefined,
77
+ hasManyParams: undefined,
78
+ }
79
+
80
+ // Handle relation with callback
81
+ if (typeof selector === 'function') {
82
+ const nestedBuilder = createSelectionBuilder<unknown>()
83
+ const resultBuilder = selector(nestedBuilder) as SelectionBuilder<unknown, object, object>
84
+ fieldMeta.nested = resultBuilder[SELECTION_META]
85
+ fieldMeta.isRelation = true
86
+
87
+ // Check if this looks like a has-many (options has filter/orderBy/limit)
88
+ if (options && isHasManyOptions(options)) {
89
+ fieldMeta.isArray = true
90
+ fieldMeta.hasManyParams = {
91
+ filter: (options as HasManyOptions).filter,
92
+ orderBy: (options as HasManyOptions).orderBy,
93
+ limit: (options as HasManyOptions).limit,
94
+ offset: (options as HasManyOptions).offset,
95
+ totalCount: (options as HasManyOptions).totalCount,
96
+ }
97
+ }
98
+ }
99
+
100
+ // Handle relation with single fragment
101
+ if (fragment && isFluentFragment(fragment)) {
102
+ fieldMeta.nested = fragment.__meta
103
+ fieldMeta.isRelation = true
104
+
105
+ // Check has-many params
106
+ if (options && isHasManyOptions(options)) {
107
+ fieldMeta.isArray = true
108
+ fieldMeta.hasManyParams = {
109
+ filter: (options as HasManyOptions).filter,
110
+ orderBy: (options as HasManyOptions).orderBy,
111
+ limit: (options as HasManyOptions).limit,
112
+ offset: (options as HasManyOptions).offset,
113
+ totalCount: (options as HasManyOptions).totalCount,
114
+ }
115
+ }
116
+ }
117
+
118
+ // Handle relation with multiple fragments (merged)
119
+ if (fragments && fragments.length > 0) {
120
+ // Merge all fragment selections into one
121
+ const mergedSelection = new SelectionMetaCollector()
122
+ for (const frag of fragments) {
123
+ mergeSelections(mergedSelection, frag.__meta)
124
+ }
125
+ fieldMeta.nested = mergedSelection
126
+ fieldMeta.isRelation = true
127
+
128
+ // Check has-many params
129
+ if (options && isHasManyOptions(options)) {
130
+ fieldMeta.isArray = true
131
+ fieldMeta.hasManyParams = {
132
+ filter: (options as HasManyOptions).filter,
133
+ orderBy: (options as HasManyOptions).orderBy,
134
+ limit: (options as HasManyOptions).limit,
135
+ offset: (options as HasManyOptions).offset,
136
+ totalCount: (options as HasManyOptions).totalCount,
137
+ }
138
+ }
139
+ }
140
+
141
+ // Determine alias:
142
+ // 1. Explicit alias from options takes priority
143
+ // 2. For has-many with params, auto-generate alias
144
+ // 3. Otherwise, use fieldName
145
+ const explicitAlias = (options as { as?: string } | undefined)?.as
146
+ if (explicitAlias) {
147
+ fieldMeta.alias = explicitAlias
148
+ } else if (fieldMeta.isArray && fieldMeta.hasManyParams) {
149
+ // Auto-generate alias for has-many with params
150
+ fieldMeta.alias = generateHasManyAlias(fieldName, fieldMeta.hasManyParams)
151
+ }
152
+ // else alias is already set to fieldName
153
+
154
+ // If it's a relation call without options but first arg is callback/fragment
155
+ // and there's no filter/orderBy, check if it's has-many by detecting array at runtime
156
+ // This is handled by the type system - if the field type is Array<T>, it will use HasManyMethod
157
+
158
+ // Create new metadata with this field added
159
+ const newMeta: SelectionMeta = {
160
+ fields: new Map(currentMeta.fields),
161
+ }
162
+ newMeta.fields.set(fieldMeta.alias, fieldMeta)
163
+
164
+ // Return new builder with accumulated selection
165
+ return createSelectionBuilder<TEntity, TSelected, THasManyParams>(newMeta)
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Type guard for HasManyOptions
171
+ */
172
+ function isHasManyOptions(value: unknown): value is HasManyOptions {
173
+ if (typeof value !== 'object' || value === null) {
174
+ return false
175
+ }
176
+ return 'filter' in value || 'orderBy' in value || 'limit' in value || 'offset' in value
177
+ }
178
+
179
+ /**
180
+ * Type guard for FluentFragment
181
+ */
182
+ function isFluentFragment(value: unknown): value is FluentFragment<unknown, unknown> {
183
+ return (
184
+ value !== null &&
185
+ typeof value === 'object' &&
186
+ '__isFragment' in value &&
187
+ (value as FluentFragment<unknown, unknown>).__isFragment === true
188
+ )
189
+ }
190
+
191
+ /**
192
+ * Parses the variable arguments to a field method.
193
+ *
194
+ * Possible call patterns:
195
+ * - e.title() -> scalar, no args
196
+ * - e.title({ as: 'x' }) -> scalar with alias
197
+ * - e.author(a => a.name()) -> has-one with callback
198
+ * - e.author(AuthorFragment) -> has-one with fragment
199
+ * - e.author(Frag1, Frag2, ...) -> has-one with multiple fragments (merged)
200
+ * - e.author({ as: 'x' }, a => a.name())-> has-one with alias + callback
201
+ * - e.author({ as: 'x' }, AuthorFrag) -> has-one with alias + fragment
202
+ * - e.author({ as: 'x' }, Frag1, Frag2) -> has-one with alias + multiple fragments
203
+ * - e.tags(t => t.name()) -> has-many with callback
204
+ * - e.tags(TagFragment) -> has-many with fragment
205
+ * - e.tags(Frag1, Frag2, ...) -> has-many with multiple fragments (merged)
206
+ * - e.tags({ filter: {} }, t => t.name()) -> has-many with options + callback
207
+ * - e.tags({ filter: {} }, TagFragment) -> has-many with options + fragment
208
+ * - e.tags({ filter: {} }, Frag1, Frag2) -> has-many with options + multiple fragments
209
+ */
210
+ function parseFieldArgs(args: unknown[]): {
211
+ options?: Record<string, unknown>
212
+ selector?: (builder: SelectionBuilder<unknown>) => SelectionBuilder<unknown, object, object>
213
+ fragment?: FluentFragment<unknown, unknown>
214
+ fragments?: FluentFragment<unknown, unknown>[]
215
+ } {
216
+ if (args.length === 0) {
217
+ // Just field name, no args: e.title()
218
+ return {}
219
+ }
220
+
221
+ if (args.length === 1) {
222
+ const arg = args[0]
223
+ if (typeof arg === 'function') {
224
+ // Selector callback: e.author(a => a.name())
225
+ return { selector: arg as (builder: SelectionBuilder<unknown>) => SelectionBuilder<unknown, object, object> }
226
+ }
227
+ if (typeof arg === 'object' && arg !== null) {
228
+ if (isFluentFragment(arg)) {
229
+ // Fragment: e.author(AuthorFragment)
230
+ return { fragment: arg }
231
+ }
232
+ // Options without selector: e.title({ as: 'caption' })
233
+ return { options: arg as Record<string, unknown> }
234
+ }
235
+ }
236
+
237
+ // Check for multiple fragments: e.author(Frag1, Frag2, ...)
238
+ if (args.length >= 2 && args.every(isFluentFragment)) {
239
+ return { fragments: args as FluentFragment<unknown, unknown>[] }
240
+ }
241
+
242
+ if (args.length >= 2) {
243
+ const [first, ...rest] = args
244
+
245
+ // Check if first is options object
246
+ if (typeof first === 'object' && first !== null && !isFluentFragment(first)) {
247
+ const second = rest[0]
248
+
249
+ if (typeof second === 'function') {
250
+ // Options + selector: e.tags({ filter: {...} }, t => t.name())
251
+ return {
252
+ options: first as Record<string, unknown>,
253
+ selector: second as (builder: SelectionBuilder<unknown>) => SelectionBuilder<unknown, object, object>,
254
+ }
255
+ }
256
+
257
+ // Check for options + multiple fragments
258
+ if (rest.every(isFluentFragment)) {
259
+ if (rest.length === 1) {
260
+ // Options + single fragment: e.tags({ filter: {...} }, TagFragment)
261
+ return {
262
+ options: first as Record<string, unknown>,
263
+ fragment: rest[0] as FluentFragment<unknown, unknown>,
264
+ }
265
+ }
266
+ // Options + multiple fragments: e.tags({ filter: {} }, Frag1, Frag2)
267
+ return {
268
+ options: first as Record<string, unknown>,
269
+ fragments: rest as FluentFragment<unknown, unknown>[],
270
+ }
271
+ }
272
+ }
273
+ }
274
+
275
+ return {}
276
+ }
277
+
278
+ /**
279
+ * Extracts SelectionMeta from a builder
280
+ */
281
+ export function getSelectionMeta(builder: SelectionBuilder<unknown, object, object>): SelectionMeta {
282
+ return builder[SELECTION_META]
283
+ }
@@ -0,0 +1,31 @@
1
+ // Types
2
+ export {
3
+ SELECTION_META,
4
+ type SelectionBuilder,
5
+ type SelectionMeta,
6
+ type SelectionFieldMeta,
7
+ type FluentFragment,
8
+ type FluentDefiner,
9
+ type HasManyOptions,
10
+ type InferSelection,
11
+ type ScalarMethod,
12
+ type HasOneMethod,
13
+ type HasManyMethod,
14
+ } from './types.js'
15
+
16
+ // Query types for typed filter/orderBy
17
+ export {
18
+ type EntityWhere,
19
+ type EntityOrderBy,
20
+ type ScalarCondition,
21
+ type OrderDirection,
22
+ type TypedHasManyOptions,
23
+ type AliasOptions,
24
+ } from './queryTypes.js'
25
+
26
+ // Runtime
27
+ export { createSelectionBuilder, getSelectionMeta } from './createSelectionBuilder.js'
28
+ export { createFragment } from './createFragment.js'
29
+ export { buildQueryFromSelection, collectPaths, type QuerySpec, type QueryFieldSpec } from './buildQuery.js'
30
+ export { SelectionMetaCollector, mergeSelections, createEmptySelection } from './SelectionMetaCollector.js'
31
+ export { SelectionScope, type HasManyParams } from './SelectionScope.js'
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Typed query parameter types for filter, orderBy, etc.
3
+ *
4
+ * Uses Input.Condition and Input.OrderDirection from @contember/schema
5
+ * for scalar conditions, wrapped in entity-aware types that work with
6
+ * plain TypeScript interfaces.
7
+ */
8
+
9
+ import { Input } from '@contember/schema'
10
+
11
+ // ============================================================================
12
+ // Re-export Contember types
13
+ // ============================================================================
14
+
15
+ /**
16
+ * Condition operators for scalar values - re-exported from @contember/schema
17
+ */
18
+ export type ScalarCondition<T> = Input.Condition<T>
19
+
20
+ /**
21
+ * Order direction enum values - string literal union from Contember enum
22
+ */
23
+ export type OrderDirection = `${Input.OrderDirection}`
24
+
25
+ // ============================================================================
26
+ // Where Types (for filtering)
27
+ // ============================================================================
28
+
29
+ /**
30
+ * Base composed where with AND/OR/NOT logic
31
+ */
32
+ export interface ComposedWhere<TEntity> {
33
+ readonly and?: readonly EntityWhere<TEntity>[]
34
+ readonly or?: readonly EntityWhere<TEntity>[]
35
+ readonly not?: EntityWhere<TEntity>
36
+ }
37
+
38
+ /**
39
+ * Checks if a type is a "plain object" (entity) vs a scalar like Date
40
+ * Date and other built-in objects are not treated as relations
41
+ */
42
+ type IsPlainObject<T> =
43
+ T extends Date ? false :
44
+ T extends Array<any> ? false :
45
+ T extends Function ? false :
46
+ T extends object ? true :
47
+ false
48
+
49
+ /**
50
+ * Field-level where clause for an entity
51
+ * Maps each field to its appropriate condition type using Input.Condition
52
+ */
53
+ export type FieldsWhere<TEntity> = {
54
+ readonly [K in keyof TEntity]?:
55
+ NonNullable<TEntity[K]> extends Array<infer U>
56
+ ? IsPlainObject<U> extends true
57
+ ? EntityWhere<U> | null // has-many: filter on related items (array of entities)
58
+ : Input.Condition<NonNullable<TEntity[K]>> | null // scalar array: use Input.Condition on the array (supports hasSome, contains, etc.)
59
+ : IsPlainObject<NonNullable<TEntity[K]>> extends true
60
+ ? EntityWhere<NonNullable<TEntity[K]>> | null // has-one: filter on related entity
61
+ : Input.Condition<NonNullable<TEntity[K]>> | null // scalar: use Input.Condition
62
+ }
63
+
64
+ /**
65
+ * Full where type for an entity - combines composed and field conditions
66
+ */
67
+ export type EntityWhere<TEntity> = ComposedWhere<TEntity> & FieldsWhere<TEntity>
68
+
69
+ // ============================================================================
70
+ // OrderBy Types (for sorting)
71
+ // ============================================================================
72
+
73
+ /**
74
+ * Order by clause for an entity
75
+ * Each scalar field can have a direction, relations can have nested ordering
76
+ */
77
+ export type EntityOrderBy<TEntity> = {
78
+ readonly [K in keyof TEntity]?:
79
+ NonNullable<TEntity[K]> extends Array<any>
80
+ ? never // has-many cannot be ordered by directly
81
+ : IsPlainObject<NonNullable<TEntity[K]>> extends true
82
+ ? EntityOrderBy<NonNullable<TEntity[K]>> | null // has-one: nested ordering
83
+ : OrderDirection | null // scalar: direction (including Date)
84
+ } & {
85
+ readonly _random?: boolean
86
+ readonly _randomSeeded?: number
87
+ }
88
+
89
+ // ============================================================================
90
+ // HasMany Options with Typed Parameters
91
+ // ============================================================================
92
+
93
+ /**
94
+ * Options for has-many relation selection with typed filter/orderBy
95
+ *
96
+ * @typeParam TItem - The related entity type (array item type)
97
+ * @typeParam TAlias - Optional alias for the field
98
+ */
99
+ export interface TypedHasManyOptions<
100
+ TItem,
101
+ TAlias extends string = string,
102
+ > {
103
+ readonly as?: TAlias
104
+ readonly filter?: EntityWhere<TItem>
105
+ readonly orderBy?: readonly EntityOrderBy<TItem>[]
106
+ readonly limit?: number
107
+ readonly offset?: number
108
+ readonly totalCount?: boolean
109
+ }
110
+
111
+ /**
112
+ * Alias-only options (for scalar or has-one with alias)
113
+ */
114
+ export interface AliasOptions<TAlias extends string = string> {
115
+ readonly as?: TAlias
116
+ }
117
+
118
+ // ============================================================================
119
+ // Type Utilities
120
+ // ============================================================================
121
+
122
+ /**
123
+ * Extracts the item type from an array type
124
+ */
125
+ export type ArrayItemType<T> = T extends Array<infer U> ? U : never
126
+
127
+ /**
128
+ * Checks if a type is an array
129
+ */
130
+ export type IsArray<T> = T extends Array<any> ? true : false
131
+
132
+ /**
133
+ * Extracts non-nullable type
134
+ */
135
+ export type NonNullableType<T> = Exclude<T, null | undefined>