@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 @@
1
+ {"version":3,"file":"snapshots.d.ts","sourceRoot":"","sources":["../../src/store/snapshots.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ import type { ActionMiddleware } from '../core/ActionDispatcher.js';
2
+ import type { SnapshotStore } from '../store/SnapshotStore.js';
3
+ import type { UndoManagerConfig, UndoState } from './types.js';
4
+ type Subscriber = () => void;
5
+ /**
6
+ * UndoManager provides undo/redo functionality for the Bindx store.
7
+ *
8
+ * Features:
9
+ * - Automatic debounced grouping of rapid changes
10
+ * - Manual grouping via beginGroup/endGroup
11
+ * - Configurable history size
12
+ * - Block/unblock during persist operations
13
+ * - React integration via subscribe/getState
14
+ */
15
+ export declare class UndoManager {
16
+ private readonly store;
17
+ private undoStack;
18
+ private redoStack;
19
+ private pendingEntry;
20
+ private debounceTimer;
21
+ private isBlocked;
22
+ private manualGroupId;
23
+ private manualGroupLabel;
24
+ private subscribers;
25
+ private cachedState;
26
+ private readonly maxHistorySize;
27
+ private readonly debounceMs;
28
+ constructor(store: SnapshotStore, config?: UndoManagerConfig);
29
+ /**
30
+ * Creates middleware for ActionDispatcher.
31
+ * Must be added to dispatcher to enable undo tracking.
32
+ */
33
+ createMiddleware(): ActionMiddleware;
34
+ /**
35
+ * Handles an action, capturing state for undo if trackable.
36
+ */
37
+ private handleAction;
38
+ /**
39
+ * Handles action in manual group mode.
40
+ */
41
+ private handleManualGroupAction;
42
+ /**
43
+ * Handles action in debounced auto-group mode.
44
+ */
45
+ private handleDebouncedAction;
46
+ /**
47
+ * Gets keys that are in source but not in target.
48
+ */
49
+ private getNewKeys;
50
+ /**
51
+ * Checks if keys object is empty.
52
+ */
53
+ private isEmptyKeys;
54
+ /**
55
+ * Merges additional snapshot into target.
56
+ */
57
+ private mergeSnapshots;
58
+ /**
59
+ * Starts the debounce timer.
60
+ */
61
+ private startDebounceTimer;
62
+ /**
63
+ * Resets the debounce timer.
64
+ */
65
+ private resetDebounceTimer;
66
+ /**
67
+ * Flushes pending entry to undo stack.
68
+ */
69
+ private flushPendingEntry;
70
+ /**
71
+ * Trims history to max size.
72
+ */
73
+ private trimHistory;
74
+ /**
75
+ * Starts a manual group. All actions until endGroup are grouped as one undo entry.
76
+ * Returns group ID that must be passed to endGroup.
77
+ */
78
+ beginGroup(label?: string): string;
79
+ /**
80
+ * Ends a manual group and pushes to undo stack.
81
+ */
82
+ endGroup(groupId: string): void;
83
+ /**
84
+ * Undoes the last entry.
85
+ */
86
+ undo(): void;
87
+ /**
88
+ * Redoes the last undone entry.
89
+ */
90
+ redo(): void;
91
+ /**
92
+ * Blocks undo/redo operations.
93
+ * Use during persist to prevent inconsistent state.
94
+ */
95
+ block(): void;
96
+ /**
97
+ * Unblocks undo/redo operations.
98
+ */
99
+ unblock(): void;
100
+ /**
101
+ * Clears all undo/redo history.
102
+ */
103
+ clear(): void;
104
+ /**
105
+ * Subscribes to state changes.
106
+ * Returns unsubscribe function.
107
+ */
108
+ subscribe(callback: Subscriber): () => void;
109
+ /**
110
+ * Gets current state for React integration.
111
+ * Returns a cached object to satisfy useSyncExternalStore's referential equality requirement.
112
+ */
113
+ getState(): UndoState;
114
+ /**
115
+ * Notifies all subscribers of state change.
116
+ */
117
+ private notifySubscribers;
118
+ }
119
+ export {};
120
+ //# sourceMappingURL=UndoManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UndoManager.d.ts","sourceRoot":"","sources":["../../src/undo/UndoManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AASnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EAIX,iBAAiB,EACjB,SAAS,EACT,MAAM,YAAY,CAAA;AAEnB,KAAK,UAAU,GAAG,MAAM,IAAI,CAAA;AAS5B;;;;;;;;;GASG;AACH,qBAAa,WAAW;IAetB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAdvB,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,WAAW,CAAwB;IAC3C,OAAO,CAAC,WAAW,CAAyB;IAE5C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;gBAGjB,KAAK,EAAE,aAAa,EACrC,MAAM,GAAE,iBAAsB;IAM/B;;;OAGG;IACH,gBAAgB,IAAI,gBAAgB;IAQpC;;OAEG;IACH,OAAO,CAAC,YAAY;IAuBpB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAsB/B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAqC7B;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;IACH,OAAO,CAAC,cAAc;IAuBtB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAqBzB;;OAEG;IACH,OAAO,CAAC,WAAW;IAMnB;;;OAGG;IACH,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM;IAUlC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAW/B;;OAEG;IACH,IAAI,IAAI,IAAI;IA4BZ;;OAEG;IACH,IAAI,IAAI,IAAI;IAyBZ;;;OAGG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,OAAO,IAAI,IAAI;IAKf;;OAEG;IACH,KAAK,IAAI,IAAI;IAab;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,IAAI;IAO3C;;;OAGG;IACH,QAAQ,IAAI,SAAS;IAarB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAMzB"}
@@ -0,0 +1,3 @@
1
+ export { UndoManager } from './UndoManager.js';
2
+ export type { PartialStoreSnapshot, PendingUndoEntry, UndoEntry, UndoManagerConfig, UndoState, StoreAffectedKeys, } from './types.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/undo/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,YAAY,EACX,oBAAoB,EACpB,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,SAAS,EACT,iBAAiB,GACjB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,74 @@
1
+ import type { StoreAffectedKeys } from '../core/actionClassification.js';
2
+ import type { EntitySnapshot } from '../store/snapshots.js';
3
+ import type { StoredRelationState, StoredHasManyState, EntityMeta } from '../store/SnapshotStore.js';
4
+ /**
5
+ * Partial snapshot of store state.
6
+ * Contains only the data affected by an action or group of actions.
7
+ */
8
+ export interface PartialStoreSnapshot {
9
+ /** Entity snapshots (frozen, immutable) */
10
+ entitySnapshots: Map<string, EntitySnapshot>;
11
+ /** Has-one relation states */
12
+ relationStates: Map<string, StoredRelationState>;
13
+ /** Has-many list states */
14
+ hasManyStates: Map<string, StoredHasManyState>;
15
+ /** Entity metadata (existsOnServer, deletion flags) */
16
+ entityMetas: Map<string, EntityMeta>;
17
+ }
18
+ /**
19
+ * A single undo/redo entry.
20
+ * Contains the snapshot of state BEFORE the action(s) were applied.
21
+ */
22
+ export interface UndoEntry {
23
+ /** Unique identifier for this entry */
24
+ id: string;
25
+ /** Optional human-readable label */
26
+ label?: string;
27
+ /** Snapshot of state BEFORE the action(s) */
28
+ beforeSnapshot: PartialStoreSnapshot;
29
+ /** Keys that were affected (for redo snapshot capture) */
30
+ affectedKeys: StoreAffectedKeys;
31
+ /** When this entry was created */
32
+ timestamp: number;
33
+ }
34
+ /**
35
+ * Pending entry during debounce window.
36
+ * Accumulates affected keys while waiting for debounce to complete.
37
+ */
38
+ export interface PendingUndoEntry {
39
+ /** Snapshot captured before the first action in the group */
40
+ beforeSnapshot: PartialStoreSnapshot;
41
+ /** Accumulated affected keys */
42
+ affectedKeys: StoreAffectedKeys;
43
+ /** When the first action occurred */
44
+ timestamp: number;
45
+ /** Optional label for manual grouping */
46
+ label?: string;
47
+ }
48
+ /**
49
+ * Configuration options for UndoManager.
50
+ */
51
+ export interface UndoManagerConfig {
52
+ /** Maximum number of undo entries to keep (default: 100) */
53
+ maxHistorySize?: number;
54
+ /** Debounce time in milliseconds for auto-grouping (default: 300) */
55
+ debounceMs?: number;
56
+ }
57
+ /**
58
+ * Current state of the undo manager.
59
+ * Used for React integration via useSyncExternalStore.
60
+ */
61
+ export interface UndoState {
62
+ /** Whether undo is available */
63
+ canUndo: boolean;
64
+ /** Whether redo is available */
65
+ canRedo: boolean;
66
+ /** Whether undo/redo is currently blocked (e.g., during persist) */
67
+ isBlocked: boolean;
68
+ /** Number of entries in undo stack */
69
+ undoCount: number;
70
+ /** Number of entries in redo stack */
71
+ redoCount: number;
72
+ }
73
+ export type { StoreAffectedKeys } from '../core/actionClassification.js';
74
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/undo/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EACX,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EACV,MAAM,2BAA2B,CAAA;AAElC;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,2CAA2C;IAC3C,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IAC5C,8BAA8B;IAC9B,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;IAChD,2BAA2B;IAC3B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;IAC9C,uDAAuD;IACvD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACzB,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAA;IACV,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6CAA6C;IAC7C,cAAc,EAAE,oBAAoB,CAAA;IACpC,0DAA0D;IAC1D,YAAY,EAAE,iBAAiB,CAAA;IAC/B,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAA;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,6DAA6D;IAC7D,cAAc,EAAE,oBAAoB,CAAA;IACpC,gCAAgC;IAChC,YAAY,EAAE,iBAAiB,CAAA;IAC/B,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACzB,gCAAgC;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,gCAAgC;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,oEAAoE;IACpE,SAAS,EAAE,OAAO,CAAA;IAClB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAA;CACjB;AAGD,YAAY,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA"}
@@ -0,0 +1,25 @@
1
+ import type { HasManyParams } from '../selection/SelectionScope.js';
2
+ /**
3
+ * Generates an alias for a has-many relation based on field name and parameters.
4
+ *
5
+ * If no parameters are provided or all are undefined, returns the fieldName as-is.
6
+ * Otherwise, generates a deterministic alias by hashing the parameters.
7
+ *
8
+ * @param fieldName - The original field name
9
+ * @param params - Optional has-many parameters (filter, orderBy, limit, offset)
10
+ * @returns The alias to use for this has-many relation
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * // No params - returns fieldName
15
+ * generateHasManyAlias('tags') // => 'tags'
16
+ *
17
+ * // With filter - generates unique alias
18
+ * generateHasManyAlias('tags', { filter: { active: true } }) // => 'tags_a7x9k2'
19
+ *
20
+ * // Different params - different aliases
21
+ * generateHasManyAlias('tags', { filter: { active: false } }) // => 'tags_b3m1p8'
22
+ * ```
23
+ */
24
+ export declare function generateHasManyAlias(fieldName: string, params?: HasManyParams): string;
25
+ //# sourceMappingURL=aliasGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aliasGenerator.d.ts","sourceRoot":"","sources":["../../src/utils/aliasGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAEnE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CA+BtF"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Deep equality check for values.
3
+ * Handles objects, arrays, and primitives.
4
+ */
5
+ export declare function deepEqual(a: unknown, b: unknown): boolean;
6
+ //# sourceMappingURL=deepEqual.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepEqual.d.ts","sourceRoot":"","sources":["../../src/utils/deepEqual.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CA4BzD"}
@@ -0,0 +1,3 @@
1
+ export { deepEqual } from './deepEqual.js';
2
+ export { generateHasManyAlias } from './aliasGenerator.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@contember/bindx",
3
+ "version": "0.1.0",
4
+ "description": "Type-safe data binding framework (core)",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc --build",
12
+ "typecheck": "tsc --build"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src"
17
+ ],
18
+ "license": "MIT",
19
+ "dependencies": {
20
+ "@contember/client-content": "^2.1.0-alpha.30",
21
+ "@contember/graphql-builder": "^2.1.0-alpha.30",
22
+ "@contember/graphql-client": "^2.1.0-alpha.30"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/contember/bindx.git",
27
+ "directory": "packages/bindx"
28
+ }
29
+ }
@@ -0,0 +1,239 @@
1
+ import { GraphQlClient } from '@contember/graphql-client'
2
+ import {
3
+ ContentClient,
4
+ ContentQueryBuilder,
5
+ ContentEntitySelection,
6
+ SchemaNames,
7
+ ContentQuery,
8
+ type EntitySelectionManyArgs,
9
+ type EntitySelectionOneArgs,
10
+ type EntitySelectionColumnArgs,
11
+ type ContentClientInput,
12
+ } from '@contember/client-content'
13
+ import { type Input } from '@contember/schema'
14
+ import type { QuerySpec, QueryFieldSpec } from '../selection/buildQuery.js'
15
+ import type { BackendAdapter, Query, QueryResult, QueryOptions, GetQuery, ListQuery, PersistResult, CreateResult, DeleteResult } from './types.js'
16
+ import type { ContemberMutationResult } from '../errors/pathMapper.js'
17
+
18
+ /**
19
+ * Options for ContemberAdapter
20
+ */
21
+ export interface ContemberAdapterOptions {
22
+ /** GraphQL client instance */
23
+ client: GraphQlClient
24
+ /** Contember schema names for query building */
25
+ schema: SchemaNames
26
+ }
27
+
28
+ /**
29
+ * Backend adapter for Contember Content API.
30
+ * Uses @contember/client-content for type-safe GraphQL operations.
31
+ */
32
+ export class ContemberAdapter implements BackendAdapter {
33
+
34
+ constructor(
35
+ private readonly contentClient: ContentClient,
36
+ private readonly queryBuilder: ContentQueryBuilder,
37
+ ) {
38
+ }
39
+
40
+ async query(queries: readonly Query[], options?: QueryOptions): Promise<QueryResult[]> {
41
+ if (queries.length === 0) return []
42
+
43
+ // Build ContentQuery for each query
44
+ const contentQueries: Record<string, ContentQuery<unknown>> = {}
45
+
46
+ for (let i = 0; i < queries.length; i++) {
47
+ const q = queries[i]!
48
+ const key = `q${i}`
49
+
50
+ if (q.type === 'get') {
51
+ contentQueries[key] = this.buildGetQuery(q)
52
+ } else {
53
+ contentQueries[key] = this.buildListQuery(q)
54
+ }
55
+ }
56
+
57
+ // Execute all queries in single request
58
+ const results = await this.contentClient.query(contentQueries, {
59
+ signal: options?.signal,
60
+ })
61
+
62
+ // Map results back to QueryResult array
63
+ // Content client transforms already unwrap paginateRelation → flat arrays
64
+ return queries.map((q, i) => {
65
+ const key = `q${i}`
66
+ const data = (results as Record<string, unknown>)[key]
67
+
68
+ if (q.type === 'get') {
69
+ return { type: 'get' as const, data: data as Record<string, unknown> | null }
70
+ } else {
71
+ return { type: 'list' as const, data: (data ?? []) as readonly Record<string, unknown>[] }
72
+ }
73
+ })
74
+ }
75
+
76
+ private buildGetQuery(query: GetQuery): ContentQuery<unknown> {
77
+ const selection = this.buildEntitySelection(query.spec)
78
+ return this.queryBuilder.get(query.entityType, { by: query.by }, selection)
79
+ }
80
+
81
+ private buildListQuery(query: ListQuery): ContentQuery<unknown> {
82
+ const selection = this.buildEntitySelection(query.spec)
83
+ const args: ContentClientInput.AnyListQueryInput = {
84
+ filter: query.filter as Input.OptionalWhere | undefined,
85
+ orderBy: query.orderBy as ContentClientInput.AnyOrderBy | undefined,
86
+ limit: query.limit,
87
+ offset: query.offset,
88
+ }
89
+ return this.queryBuilder.list(query.entityType, args, selection)
90
+ }
91
+
92
+ async persist(
93
+ entityType: string,
94
+ id: string,
95
+ changes: Record<string, unknown>,
96
+ ): Promise<PersistResult> {
97
+ const mutation = this.queryBuilder.update(entityType, {
98
+ by: { id },
99
+ data: changes as Input.UpdateDataInput,
100
+ })
101
+
102
+ const result = await this.contentClient.mutate(mutation)
103
+
104
+ if (!result.ok) {
105
+ return {
106
+ ok: false,
107
+ errorMessage: result.errorMessage ?? `Failed to update ${entityType}:${id}`,
108
+ mutationResult: this.toMutationResult(result),
109
+ }
110
+ }
111
+
112
+ return { ok: true }
113
+ }
114
+
115
+ async create(
116
+ entityType: string,
117
+ data: Record<string, unknown>,
118
+ ): Promise<CreateResult> {
119
+ // Build selection to return created entity
120
+ const selection = (s: ContentEntitySelection) => s.$('id')
121
+
122
+ const mutation = this.queryBuilder.create(entityType, { data: data as Input.CreateDataInput }, selection)
123
+ const result = await this.contentClient.mutate(mutation)
124
+
125
+ if (!result.ok) {
126
+ return {
127
+ ok: false,
128
+ errorMessage: result.errorMessage ?? `Failed to create ${entityType}`,
129
+ mutationResult: this.toMutationResult(result),
130
+ }
131
+ }
132
+
133
+ return {
134
+ ok: true,
135
+ data: result.node as Record<string, unknown>,
136
+ }
137
+ }
138
+
139
+ async delete(entityType: string, id: string): Promise<DeleteResult> {
140
+ const mutation = this.queryBuilder.delete(entityType, { by: { id } })
141
+ const result = await this.contentClient.mutate(mutation)
142
+
143
+ if (!result.ok) {
144
+ return {
145
+ ok: false,
146
+ errorMessage: result.errorMessage ?? `Failed to delete ${entityType}:${id}`,
147
+ mutationResult: this.toMutationResult(result),
148
+ }
149
+ }
150
+
151
+ return { ok: true }
152
+ }
153
+
154
+ /**
155
+ * Converts Contember mutation result to our ContemberMutationResult type.
156
+ */
157
+ private toMutationResult(result: {
158
+ ok: boolean
159
+ errorMessage: string | null
160
+ errors: Array<{ paths: Array<Array<{ field: string } | { index: number; alias: string | null }>>; message: string; type: string }>
161
+ validation: { valid: boolean; errors: Array<{ path: Array<{ field: string } | { index: number; alias: string | null }>; message: { text: string } }> }
162
+ }): ContemberMutationResult {
163
+ return {
164
+ ok: result.ok,
165
+ errorMessage: result.errorMessage,
166
+ errors: result.errors.map(e => ({
167
+ paths: e.paths,
168
+ message: e.message,
169
+ type: e.type,
170
+ })),
171
+ validation: {
172
+ valid: result.validation.valid,
173
+ errors: result.validation.errors.map(e => ({
174
+ path: e.path,
175
+ message: { text: e.message.text },
176
+ })),
177
+ },
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Builds ContentEntitySelection from QuerySpec
183
+ */
184
+ private buildEntitySelection(
185
+ query: QuerySpec,
186
+ ): (selection: ContentEntitySelection) => ContentEntitySelection {
187
+ return (selection: ContentEntitySelection) => {
188
+ return this.applyFieldsToSelection(selection, query.fields)
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Recursively applies QueryFieldSpec[] to ContentEntitySelection
194
+ */
195
+ private applyFieldsToSelection(
196
+ selection: ContentEntitySelection,
197
+ fields: QueryFieldSpec[],
198
+ ): ContentEntitySelection {
199
+ let result = selection
200
+
201
+ for (const field of fields) {
202
+ const fieldName = field.sourcePath[0]
203
+ if (!fieldName) continue
204
+
205
+ if (field.nested) {
206
+ // Relation field (has-one or has-many)
207
+ const nestedCallback = (s: ContentEntitySelection) =>
208
+ this.applyFieldsToSelection(s, field.nested!.fields)
209
+
210
+ if (field.isArray) {
211
+ // has-many relation — always uses paginateRelation
212
+ const manyArgs: EntitySelectionManyArgs = {
213
+ ...(field.name !== fieldName ? { as: field.name } : undefined),
214
+ ...(field.filter !== undefined ? { filter: field.filter as Input.OptionalWhere } : undefined),
215
+ ...(field.orderBy !== undefined ? { orderBy: field.orderBy as ContentClientInput.AnyOrderBy } : undefined),
216
+ ...(field.limit !== undefined ? { limit: field.limit } : undefined),
217
+ ...(field.offset !== undefined ? { offset: field.offset } : undefined),
218
+ ...(field.totalCount ? { totalCount: true } : undefined),
219
+ }
220
+
221
+ result = result.$(fieldName, manyArgs, nestedCallback)
222
+ } else {
223
+ // has-one relation
224
+ const oneArgs: EntitySelectionOneArgs = {
225
+ ...(field.name !== fieldName ? { as: field.name } : undefined),
226
+ }
227
+ result = result.$(fieldName, oneArgs, nestedCallback)
228
+ }
229
+ } else {
230
+ // Scalar field
231
+ const columnArgs: EntitySelectionColumnArgs | undefined =
232
+ field.name !== fieldName ? { as: field.name } : undefined
233
+ result = result.$(fieldName, columnArgs)
234
+ }
235
+ }
236
+
237
+ return result
238
+ }
239
+ }