@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,430 @@
1
+ /**
2
+ * Factory functions for creating events from actions.
3
+ */
4
+
5
+ import type { Action } from '../core/actions.js'
6
+ import type { SnapshotStore } from '../store/SnapshotStore.js'
7
+ import type {
8
+ BeforeEvent,
9
+ AfterEvent,
10
+ FieldChangingEvent,
11
+ FieldChangedEvent,
12
+ RelationConnectingEvent,
13
+ RelationConnectedEvent,
14
+ RelationDisconnectingEvent,
15
+ RelationDisconnectedEvent,
16
+ RelationDeletingEvent,
17
+ RelationDeletedEvent,
18
+ HasManyConnectingEvent,
19
+ HasManyConnectedEvent,
20
+ HasManyDisconnectingEvent,
21
+ HasManyDisconnectedEvent,
22
+ EntityResettingEvent,
23
+ EntityResetEvent,
24
+ ErrorAddedEvent,
25
+ ErrorsClearedEvent,
26
+ LoadStateChangedEvent,
27
+ } from './types.js'
28
+
29
+ /**
30
+ * State captured before action execution.
31
+ * Used to create after events with old values.
32
+ */
33
+ export interface CapturedState {
34
+ oldValue?: unknown
35
+ previousId?: string | null
36
+ currentId?: string | null
37
+ previousStatus?: string
38
+ }
39
+
40
+ /**
41
+ * Creates a before event from an action.
42
+ * Returns null for actions that don't have before events.
43
+ */
44
+ export function createBeforeEvent(
45
+ action: Action,
46
+ store: SnapshotStore,
47
+ ): BeforeEvent | null {
48
+ const timestamp = Date.now()
49
+
50
+ switch (action.type) {
51
+ case 'SET_FIELD': {
52
+ const snapshot = store.getEntitySnapshot(action.entityType, action.entityId)
53
+ const fieldName = action.fieldPath[action.fieldPath.length - 1] ?? action.fieldPath[0] ?? ''
54
+ const oldValue = getNestedValue(snapshot?.data, action.fieldPath)
55
+
56
+ return {
57
+ type: 'field:changing',
58
+ timestamp,
59
+ entityType: action.entityType,
60
+ entityId: action.entityId,
61
+ fieldName,
62
+ fieldPath: action.fieldPath,
63
+ oldValue,
64
+ newValue: action.value,
65
+ } satisfies FieldChangingEvent
66
+ }
67
+
68
+ case 'CONNECT_RELATION': {
69
+ const relation = store.getRelation(
70
+ action.entityType,
71
+ action.entityId,
72
+ action.fieldName,
73
+ )
74
+ return {
75
+ type: 'relation:connecting',
76
+ timestamp,
77
+ entityType: action.entityType,
78
+ entityId: action.entityId,
79
+ fieldName: action.fieldName,
80
+ targetId: action.targetId,
81
+ previousId: relation?.currentId ?? null,
82
+ } satisfies RelationConnectingEvent
83
+ }
84
+
85
+ case 'DISCONNECT_RELATION': {
86
+ const relation = store.getRelation(
87
+ action.entityType,
88
+ action.entityId,
89
+ action.fieldName,
90
+ )
91
+ return {
92
+ type: 'relation:disconnecting',
93
+ timestamp,
94
+ entityType: action.entityType,
95
+ entityId: action.entityId,
96
+ fieldName: action.fieldName,
97
+ currentId: relation?.currentId ?? null,
98
+ } satisfies RelationDisconnectingEvent
99
+ }
100
+
101
+ case 'DELETE_RELATION': {
102
+ const relation = store.getRelation(
103
+ action.entityType,
104
+ action.entityId,
105
+ action.fieldName,
106
+ )
107
+ return {
108
+ type: 'relation:deleting',
109
+ timestamp,
110
+ entityType: action.entityType,
111
+ entityId: action.entityId,
112
+ fieldName: action.fieldName,
113
+ currentId: relation?.currentId ?? null,
114
+ } satisfies RelationDeletingEvent
115
+ }
116
+
117
+ case 'RESET_ENTITY': {
118
+ return {
119
+ type: 'entity:resetting',
120
+ timestamp,
121
+ entityType: action.entityType,
122
+ entityId: action.entityId,
123
+ } satisfies EntityResettingEvent
124
+ }
125
+
126
+ // HasMany events - these would need to be created from the store's planned operations
127
+ // For now, we handle ADD_TO_LIST and REMOVE_FROM_LIST actions
128
+ // Note: The current actions don't have a direct "connect to hasMany" action,
129
+ // but the store has planHasManyConnection/planHasManyRemoval methods
130
+
131
+ default:
132
+ return null
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Captures relevant state before action execution.
138
+ * Used to create after events with accurate old values.
139
+ */
140
+ export function captureStateBeforeAction(
141
+ action: Action,
142
+ store: SnapshotStore,
143
+ ): CapturedState {
144
+ switch (action.type) {
145
+ case 'SET_FIELD': {
146
+ const snapshot = store.getEntitySnapshot(action.entityType, action.entityId)
147
+ return {
148
+ oldValue: getNestedValue(snapshot?.data, action.fieldPath),
149
+ }
150
+ }
151
+
152
+ case 'CONNECT_RELATION':
153
+ case 'DISCONNECT_RELATION':
154
+ case 'DELETE_RELATION': {
155
+ const relation = store.getRelation(
156
+ action.entityType,
157
+ action.entityId,
158
+ action.fieldName,
159
+ )
160
+ return {
161
+ previousId: relation?.currentId ?? null,
162
+ }
163
+ }
164
+
165
+ case 'SET_LOAD_STATE': {
166
+ const loadState = store.getLoadState(action.entityType, action.entityId)
167
+ return {
168
+ previousStatus: loadState?.status,
169
+ }
170
+ }
171
+
172
+ default:
173
+ return {}
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Creates an after event from an action and captured state.
179
+ * Returns null for actions that don't have after events.
180
+ */
181
+ export function createAfterEvent(
182
+ action: Action,
183
+ stateBefore: CapturedState,
184
+ store: SnapshotStore,
185
+ ): AfterEvent | null {
186
+ const timestamp = Date.now()
187
+
188
+ switch (action.type) {
189
+ case 'SET_FIELD': {
190
+ const fieldName = action.fieldPath[action.fieldPath.length - 1] ?? action.fieldPath[0] ?? ''
191
+ return {
192
+ type: 'field:changed',
193
+ timestamp,
194
+ entityType: action.entityType,
195
+ entityId: action.entityId,
196
+ fieldName,
197
+ fieldPath: action.fieldPath,
198
+ oldValue: stateBefore.oldValue,
199
+ newValue: action.value,
200
+ } satisfies FieldChangedEvent
201
+ }
202
+
203
+ case 'CONNECT_RELATION': {
204
+ return {
205
+ type: 'relation:connected',
206
+ timestamp,
207
+ entityType: action.entityType,
208
+ entityId: action.entityId,
209
+ fieldName: action.fieldName,
210
+ targetId: action.targetId,
211
+ previousId: stateBefore.previousId ?? null,
212
+ } satisfies RelationConnectedEvent
213
+ }
214
+
215
+ case 'DISCONNECT_RELATION': {
216
+ return {
217
+ type: 'relation:disconnected',
218
+ timestamp,
219
+ entityType: action.entityType,
220
+ entityId: action.entityId,
221
+ fieldName: action.fieldName,
222
+ previousId: stateBefore.previousId ?? null,
223
+ } satisfies RelationDisconnectedEvent
224
+ }
225
+
226
+ case 'DELETE_RELATION': {
227
+ return {
228
+ type: 'relation:deleted',
229
+ timestamp,
230
+ entityType: action.entityType,
231
+ entityId: action.entityId,
232
+ fieldName: action.fieldName,
233
+ previousId: stateBefore.previousId ?? null,
234
+ } satisfies RelationDeletedEvent
235
+ }
236
+
237
+ case 'RESET_ENTITY': {
238
+ return {
239
+ type: 'entity:reset',
240
+ timestamp,
241
+ entityType: action.entityType,
242
+ entityId: action.entityId,
243
+ } satisfies EntityResetEvent
244
+ }
245
+
246
+ case 'ADD_FIELD_ERROR': {
247
+ return {
248
+ type: 'error:added',
249
+ timestamp,
250
+ entityType: action.entityType,
251
+ entityId: action.entityId,
252
+ target: 'field',
253
+ targetName: action.fieldName,
254
+ error: action.error,
255
+ } satisfies ErrorAddedEvent
256
+ }
257
+
258
+ case 'ADD_ENTITY_ERROR': {
259
+ return {
260
+ type: 'error:added',
261
+ timestamp,
262
+ entityType: action.entityType,
263
+ entityId: action.entityId,
264
+ target: 'entity',
265
+ targetName: null,
266
+ error: action.error,
267
+ } satisfies ErrorAddedEvent
268
+ }
269
+
270
+ case 'ADD_RELATION_ERROR': {
271
+ return {
272
+ type: 'error:added',
273
+ timestamp,
274
+ entityType: action.entityType,
275
+ entityId: action.entityId,
276
+ target: 'relation',
277
+ targetName: action.relationName,
278
+ error: action.error,
279
+ } satisfies ErrorAddedEvent
280
+ }
281
+
282
+ case 'CLEAR_FIELD_ERRORS': {
283
+ return {
284
+ type: 'errors:cleared',
285
+ timestamp,
286
+ entityType: action.entityType,
287
+ entityId: action.entityId,
288
+ target: 'field',
289
+ targetName: action.fieldName,
290
+ } satisfies ErrorsClearedEvent
291
+ }
292
+
293
+ case 'CLEAR_ENTITY_ERRORS': {
294
+ return {
295
+ type: 'errors:cleared',
296
+ timestamp,
297
+ entityType: action.entityType,
298
+ entityId: action.entityId,
299
+ target: 'entity',
300
+ targetName: null,
301
+ } satisfies ErrorsClearedEvent
302
+ }
303
+
304
+ case 'CLEAR_RELATION_ERRORS': {
305
+ return {
306
+ type: 'errors:cleared',
307
+ timestamp,
308
+ entityType: action.entityType,
309
+ entityId: action.entityId,
310
+ target: 'relation',
311
+ targetName: action.relationName,
312
+ } satisfies ErrorsClearedEvent
313
+ }
314
+
315
+ case 'CLEAR_ALL_ERRORS':
316
+ case 'CLEAR_ALL_SERVER_ERRORS': {
317
+ return {
318
+ type: 'errors:cleared',
319
+ timestamp,
320
+ entityType: action.entityType,
321
+ entityId: action.entityId,
322
+ target: 'all',
323
+ targetName: null,
324
+ } satisfies ErrorsClearedEvent
325
+ }
326
+
327
+ case 'SET_LOAD_STATE': {
328
+ return {
329
+ type: 'load:stateChanged',
330
+ timestamp,
331
+ entityType: action.entityType,
332
+ entityId: action.entityId,
333
+ previousStatus: stateBefore.previousStatus,
334
+ newStatus: action.status,
335
+ error: action.error,
336
+ } satisfies LoadStateChangedEvent
337
+ }
338
+
339
+ default:
340
+ return null
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Creates hasMany connection events.
346
+ * These are separate since hasMany operations may not go through standard actions.
347
+ */
348
+ export function createHasManyConnectingEvent(
349
+ entityType: string,
350
+ entityId: string,
351
+ fieldName: string,
352
+ itemId: string,
353
+ ): HasManyConnectingEvent {
354
+ return {
355
+ type: 'hasMany:connecting',
356
+ timestamp: Date.now(),
357
+ entityType,
358
+ entityId,
359
+ fieldName,
360
+ itemId,
361
+ }
362
+ }
363
+
364
+ export function createHasManyConnectedEvent(
365
+ entityType: string,
366
+ entityId: string,
367
+ fieldName: string,
368
+ itemId: string,
369
+ ): HasManyConnectedEvent {
370
+ return {
371
+ type: 'hasMany:connected',
372
+ timestamp: Date.now(),
373
+ entityType,
374
+ entityId,
375
+ fieldName,
376
+ itemId,
377
+ }
378
+ }
379
+
380
+ export function createHasManyDisconnectingEvent(
381
+ entityType: string,
382
+ entityId: string,
383
+ fieldName: string,
384
+ itemId: string,
385
+ ): HasManyDisconnectingEvent {
386
+ return {
387
+ type: 'hasMany:disconnecting',
388
+ timestamp: Date.now(),
389
+ entityType,
390
+ entityId,
391
+ fieldName,
392
+ itemId,
393
+ }
394
+ }
395
+
396
+ export function createHasManyDisconnectedEvent(
397
+ entityType: string,
398
+ entityId: string,
399
+ fieldName: string,
400
+ itemId: string,
401
+ ): HasManyDisconnectedEvent {
402
+ return {
403
+ type: 'hasMany:disconnected',
404
+ timestamp: Date.now(),
405
+ entityType,
406
+ entityId,
407
+ fieldName,
408
+ itemId,
409
+ }
410
+ }
411
+
412
+ // ============================================================================
413
+ // Helper Functions
414
+ // ============================================================================
415
+
416
+ /**
417
+ * Gets a nested value from an object by path.
418
+ */
419
+ function getNestedValue(obj: unknown, path: readonly string[]): unknown {
420
+ if (obj === null || obj === undefined) return undefined
421
+ if (path.length === 0) return obj
422
+
423
+ let current: unknown = obj
424
+ for (const key of path) {
425
+ if (current === null || current === undefined) return undefined
426
+ if (typeof current !== 'object') return undefined
427
+ current = (current as Record<string, unknown>)[key]
428
+ }
429
+ return current
430
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Event system for bindx.
3
+ *
4
+ * Provides type-safe event subscriptions and interceptors for:
5
+ * - Field value changes
6
+ * - Relation changes (hasOne, hasMany)
7
+ * - Entity lifecycle (persist, reset, delete)
8
+ * - Errors
9
+ * - Load state changes
10
+ */
11
+
12
+ export { EventEmitter } from './EventEmitter.js'
13
+
14
+ export {
15
+ createBeforeEvent,
16
+ createAfterEvent,
17
+ captureStateBeforeAction,
18
+ createHasManyConnectingEvent,
19
+ createHasManyConnectedEvent,
20
+ createHasManyDisconnectingEvent,
21
+ createHasManyDisconnectedEvent,
22
+ } from './eventFactory.js'
23
+
24
+ export type {
25
+ // Base types
26
+ BindxEvent,
27
+ FieldScopedEvent,
28
+
29
+ // Field events
30
+ FieldChangingEvent,
31
+ FieldChangedEvent,
32
+
33
+ // HasOne relation events
34
+ RelationConnectingEvent,
35
+ RelationConnectedEvent,
36
+ RelationDisconnectingEvent,
37
+ RelationDisconnectedEvent,
38
+ RelationDeletingEvent,
39
+ RelationDeletedEvent,
40
+
41
+ // HasMany relation events
42
+ HasManyConnectingEvent,
43
+ HasManyConnectedEvent,
44
+ HasManyDisconnectingEvent,
45
+ HasManyDisconnectedEvent,
46
+
47
+ // Entity lifecycle events
48
+ EntityPersistingEvent,
49
+ EntityPersistedEvent,
50
+ EntityPersistFailedEvent,
51
+ EntityResettingEvent,
52
+ EntityResetEvent,
53
+ EntityDeletingEvent,
54
+ EntityDeletedEvent,
55
+
56
+ // Error events
57
+ ErrorAddedEvent,
58
+ ErrorsClearedEvent,
59
+
60
+ // Load state events
61
+ LoadStateChangedEvent,
62
+
63
+ // Union types
64
+ BeforeEvent,
65
+ AfterEvent,
66
+ AnyBindxEvent,
67
+
68
+ // Event type strings
69
+ BeforeEventType,
70
+ AfterEventType,
71
+ AnyEventType,
72
+
73
+ // Type map
74
+ EventTypeMap,
75
+ EventByType,
76
+
77
+ // Interceptor types
78
+ InterceptorResult,
79
+ Interceptor,
80
+ EventListener,
81
+ Unsubscribe,
82
+
83
+ // Helper types
84
+ BeforeEventTypes,
85
+ AfterEventTypes,
86
+ FieldScopedEventTypes,
87
+ } from './types.js'
88
+
89
+ export type { CapturedState } from './eventFactory.js'