@contember/bindx-react 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 (194) hide show
  1. package/dist/batching/QueryBatcher.d.ts +31 -0
  2. package/dist/batching/QueryBatcher.d.ts.map +1 -0
  3. package/dist/batching/index.d.ts +2 -0
  4. package/dist/batching/index.d.ts.map +1 -0
  5. package/dist/components/EntityScope.d.ts +28 -0
  6. package/dist/components/EntityScope.d.ts.map +1 -0
  7. package/dist/dataview/DataGrid.d.ts +45 -0
  8. package/dist/dataview/DataGrid.d.ts.map +1 -0
  9. package/dist/dataview/DataGridPagination.d.ts +24 -0
  10. package/dist/dataview/DataGridPagination.d.ts.map +1 -0
  11. package/dist/dataview/DataGridToolbar.d.ts +16 -0
  12. package/dist/dataview/DataGridToolbar.d.ts.map +1 -0
  13. package/dist/dataview/DataViewContext.d.ts +32 -0
  14. package/dist/dataview/DataViewContext.d.ts.map +1 -0
  15. package/dist/dataview/columns.d.ts +115 -0
  16. package/dist/dataview/columns.d.ts.map +1 -0
  17. package/dist/dataview/contextHooks.d.ts +48 -0
  18. package/dist/dataview/contextHooks.d.ts.map +1 -0
  19. package/dist/dataview/dataAttribute.d.ts +6 -0
  20. package/dist/dataview/dataAttribute.d.ts.map +1 -0
  21. package/dist/dataview/export.d.ts +46 -0
  22. package/dist/dataview/export.d.ts.map +1 -0
  23. package/dist/dataview/filterComponents.d.ts +127 -0
  24. package/dist/dataview/filterComponents.d.ts.map +1 -0
  25. package/dist/dataview/filterContext.d.ts +10 -0
  26. package/dist/dataview/filterContext.d.ts.map +1 -0
  27. package/dist/dataview/filterHooks.d.ts +74 -0
  28. package/dist/dataview/filterHooks.d.ts.map +1 -0
  29. package/dist/dataview/filterScope.d.ts +54 -0
  30. package/dist/dataview/filterScope.d.ts.map +1 -0
  31. package/dist/dataview/filterWrappers.d.ts +65 -0
  32. package/dist/dataview/filterWrappers.d.ts.map +1 -0
  33. package/dist/dataview/index.d.ts +21 -0
  34. package/dist/dataview/index.d.ts.map +1 -0
  35. package/dist/dataview/infiniteLoad.d.ts +32 -0
  36. package/dist/dataview/infiniteLoad.d.ts.map +1 -0
  37. package/dist/dataview/loaderComponents.d.ts +80 -0
  38. package/dist/dataview/loaderComponents.d.ts.map +1 -0
  39. package/dist/dataview/pagingComponents.d.ts +50 -0
  40. package/dist/dataview/pagingComponents.d.ts.map +1 -0
  41. package/dist/dataview/renderCellValue.d.ts +9 -0
  42. package/dist/dataview/renderCellValue.d.ts.map +1 -0
  43. package/dist/dataview/selectionComponents.d.ts +62 -0
  44. package/dist/dataview/selectionComponents.d.ts.map +1 -0
  45. package/dist/dataview/sortingComponents.d.ts +39 -0
  46. package/dist/dataview/sortingComponents.d.ts.map +1 -0
  47. package/dist/dataview/stateStorage.d.ts +19 -0
  48. package/dist/dataview/stateStorage.d.ts.map +1 -0
  49. package/dist/dataview/useDataView.d.ts +58 -0
  50. package/dist/dataview/useDataView.d.ts.map +1 -0
  51. package/dist/dataview/useDataViewState.d.ts +89 -0
  52. package/dist/dataview/useDataViewState.d.ts.map +1 -0
  53. package/dist/hooks/BackendAdapterContext.d.ts +110 -0
  54. package/dist/hooks/BackendAdapterContext.d.ts.map +1 -0
  55. package/dist/hooks/ContemberBindxProvider.d.ts +67 -0
  56. package/dist/hooks/ContemberBindxProvider.d.ts.map +1 -0
  57. package/dist/hooks/createBindx.d.ts +39 -0
  58. package/dist/hooks/createBindx.d.ts.map +1 -0
  59. package/dist/hooks/index.d.ts +10 -0
  60. package/dist/hooks/index.d.ts.map +1 -0
  61. package/dist/hooks/useBindxEvents.d.ts +83 -0
  62. package/dist/hooks/useBindxEvents.d.ts.map +1 -0
  63. package/dist/hooks/useEntity.d.ts +123 -0
  64. package/dist/hooks/useEntity.d.ts.map +1 -0
  65. package/dist/hooks/useEntityBeforePersist.d.ts +22 -0
  66. package/dist/hooks/useEntityBeforePersist.d.ts.map +1 -0
  67. package/dist/hooks/useEntityCore.d.ts +52 -0
  68. package/dist/hooks/useEntityCore.d.ts.map +1 -0
  69. package/dist/hooks/useEntityImpl.d.ts +105 -0
  70. package/dist/hooks/useEntityImpl.d.ts.map +1 -0
  71. package/dist/hooks/useEntityList.d.ts +102 -0
  72. package/dist/hooks/useEntityList.d.ts.map +1 -0
  73. package/dist/hooks/useEntityListCore.d.ts +49 -0
  74. package/dist/hooks/useEntityListCore.d.ts.map +1 -0
  75. package/dist/hooks/useEntityListImpl.d.ts +70 -0
  76. package/dist/hooks/useEntityListImpl.d.ts.map +1 -0
  77. package/dist/hooks/useErrors.d.ts +72 -0
  78. package/dist/hooks/useErrors.d.ts.map +1 -0
  79. package/dist/hooks/usePersist.d.ts +115 -0
  80. package/dist/hooks/usePersist.d.ts.map +1 -0
  81. package/dist/hooks/useSelectionCollection.d.ts +41 -0
  82. package/dist/hooks/useSelectionCollection.d.ts.map +1 -0
  83. package/dist/hooks/useSelectionCollectionForList.d.ts +40 -0
  84. package/dist/hooks/useSelectionCollectionForList.d.ts.map +1 -0
  85. package/dist/hooks/useStableSelectionMeta.d.ts +11 -0
  86. package/dist/hooks/useStableSelectionMeta.d.ts.map +1 -0
  87. package/dist/hooks/useStoreSubscription.d.ts +19 -0
  88. package/dist/hooks/useStoreSubscription.d.ts.map +1 -0
  89. package/dist/hooks/useUndo.d.ts +45 -0
  90. package/dist/hooks/useUndo.d.ts.map +1 -0
  91. package/dist/index.d.ts +17 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/jsx/SelectionMeta.d.ts +2 -0
  94. package/dist/jsx/SelectionMeta.d.ts.map +1 -0
  95. package/dist/jsx/analyzer.d.ts +22 -0
  96. package/dist/jsx/analyzer.d.ts.map +1 -0
  97. package/dist/jsx/collectorProxy.d.ts +14 -0
  98. package/dist/jsx/collectorProxy.d.ts.map +1 -0
  99. package/dist/jsx/componentBuilder.d.ts +51 -0
  100. package/dist/jsx/componentBuilder.d.ts.map +1 -0
  101. package/dist/jsx/componentBuilder.types.d.ts +268 -0
  102. package/dist/jsx/componentBuilder.types.d.ts.map +1 -0
  103. package/dist/jsx/componentBuilderCompat.d.ts +17 -0
  104. package/dist/jsx/componentBuilderCompat.d.ts.map +1 -0
  105. package/dist/jsx/componentFactory.d.ts +39 -0
  106. package/dist/jsx/componentFactory.d.ts.map +1 -0
  107. package/dist/jsx/components/Entity.d.ts +82 -0
  108. package/dist/jsx/components/Entity.d.ts.map +1 -0
  109. package/dist/jsx/components/EntityList.d.ts +48 -0
  110. package/dist/jsx/components/EntityList.d.ts.map +1 -0
  111. package/dist/jsx/components/Field.d.ts +27 -0
  112. package/dist/jsx/components/Field.d.ts.map +1 -0
  113. package/dist/jsx/components/HasMany.d.ts +34 -0
  114. package/dist/jsx/components/HasMany.d.ts.map +1 -0
  115. package/dist/jsx/components/HasOne.d.ts +25 -0
  116. package/dist/jsx/components/HasOne.d.ts.map +1 -0
  117. package/dist/jsx/components/HasRole.d.ts +51 -0
  118. package/dist/jsx/components/HasRole.d.ts.map +1 -0
  119. package/dist/jsx/components/If.d.ts +51 -0
  120. package/dist/jsx/components/If.d.ts.map +1 -0
  121. package/dist/jsx/components/Show.d.ts +33 -0
  122. package/dist/jsx/components/Show.d.ts.map +1 -0
  123. package/dist/jsx/components/index.d.ts +8 -0
  124. package/dist/jsx/components/index.d.ts.map +1 -0
  125. package/dist/jsx/conditions.d.ts +310 -0
  126. package/dist/jsx/conditions.d.ts.map +1 -0
  127. package/dist/jsx/createComponent.d.ts +44 -0
  128. package/dist/jsx/createComponent.d.ts.map +1 -0
  129. package/dist/jsx/index.d.ts +19 -0
  130. package/dist/jsx/index.d.ts.map +1 -0
  131. package/dist/jsx/inlineProxy.d.ts +14 -0
  132. package/dist/jsx/inlineProxy.d.ts.map +1 -0
  133. package/dist/jsx/legacyTypes.d.ts +26 -0
  134. package/dist/jsx/legacyTypes.d.ts.map +1 -0
  135. package/dist/jsx/proxy.d.ts +2 -0
  136. package/dist/jsx/proxy.d.ts.map +1 -0
  137. package/dist/jsx/proxyShared.d.ts +14 -0
  138. package/dist/jsx/proxyShared.d.ts.map +1 -0
  139. package/dist/jsx/runtimeProxy.d.ts +11 -0
  140. package/dist/jsx/runtimeProxy.d.ts.map +1 -0
  141. package/dist/jsx/standaloneCreateComponent.d.ts +30 -0
  142. package/dist/jsx/standaloneCreateComponent.d.ts.map +1 -0
  143. package/dist/jsx/types.d.ts +109 -0
  144. package/dist/jsx/types.d.ts.map +1 -0
  145. package/dist/jsx/withCollector.d.ts +39 -0
  146. package/dist/jsx/withCollector.d.ts.map +1 -0
  147. package/dist/roles/RoleContext.d.ts +31 -0
  148. package/dist/roles/RoleContext.d.ts.map +1 -0
  149. package/dist/roles/index.d.ts +2 -0
  150. package/dist/roles/index.d.ts.map +1 -0
  151. package/package.json +34 -0
  152. package/src/batching/QueryBatcher.ts +114 -0
  153. package/src/batching/index.ts +1 -0
  154. package/src/components/EntityScope.tsx +43 -0
  155. package/src/hooks/BackendAdapterContext.tsx +214 -0
  156. package/src/hooks/ContemberBindxProvider.tsx +158 -0
  157. package/src/hooks/index.ts +61 -0
  158. package/src/hooks/useBindxEvents.ts +229 -0
  159. package/src/hooks/useEntity.ts +489 -0
  160. package/src/hooks/useEntityBeforePersist.ts +32 -0
  161. package/src/hooks/useEntityList.ts +444 -0
  162. package/src/hooks/useErrors.ts +150 -0
  163. package/src/hooks/usePersist.ts +377 -0
  164. package/src/hooks/useSelectionCollection.ts +122 -0
  165. package/src/hooks/useStoreSubscription.ts +51 -0
  166. package/src/hooks/useUndo.ts +83 -0
  167. package/src/index.ts +357 -0
  168. package/src/jsx/SelectionMeta.ts +2 -0
  169. package/src/jsx/analyzer.ts +261 -0
  170. package/src/jsx/collectorProxy.ts +282 -0
  171. package/src/jsx/componentBuilder.ts +180 -0
  172. package/src/jsx/componentBuilder.types.ts +463 -0
  173. package/src/jsx/componentBuilderCompat.ts +39 -0
  174. package/src/jsx/componentFactory.ts +400 -0
  175. package/src/jsx/components/Entity.tsx +383 -0
  176. package/src/jsx/components/EntityList.tsx +147 -0
  177. package/src/jsx/components/Field.tsx +79 -0
  178. package/src/jsx/components/HasMany.tsx +109 -0
  179. package/src/jsx/components/HasOne.tsx +78 -0
  180. package/src/jsx/components/HasRole.tsx +95 -0
  181. package/src/jsx/components/If.tsx +127 -0
  182. package/src/jsx/components/Show.tsx +56 -0
  183. package/src/jsx/components/index.ts +20 -0
  184. package/src/jsx/conditions.ts +472 -0
  185. package/src/jsx/createComponent.ts +175 -0
  186. package/src/jsx/index.ts +135 -0
  187. package/src/jsx/legacyTypes.ts +30 -0
  188. package/src/jsx/proxy.ts +1 -0
  189. package/src/jsx/proxyShared.ts +42 -0
  190. package/src/jsx/standaloneCreateComponent.ts +47 -0
  191. package/src/jsx/types.ts +192 -0
  192. package/src/jsx/withCollector.ts +45 -0
  193. package/src/roles/RoleContext.tsx +57 -0
  194. package/src/roles/index.ts +1 -0
package/src/index.ts ADDED
@@ -0,0 +1,357 @@
1
+ /**
2
+ * React Bindx - Type-safe React data binding framework
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+
7
+ // ============================================================================
8
+ // Re-export core types and functions from @contember/bindx
9
+ // ============================================================================
10
+
11
+ export type {
12
+ // Schema types
13
+ RelationType,
14
+ ScalarFieldDef,
15
+ HasOneRelationDef,
16
+ HasManyRelationDef,
17
+ FieldDef,
18
+ EntitySchemaDef,
19
+ SchemaDefinition,
20
+ InferModel,
21
+ InferEntityNames,
22
+ EntityDef,
23
+ InferEntityDef,
24
+ // Role types
25
+ UnionToIntersection,
26
+ RoleNames,
27
+ CommonEntity,
28
+ EntityForRoles,
29
+ ResolveEntity,
30
+ SingleRoleMap,
31
+ DefaultRole,
32
+ // Selection types
33
+ SelectionBuilder,
34
+ SelectionMeta,
35
+ SelectionFieldMeta,
36
+ FluentFragment,
37
+ FluentDefiner,
38
+ HasManyOptions,
39
+ InferSelection,
40
+ QuerySpec,
41
+ QueryFieldSpec,
42
+ // Query types (typed filter/orderBy)
43
+ EntityWhere,
44
+ EntityOrderBy,
45
+ ScalarCondition,
46
+ OrderDirection,
47
+ TypedHasManyOptions,
48
+ AliasOptions,
49
+ // Handle types
50
+ HasOneRelationState,
51
+ InputProps,
52
+ EntityFields,
53
+ SelectedEntityFields,
54
+ ScalarKeys,
55
+ HasManyKeys,
56
+ HasOneKeys,
57
+ FieldRefMeta,
58
+ FieldRef,
59
+ HasManyRef,
60
+ HasOneRef,
61
+ EntityRef,
62
+ // Type extraction helpers
63
+ ExtractHasOneEntityName,
64
+ ExtractHasManyEntityName,
65
+ ExtractRoleMap,
66
+ // Adapter types
67
+ BackendAdapter,
68
+ QueryOptions,
69
+ Query,
70
+ GetQuery,
71
+ ListQuery,
72
+ QueryResult,
73
+ GetQueryResult,
74
+ ListQueryResult,
75
+ MockDataStore,
76
+ MockAdapterOptions,
77
+ ContemberAdapterOptions,
78
+ // Core types
79
+ EntityLoadResult,
80
+ EntityListLoadResult,
81
+ LoadEntityOptions,
82
+ LoadEntityListOptions,
83
+ SelectionInput,
84
+ CoreFluentDefiner,
85
+ // Store types
86
+ EntitySnapshot,
87
+ FieldSnapshot,
88
+ HasOneRelationSnapshot,
89
+ HasManySnapshot,
90
+ EntityState,
91
+ LoadStatus,
92
+ // Undo types
93
+ UndoManagerConfig,
94
+ UndoState,
95
+ UndoEntry,
96
+ // Event types
97
+ BindxEvent,
98
+ FieldScopedEvent,
99
+ FieldChangingEvent,
100
+ FieldChangedEvent,
101
+ RelationConnectingEvent,
102
+ RelationConnectedEvent,
103
+ RelationDisconnectingEvent,
104
+ RelationDisconnectedEvent,
105
+ RelationDeletingEvent,
106
+ RelationDeletedEvent,
107
+ HasManyConnectingEvent,
108
+ HasManyConnectedEvent,
109
+ HasManyDisconnectingEvent,
110
+ HasManyDisconnectedEvent,
111
+ EntityPersistingEvent,
112
+ EntityPersistedEvent,
113
+ EntityPersistFailedEvent,
114
+ EntityResettingEvent,
115
+ EntityResetEvent,
116
+ EntityDeletingEvent,
117
+ EntityDeletedEvent,
118
+ ErrorAddedEvent,
119
+ ErrorsClearedEvent,
120
+ LoadStateChangedEvent,
121
+ BeforeEvent,
122
+ AfterEvent,
123
+ AnyBindxEvent,
124
+ EventTypeMap,
125
+ InterceptorResult,
126
+ Interceptor,
127
+ EventListener,
128
+ Unsubscribe,
129
+ BeforeEventTypes,
130
+ AfterEventTypes,
131
+ } from '@contember/bindx'
132
+
133
+ export {
134
+ // Schema utilities
135
+ scalar,
136
+ hasOne,
137
+ hasMany,
138
+ defineSchema,
139
+ entityDef,
140
+ roleEntityDef,
141
+ DEFAULT_ROLE,
142
+ SchemaRegistry,
143
+ // Selection utilities
144
+ createFragment,
145
+ buildQueryFromSelection,
146
+ // Handles
147
+ EntityHandle,
148
+ HasOneHandle,
149
+ HasManyListHandle,
150
+ FieldHandle,
151
+ // Handle symbols
152
+ FIELD_REF_META,
153
+ // Adapter
154
+ MockAdapter,
155
+ ContemberAdapter,
156
+ // Core
157
+ createEntityLoader,
158
+ resolveSelectionMeta,
159
+ buildQuery,
160
+ // Store
161
+ SnapshotStore,
162
+ ActionDispatcher,
163
+ // ID type detection
164
+ isTempId,
165
+ isPlaceholderId,
166
+ isPersistedId,
167
+ generatePlaceholderId,
168
+ // Persistence
169
+ BatchPersister,
170
+ ChangeRegistry,
171
+ // Undo
172
+ UndoManager,
173
+ // Events
174
+ EventEmitter,
175
+ // Internal
176
+ __internal,
177
+ } from '@contember/bindx'
178
+
179
+ // ============================================================================
180
+ // React-specific types
181
+ // ============================================================================
182
+
183
+ // Hook types
184
+ export type {
185
+ BindxProviderProps,
186
+ BindxContextValue,
187
+ UseEntityOptions,
188
+ UseEntityListOptions,
189
+ LoadingEntityAccessor,
190
+ ErrorEntityAccessor,
191
+ NotFoundEntityAccessor,
192
+ ReadyEntityAccessor,
193
+ EntityAccessorResult,
194
+ LoadingEntityListAccessor,
195
+ ErrorEntityListAccessor,
196
+ ReadyEntityListAccessor,
197
+ EntityListAccessorResult,
198
+ // Persistence hook types
199
+ PersistApi,
200
+ EntityPersistApi,
201
+ AnyRefWithMeta,
202
+ } from './hooks/index.js'
203
+
204
+ // Persistence types from @contember/bindx
205
+ export type {
206
+ DirtyEntity,
207
+ PersistScope,
208
+ AllScope,
209
+ EntityScope as EntityPersistScope,
210
+ FieldsScope,
211
+ RelationScope,
212
+ CustomScope,
213
+ EntityPersistResult,
214
+ PersistenceResult,
215
+ BatchPersistOptions,
216
+ } from '@contember/bindx'
217
+
218
+ // JSX types
219
+ export type {
220
+ EntityAccessor,
221
+ FieldRefBase,
222
+ FieldProps,
223
+ HasManyProps,
224
+ HasOneProps,
225
+ IfProps,
226
+ EntityComponentProps,
227
+ HasManyComponentOptions,
228
+ ShowProps,
229
+ SelectionPropMeta,
230
+ SelectionProvider,
231
+ EntityPropKeys,
232
+ EntityFromProp,
233
+ SelectionFromProp,
234
+ ImplicitFragmentProperties,
235
+ // Component builder types
236
+ BindxComponentBase,
237
+ BindxComponent,
238
+ ComponentBuilder,
239
+ ComponentBuilderState,
240
+ CreateComponentOptions,
241
+ // Interface types
242
+ InterfaceEntityPropConfig,
243
+ ImplicitInterfaceEntityConfig,
244
+ ExplicitInterfaceEntityConfig,
245
+ AddInterfaces,
246
+ InterfaceSelectorsMap,
247
+ AnyEntityPropConfig,
248
+ // Entity prop config types
249
+ EntityPropConfig,
250
+ ImplicitEntityConfig,
251
+ ExplicitEntityConfig,
252
+ // State helpers
253
+ AddImplicitEntity,
254
+ AddExplicitEntity,
255
+ AddImplicitInterfaceEntity,
256
+ AddExplicitInterfaceEntity,
257
+ SetScalarProps,
258
+ // Props building
259
+ BuildEntityProps,
260
+ BuildProps,
261
+ BuildFragmentProps,
262
+ InitialBuilderState,
263
+ } from './jsx/index.js'
264
+
265
+ // ============================================================================
266
+ // React-specific functions & components
267
+ // ============================================================================
268
+
269
+ // Hooks
270
+ export {
271
+ BindxProvider,
272
+ useBackendAdapter,
273
+ useSnapshotStore,
274
+ useDispatcher,
275
+ useBatchPersister,
276
+ useBindxContext,
277
+ useSchemaRegistry,
278
+ // Standalone hooks
279
+ useEntity,
280
+ useEntityList,
281
+ // Persistence
282
+ usePersist,
283
+ usePersistEntity,
284
+ // Undo
285
+ useUndo,
286
+ // Events
287
+ useOnEvent,
288
+ useOnEntityEvent,
289
+ useOnFieldEvent,
290
+ useIntercept,
291
+ useInterceptEntity,
292
+ useInterceptField,
293
+ // Contember
294
+ ContemberBindxProvider,
295
+ schemaNamesToDef,
296
+ } from './hooks/index.js'
297
+
298
+ // Role context
299
+ export { RoleProvider, useHasRole, type RoleProviderProps } from './roles/index.js'
300
+
301
+ // Undo hook types
302
+ export type { UndoHookResult } from './hooks/useUndo.js'
303
+
304
+
305
+ // JSX Components
306
+ export {
307
+ Field,
308
+ HasMany,
309
+ HasOne,
310
+ If,
311
+ Show,
312
+ Entity,
313
+ type EntityProps,
314
+ EntityList,
315
+ type EntityListProps,
316
+ HasRole,
317
+ type HasRoleProps,
318
+ BINDX_COMPONENT,
319
+ // Condition DSL for <If> component
320
+ cond,
321
+ type Condition,
322
+ // Component (unified API)
323
+ createComponent,
324
+ withCollector,
325
+ isBindxComponent,
326
+ mergeFragments,
327
+ COMPONENT_MARKER,
328
+ COMPONENT_BRAND,
329
+ COMPONENT_SELECTIONS,
330
+ createComponentBuilder,
331
+ getComponentBrand,
332
+ setBrandValidation,
333
+ validateBrand,
334
+ } from './jsx/index.js'
335
+
336
+ // Entity Scope
337
+ export {
338
+ EntityScope,
339
+ useEntityScope,
340
+ useOptionalEntityScope,
341
+ type EntityScopeProps,
342
+ } from './components/EntityScope.js'
343
+
344
+ // ============================================================================
345
+ // Internal React API
346
+ // ============================================================================
347
+
348
+ export {
349
+ SelectionMetaCollector,
350
+ mergeSelections,
351
+ createEmptySelection,
352
+ createCollectorProxy,
353
+ analyzeJsx,
354
+ collectSelection,
355
+ convertToQuerySelection,
356
+ debugSelection,
357
+ } from './jsx/index.js'
@@ -0,0 +1,2 @@
1
+ // Re-export from @contember/bindx
2
+ export { SelectionMetaCollector, mergeSelections, createEmptySelection } from '@contember/bindx'
@@ -0,0 +1,261 @@
1
+ import type { ReactNode, ReactElement } from 'react'
2
+ import { isValidElement } from 'react'
3
+ import type { SelectionMeta, SelectionFieldMeta, SelectionProvider } from './types.js'
4
+ import { FIELD_REF_META, SCOPE_REF } from './types.js'
5
+ import { SelectionMetaCollector, mergeSelections } from './SelectionMeta.js'
6
+ import { SelectionScope } from '@contember/bindx'
7
+ import { isBindxComponent, COMPONENT_SELECTIONS } from './createComponent.js'
8
+
9
+ /**
10
+ * Analyzes JSX tree to extract field selection metadata.
11
+ * This is called during the collection phase to determine which fields need to be fetched.
12
+ */
13
+ export function analyzeJsx(node: ReactNode, selection: SelectionMetaCollector): void {
14
+ // Handle null/undefined
15
+ if (node === null || node === undefined) {
16
+ return
17
+ }
18
+
19
+ // Handle primitives (string, number, boolean)
20
+ if (typeof node !== 'object') {
21
+ return
22
+ }
23
+
24
+ // Handle arrays
25
+ if (Array.isArray(node)) {
26
+ for (const child of node) {
27
+ analyzeJsx(child, selection)
28
+ }
29
+ return
30
+ }
31
+
32
+ // Must be a React element at this point
33
+ if (!isValidElement(node)) {
34
+ return
35
+ }
36
+
37
+ const element = node as ReactElement<{ children?: ReactNode }>
38
+
39
+ // Handle React Fragment and host elements (div, span, etc.) - just analyze children
40
+ if (typeof element.type === 'symbol' || typeof element.type === 'string') {
41
+ if (element.props.children) {
42
+ analyzeJsx(element.props.children, selection)
43
+ }
44
+ return
45
+ }
46
+
47
+ // Handle function components
48
+ const component = element.type as unknown
49
+
50
+ // Check if it's a bindx component with pre-computed selection - needs special handling
51
+ if (isBindxComponent(component)) {
52
+ // First, trigger implicit collection if the component has getSelection
53
+ // This ensures lazy implicit selections are collected before we access COMPONENT_SELECTIONS
54
+ const componentObj = component as Record<string, unknown>
55
+ if ('getSelection' in componentObj && typeof componentObj['getSelection'] === 'function') {
56
+ ;(component as SelectionProvider).getSelection(element.props as Record<string, unknown>, () => new SelectionMetaCollector())
57
+ }
58
+
59
+ handleBindxComponent(
60
+ component as { [COMPONENT_SELECTIONS]: Map<string, { selection: SelectionMeta }> },
61
+ element.props as Record<string, unknown>,
62
+ selection,
63
+ )
64
+ return
65
+ }
66
+
67
+ // Check if it's a component with getSelection (memo() wrapped or plain function components)
68
+ if (
69
+ component !== null &&
70
+ (typeof component === 'object' || typeof component === 'function') &&
71
+ 'getSelection' in component &&
72
+ typeof (component as SelectionProvider).getSelection === 'function'
73
+ ) {
74
+ const provider = component as SelectionProvider
75
+
76
+ // Collector function for nested selection analysis
77
+ const collectNested = (children: ReactNode): SelectionMeta => {
78
+ const nestedSelection = new SelectionMetaCollector()
79
+ analyzeJsx(children, nestedSelection)
80
+ return nestedSelection
81
+ }
82
+
83
+ // Get selection info from the component
84
+ const fieldSelection = provider.getSelection(element.props, collectNested)
85
+
86
+ // Add to selection
87
+ if (fieldSelection) {
88
+ if (Array.isArray(fieldSelection)) {
89
+ for (const field of fieldSelection) {
90
+ selection.addField(field)
91
+ }
92
+ } else {
93
+ selection.addField(fieldSelection)
94
+ }
95
+ }
96
+
97
+ return
98
+ }
99
+
100
+ // Check if component has staticRender — like Contember OSS's Component(render, staticRender) pattern.
101
+ // staticRender receives the same props (with collector proxies during analysis) and returns JSX to analyze.
102
+ if (
103
+ component !== null &&
104
+ (typeof component === 'object' || typeof component === 'function') &&
105
+ 'staticRender' in component &&
106
+ typeof (component as { staticRender: unknown }).staticRender === 'function'
107
+ ) {
108
+ const staticJsx = (component as { staticRender: (props: Record<string, unknown>) => ReactNode }).staticRender(
109
+ element.props as Record<string, unknown>,
110
+ )
111
+ if (staticJsx) {
112
+ analyzeJsx(staticJsx, selection)
113
+ }
114
+ return
115
+ }
116
+
117
+ // For non-bindx components, try to analyze children
118
+ // This handles wrapper components that pass children through
119
+ const children = element.props.children
120
+ if (children) {
121
+ analyzeJsx(children, selection)
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Collects all field selections from a JSX tree.
127
+ * Entry point for the collection phase.
128
+ */
129
+ export function collectSelection(jsx: ReactNode): SelectionMetaCollector {
130
+ const selection = new SelectionMetaCollector()
131
+ analyzeJsx(jsx, selection)
132
+ return selection
133
+ }
134
+
135
+ /**
136
+ * Converts JSX selection metadata to the format used by query builder
137
+ */
138
+ export function convertToQuerySelection(jsxSelection: SelectionMeta): Record<string, unknown> {
139
+ const result: Record<string, unknown> = {}
140
+
141
+ for (const [_, field] of jsxSelection.fields) {
142
+ if (field.path.length !== 1) {
143
+ // Skip nested paths - they're handled by parent relations
144
+ continue
145
+ }
146
+
147
+ if (field.isRelation && field.nested) {
148
+ // Relation with nested selection
149
+ const nestedQuery = convertToQuerySelection(field.nested)
150
+
151
+ if (field.isArray && field.hasManyParams) {
152
+ // HasMany with params
153
+ result[field.fieldName] = {
154
+ ...nestedQuery,
155
+ __params: field.hasManyParams,
156
+ }
157
+ } else {
158
+ result[field.fieldName] = nestedQuery
159
+ }
160
+ } else {
161
+ // Scalar field
162
+ result[field.fieldName] = true
163
+ }
164
+ }
165
+
166
+ return result
167
+ }
168
+
169
+ /**
170
+ * Handles bindx component in JSX analysis.
171
+ * Merges the component's pre-computed selection into the parent selection,
172
+ * adjusted to the correct path context based on the entity prop.
173
+ */
174
+ function handleBindxComponent(
175
+ component: { [COMPONENT_SELECTIONS]: Map<string, { selection: SelectionMeta }> },
176
+ props: Record<string, unknown>,
177
+ parentSelection: SelectionMetaCollector,
178
+ ): void {
179
+ const entityPropsMap = component[COMPONENT_SELECTIONS]
180
+ // For each entity prop in the fragment component
181
+ for (const [propName, meta] of entityPropsMap) {
182
+ const propValue = props[propName]
183
+
184
+ if (!propValue || typeof propValue !== 'object') {
185
+ continue
186
+ }
187
+
188
+ // Case 1: Prop has SCOPE_REF - direct reference to SelectionScope
189
+ // This is the case when passing relation.entity to a nested component
190
+ if (SCOPE_REF in propValue) {
191
+ const targetScope = (propValue as { [SCOPE_REF]: SelectionScope })[SCOPE_REF]
192
+ // Merge the nested component's selection directly into the relation's scope
193
+ targetScope.mergeFromSelectionMeta(meta.selection)
194
+ }
195
+ // Case 2: Prop is a FieldRef from a relation (e.g., article.fields.author)
196
+ // This has FIELD_REF_META with a path to adjust
197
+ else if (FIELD_REF_META in propValue) {
198
+ const refMeta = (propValue as { [FIELD_REF_META]: { path: string[]; fieldName: string } })[FIELD_REF_META]
199
+
200
+ // Add fields with adjusted paths to parent selection
201
+ for (const [_key, field] of meta.selection.fields) {
202
+ // Only process root-level fields from the fragment
203
+ if (field.path.length === 1) {
204
+ // Create a new field meta with the path adjusted to the parent context
205
+ const adjustedField: SelectionFieldMeta = {
206
+ ...field,
207
+ path: [...refMeta.path, ...field.path],
208
+ }
209
+
210
+ // If the field has nested selection, we need to preserve it
211
+ if (field.nested) {
212
+ adjustedField.nested = field.nested
213
+ }
214
+
215
+ parentSelection.addField(adjustedField)
216
+ }
217
+ }
218
+ }
219
+ // Case 3: Prop is an EntityRef from Entity children callback (root level)
220
+ // This doesn't have FIELD_REF_META, just merge the fields directly
221
+ else if ('id' in propValue && 'fields' in propValue) {
222
+ // This is an EntityRef - merge fragment's selection directly
223
+ for (const [_key, field] of meta.selection.fields) {
224
+ if (field.path.length === 1) {
225
+ parentSelection.addField({ ...field })
226
+ }
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Debug helper - prints selection tree
234
+ */
235
+ export function debugSelection(selection: SelectionMeta, indent = 0): string {
236
+ const lines: string[] = []
237
+ const prefix = ' '.repeat(indent)
238
+
239
+ for (const [key, field] of selection.fields) {
240
+ let line = `${prefix}${field.fieldName}`
241
+
242
+ if (field.isArray) {
243
+ line += '[]'
244
+ }
245
+ if (field.isRelation) {
246
+ line += ' (relation)'
247
+ }
248
+ if (field.hasManyParams) {
249
+ const params = JSON.stringify(field.hasManyParams)
250
+ line += ` ${params}`
251
+ }
252
+
253
+ lines.push(line)
254
+
255
+ if (field.nested) {
256
+ lines.push(debugSelection(field.nested, indent + 1))
257
+ }
258
+ }
259
+
260
+ return lines.join('\n')
261
+ }