@abloatai/ablo 0.35.0 → 0.36.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 (215) hide show
  1. package/CHANGELOG.md +713 -629
  2. package/README.md +56 -519
  3. package/bin/ablo.cjs +39 -0
  4. package/dist/BaseSyncedStore.d.ts +24 -4
  5. package/dist/BaseSyncedStore.js +53 -37
  6. package/dist/Database.d.ts +8 -20
  7. package/dist/Database.js +61 -59
  8. package/dist/InstanceCache.d.ts +6 -2
  9. package/dist/InstanceCache.js +18 -16
  10. package/dist/Model.d.ts +17 -7
  11. package/dist/Model.js +17 -7
  12. package/dist/ModelRegistry.d.ts +4 -0
  13. package/dist/ModelRegistry.js +17 -15
  14. package/dist/NetworkMonitor.d.ts +3 -1
  15. package/dist/NetworkMonitor.js +7 -5
  16. package/dist/SyncClient.d.ts +5 -15
  17. package/dist/SyncClient.js +60 -57
  18. package/dist/client/Ablo.js +30 -19
  19. package/dist/client/createInternalComponents.d.ts +4 -0
  20. package/dist/client/createInternalComponents.js +8 -2
  21. package/dist/client/createModelProxy.d.ts +21 -1
  22. package/dist/client/createModelProxy.js +123 -57
  23. package/dist/client/humans.d.ts +30 -9
  24. package/dist/client/humans.js +45 -19
  25. package/dist/client/reactiveEngine.d.ts +16 -11
  26. package/dist/client/reactiveEngine.js +113 -335
  27. package/dist/client/storeCluster.d.ts +47 -0
  28. package/dist/client/storeCluster.js +118 -0
  29. package/dist/client/storeLifecycle.d.ts +61 -0
  30. package/dist/client/storeLifecycle.js +231 -0
  31. package/dist/context.d.ts +13 -0
  32. package/dist/context.js +23 -0
  33. package/dist/core/index.d.ts +1 -1
  34. package/dist/docs/catalog.js +6 -3
  35. package/dist/index.d.ts +4 -2
  36. package/dist/index.js +4 -2
  37. package/dist/query/client.d.ts +3 -0
  38. package/dist/query/client.js +6 -5
  39. package/dist/react/AbloProvider.d.ts +13 -1
  40. package/dist/react/AbloProvider.js +5 -2
  41. package/dist/react/context.d.ts +2 -2
  42. package/dist/react/createAbloReact.d.ts +56 -0
  43. package/dist/react/createAbloReact.js +51 -0
  44. package/dist/react/index.d.ts +1 -0
  45. package/dist/react/index.js +3 -0
  46. package/dist/react/useAblo.d.ts +9 -2
  47. package/dist/react/useAblo.js +25 -7
  48. package/dist/schema/coordination.js +5 -1
  49. package/dist/schema/index.d.ts +1 -0
  50. package/dist/schema/index.js +4 -0
  51. package/dist/schema/select.js +3 -0
  52. package/dist/schema/serialize.js +3 -0
  53. package/dist/source/adapter.d.ts +7 -5
  54. package/dist/source/adapter.js +7 -5
  55. package/dist/source/index.d.ts +1 -1
  56. package/dist/source/index.js +1 -1
  57. package/dist/{core/storeContract.d.ts → storeContract.d.ts} +5 -5
  58. package/dist/{core → stores}/DatabaseManager.d.ts +3 -1
  59. package/dist/{core → stores}/DatabaseManager.js +13 -12
  60. package/dist/{core → stores}/StoreManager.d.ts +5 -3
  61. package/dist/{core → stores}/StoreManager.js +24 -22
  62. package/dist/stores/SyncActionStore.d.ts +3 -1
  63. package/dist/stores/SyncActionStore.js +9 -7
  64. package/dist/sync/BootstrapFetcher.d.ts +4 -0
  65. package/dist/sync/BootstrapFetcher.js +28 -26
  66. package/dist/sync/OnDemandLoader.d.ts +3 -0
  67. package/dist/sync/OnDemandLoader.js +1 -0
  68. package/dist/sync/bootstrapApply.d.ts +3 -0
  69. package/dist/sync/bootstrapApply.js +2 -2
  70. package/dist/sync/deltaPipeline.d.ts +13 -12
  71. package/dist/sync/deltaPipeline.js +21 -4
  72. package/dist/sync/groupChange.d.ts +3 -0
  73. package/dist/sync/groupChange.js +16 -14
  74. package/dist/sync/participants.d.ts +19 -2
  75. package/dist/sync/participants.js +3 -1
  76. package/dist/sync/schemas.d.ts +2 -1
  77. package/dist/sync/schemas.js +3 -3
  78. package/dist/syncLog/contract.d.ts +20 -0
  79. package/dist/syncLog/contract.js +19 -0
  80. package/dist/syncLog/index.d.ts +1 -0
  81. package/dist/syncLog/index.js +1 -0
  82. package/dist/transaction/auth/capability.d.ts +35 -0
  83. package/dist/transaction/auth/capability.js +25 -0
  84. package/dist/transaction/coordination/awaitClaimGrant.d.ts +7 -0
  85. package/dist/transaction/coordination/awaitClaimGrant.js +12 -0
  86. package/dist/transaction/coordination/claimHeartbeatLoop.d.ts +34 -0
  87. package/dist/transaction/coordination/claimHeartbeatLoop.js +20 -0
  88. package/dist/transaction/coordination/index.d.ts +4 -4
  89. package/dist/transaction/coordination/index.js +4 -3
  90. package/dist/transaction/coordination/locator.d.ts +23 -2
  91. package/dist/transaction/coordination/locator.js +22 -2
  92. package/dist/transaction/coordination/schema.d.ts +125 -62
  93. package/dist/transaction/coordination/schema.js +228 -64
  94. package/dist/transaction/coordination/targetConflict.js +32 -28
  95. package/dist/transaction/errorCodes.d.ts +2 -2
  96. package/dist/transaction/errorCodes.js +13 -9
  97. package/dist/transaction/plugin.d.ts +95 -2
  98. package/dist/transaction/plugin.js +21 -2
  99. package/dist/transaction/resources/httpResources.d.ts +57 -2
  100. package/dist/transaction/resources/modelOperations.d.ts +148 -40
  101. package/dist/transaction/resources/where.d.ts +16 -0
  102. package/dist/transaction/resources/where.js +45 -0
  103. package/dist/transaction/schema/field.d.ts +12 -18
  104. package/dist/transaction/schema/fieldRef.d.ts +38 -0
  105. package/dist/transaction/schema/fieldRef.js +11 -0
  106. package/dist/transaction/schema/openapi.d.ts +16 -15
  107. package/dist/transaction/schema/openapi.js +186 -25
  108. package/dist/transaction/schema/relation.d.ts +7 -2
  109. package/dist/transaction/schema/schema.d.ts +27 -0
  110. package/dist/transaction/schema/schema.js +20 -0
  111. package/dist/transaction/transactions/settlement/commitEnvelope.d.ts +1 -1
  112. package/dist/transaction/transactions/settlement/pendingWrite.d.ts +1 -1
  113. package/dist/transaction/transport/httpClient.d.ts +9 -1
  114. package/dist/transaction/transport/httpClient.js +1 -0
  115. package/dist/transaction/transport/httpTransport.js +156 -44
  116. package/dist/transaction/transport/wsTransport.d.ts +2 -4
  117. package/dist/transaction/transport/wsTransport.js +16 -10
  118. package/dist/transaction/types/streams.d.ts +10 -0
  119. package/dist/transaction/utils/duration.d.ts +25 -0
  120. package/dist/transaction/utils/duration.js +32 -0
  121. package/dist/transaction/wire/accountResponses.d.ts +69 -0
  122. package/dist/transaction/wire/accountResponses.js +36 -1
  123. package/dist/transaction/wire/auth.d.ts +9 -2
  124. package/dist/transaction/wire/auth.js +7 -1
  125. package/dist/transaction/wire/claims.d.ts +164 -97
  126. package/dist/transaction/wire/claims.js +126 -28
  127. package/dist/transaction/wire/commit.d.ts +1 -1
  128. package/dist/transaction/wire/feedEvent.d.ts +27 -0
  129. package/dist/transaction/wire/feedEvent.js +27 -1
  130. package/dist/transaction/wire/frames.d.ts +2 -2
  131. package/dist/transaction/wire/inboundFrames.d.ts +12 -2
  132. package/dist/transaction/wire/index.d.ts +10 -6
  133. package/dist/transaction/wire/index.js +12 -3
  134. package/dist/transaction/wire/modelMutations.d.ts +31 -0
  135. package/dist/transaction/wire/modelMutations.js +52 -0
  136. package/dist/transaction/wire/modelShape.d.ts +78 -0
  137. package/dist/transaction/wire/modelShape.js +74 -0
  138. package/dist/transactions/mutations/MutationQueue.d.ts +6 -0
  139. package/dist/transactions/mutations/MutationQueue.js +55 -45
  140. package/dist/transactions/mutations/commitPayload.d.ts +3 -2
  141. package/dist/transactions/mutations/commitPayload.js +5 -5
  142. package/dist/transactions/mutations/deltaConfirmation.d.ts +4 -0
  143. package/dist/transactions/mutations/deltaConfirmation.js +10 -8
  144. package/dist/transactions/mutations/replayValidation.d.ts +2 -1
  145. package/dist/transactions/mutations/replayValidation.js +3 -2
  146. package/dist/{core → views}/QueryView.d.ts +1 -1
  147. package/dist/{core → views}/QueryView.js +1 -1
  148. package/dist/{core → views}/ViewRegistry.d.ts +1 -1
  149. package/dist/{core/queryUtils.d.ts → views/incrementalView.d.ts} +6 -6
  150. package/dist/{core/queryUtils.js → views/incrementalView.js} +6 -6
  151. package/docs/agents.md +1 -1
  152. package/docs/api-keys.md +6 -6
  153. package/docs/api.md +5 -43
  154. package/docs/audit.md +4 -3
  155. package/docs/cli.md +11 -11
  156. package/docs/client-behavior.md +4 -4
  157. package/docs/concurrency-convention.md +28 -42
  158. package/docs/coordination.md +235 -83
  159. package/docs/data-sources.md +4 -4
  160. package/docs/debugging.md +34 -12
  161. package/docs/deployment.md +8 -8
  162. package/docs/examples/scoped-agent.md +3 -3
  163. package/docs/groups.md +57 -3
  164. package/docs/guarantees.md +37 -10
  165. package/docs/how-it-works.md +29 -5
  166. package/docs/idempotency.md +6 -6
  167. package/docs/identity.md +22 -23
  168. package/docs/index.md +8 -8
  169. package/docs/integration-guide.md +14 -3
  170. package/docs/mcp.md +7 -7
  171. package/docs/migration.md +34 -15
  172. package/docs/projects.md +1 -1
  173. package/docs/react.md +19 -8
  174. package/docs/sessions.md +1 -1
  175. package/docs/webhooks.md +9 -9
  176. package/llms.txt +4 -4
  177. package/package.json +13 -20
  178. package/dist/cli.cjs +0 -288600
  179. package/dist/testing/fixtures/bootstrap.d.ts +0 -49
  180. package/dist/testing/fixtures/bootstrap.js +0 -59
  181. package/dist/testing/fixtures/deltas.d.ts +0 -83
  182. package/dist/testing/fixtures/deltas.js +0 -136
  183. package/dist/testing/fixtures/httpResponses.d.ts +0 -70
  184. package/dist/testing/fixtures/httpResponses.js +0 -90
  185. package/dist/testing/fixtures/models.d.ts +0 -83
  186. package/dist/testing/fixtures/models.js +0 -272
  187. package/dist/testing/helpers/reactWrapper.d.ts +0 -69
  188. package/dist/testing/helpers/reactWrapper.js +0 -67
  189. package/dist/testing/helpers/syncEngineHarness.d.ts +0 -54
  190. package/dist/testing/helpers/syncEngineHarness.js +0 -73
  191. package/dist/testing/helpers/wait.d.ts +0 -30
  192. package/dist/testing/helpers/wait.js +0 -49
  193. package/dist/testing/index.d.ts +0 -23
  194. package/dist/testing/index.js +0 -33
  195. package/dist/testing/mocks/FakeDatabase.d.ts +0 -18
  196. package/dist/testing/mocks/FakeDatabase.js +0 -10
  197. package/dist/testing/mocks/MockMutationExecutor.d.ts +0 -87
  198. package/dist/testing/mocks/MockMutationExecutor.js +0 -186
  199. package/dist/testing/mocks/MockNetworkMonitor.d.ts +0 -20
  200. package/dist/testing/mocks/MockNetworkMonitor.js +0 -46
  201. package/dist/testing/mocks/MockSyncContext.d.ts +0 -51
  202. package/dist/testing/mocks/MockSyncContext.js +0 -72
  203. package/dist/testing/mocks/MockSyncStore.d.ts +0 -88
  204. package/dist/testing/mocks/MockSyncStore.js +0 -171
  205. package/dist/testing/mocks/MockWebSocket.d.ts +0 -71
  206. package/dist/testing/mocks/MockWebSocket.js +0 -118
  207. package/docs/interaction-model.md +0 -99
  208. /package/dist/{core → query}/QueryProcessor.d.ts +0 -0
  209. /package/dist/{core → query}/QueryProcessor.js +0 -0
  210. /package/dist/{core/storeContract.js → storeContract.js} +0 -0
  211. /package/dist/{core → stores}/openIDBWithTimeout.d.ts +0 -0
  212. /package/dist/{core → stores}/openIDBWithTimeout.js +0 -0
  213. /package/dist/{source → transaction}/footprint.d.ts +0 -0
  214. /package/dist/{source → transaction}/footprint.js +0 -0
  215. /package/dist/{core → views}/ViewRegistry.js +0 -0
@@ -1,272 +0,0 @@
1
- /**
2
- * A small set of {@link Model} subclasses used across the package's tests.
3
- * They form two foreign-key chains and carry the creation priorities the
4
- * transaction queue uses to order dependent writes:
5
- * TestProject (10) → TestTask (10, references Project) → TestComment (30, references Task)
6
- * TestSlideDeck (10) → TestSlide (15, references SlideDeck) → TestSlideLayer (20, references Slide)
7
- */
8
- import { Model } from '../../Model.js';
9
- import { ModelRegistry } from '../../ModelRegistry.js';
10
- import { PropertyType, LoadStrategy } from '../../transaction/types/index.js';
11
- // ─────────────────────────────────────────────
12
- // Test Model Classes
13
- // ─────────────────────────────────────────────
14
- export class TestProject extends Model {
15
- name = '';
16
- description = '';
17
- organizationId = 'test-org';
18
- constructor(data = {}) {
19
- super(data);
20
- if (data.name != null)
21
- this.name = data.name;
22
- if (data.description != null)
23
- this.description = data.description;
24
- if (data.organizationId != null)
25
- this.organizationId = data.organizationId;
26
- }
27
- getModelName() {
28
- return 'Project';
29
- }
30
- }
31
- export class TestTask extends Model {
32
- title = '';
33
- status = 'todo';
34
- projectId = null;
35
- assigneeId = null;
36
- organizationId = 'test-org';
37
- constructor(data = {}) {
38
- super(data);
39
- if (data.title != null)
40
- this.title = data.title;
41
- if (data.status != null)
42
- this.status = data.status;
43
- if (data.projectId !== undefined)
44
- this.projectId = data.projectId;
45
- if (data.assigneeId !== undefined)
46
- this.assigneeId = data.assigneeId;
47
- if (data.organizationId != null)
48
- this.organizationId = data.organizationId;
49
- }
50
- getModelName() {
51
- return 'Task';
52
- }
53
- }
54
- export class TestComment extends Model {
55
- body = '';
56
- taskId = null;
57
- organizationId = 'test-org';
58
- constructor(data = {}) {
59
- super(data);
60
- if (data.body != null)
61
- this.body = data.body;
62
- if (data.taskId !== undefined)
63
- this.taskId = data.taskId;
64
- if (data.organizationId != null)
65
- this.organizationId = data.organizationId;
66
- }
67
- getModelName() {
68
- return 'Comment';
69
- }
70
- }
71
- export class TestSlideDeck extends Model {
72
- title = '';
73
- organizationId = 'test-org';
74
- constructor(data = {}) {
75
- super(data);
76
- if (data.title != null)
77
- this.title = data.title;
78
- if (data.organizationId != null)
79
- this.organizationId = data.organizationId;
80
- }
81
- getModelName() {
82
- return 'SlideDeck';
83
- }
84
- }
85
- export class TestSlide extends Model {
86
- order = 0;
87
- deckId = null;
88
- organizationId = 'test-org';
89
- constructor(data = {}) {
90
- super(data);
91
- if (data.order != null)
92
- this.order = data.order;
93
- if (data.deckId !== undefined)
94
- this.deckId = data.deckId;
95
- if (data.organizationId != null)
96
- this.organizationId = data.organizationId;
97
- }
98
- getModelName() {
99
- return 'Slide';
100
- }
101
- }
102
- export class TestSlideLayer extends Model {
103
- slideId = null;
104
- zIndex = 0;
105
- type = 'text';
106
- content = '';
107
- organizationId = 'test-org';
108
- constructor(data = {}) {
109
- super(data);
110
- if (data.slideId !== undefined)
111
- this.slideId = data.slideId;
112
- if (data.zIndex != null)
113
- this.zIndex = data.zIndex;
114
- if (data.type != null)
115
- this.type = data.type;
116
- if (data.content != null)
117
- this.content = data.content;
118
- if (data.organizationId != null)
119
- this.organizationId = data.organizationId;
120
- }
121
- getModelName() {
122
- return 'SlideLayer';
123
- }
124
- }
125
- // ─────────────────────────────────────────────
126
- // Model Registration Helper
127
- // ─────────────────────────────────────────────
128
- /**
129
- * Maps each test model to its creation priority, matching the order the
130
- * transaction queue uses when writing dependent models.
131
- */
132
- export const TEST_MODEL_PRIORITIES = new Map([
133
- ['Project', 10],
134
- ['Task', 10],
135
- ['SlideDeck', 10],
136
- ['Slide', 15],
137
- ['SlideLayer', 20],
138
- ['Comment', 30],
139
- ]);
140
- /**
141
- * Registers every test model with a {@link ModelRegistry}, wiring up their
142
- * properties, references, and foreign-key relationships.
143
- */
144
- export function registerTestModels(registry) {
145
- registry.startBatch();
146
- // Register model classes
147
- registry.registerModel('Project', TestProject, { loadStrategy: LoadStrategy.instant });
148
- registry.registerModel('Task', TestTask, { loadStrategy: LoadStrategy.instant });
149
- registry.registerModel('Comment', TestComment, { loadStrategy: LoadStrategy.instant });
150
- registry.registerModel('SlideDeck', TestSlideDeck, { loadStrategy: LoadStrategy.instant });
151
- registry.registerModel('Slide', TestSlide, { loadStrategy: LoadStrategy.instant });
152
- registry.registerModel('SlideLayer', TestSlideLayer, { loadStrategy: LoadStrategy.instant });
153
- // Register properties
154
- registry.registerProperty('Project', 'name', { type: PropertyType.property });
155
- registry.registerProperty('Project', 'description', { type: PropertyType.property, optional: true });
156
- registry.registerProperty('Project', 'organizationId', { type: PropertyType.property });
157
- registry.registerProperty('Task', 'title', { type: PropertyType.property });
158
- registry.registerProperty('Task', 'status', { type: PropertyType.property });
159
- registry.registerProperty('Task', 'projectId', { type: PropertyType.reference, nullable: true });
160
- registry.registerProperty('Task', 'assigneeId', { type: PropertyType.reference, nullable: true });
161
- registry.registerProperty('Task', 'organizationId', { type: PropertyType.property });
162
- registry.registerProperty('Comment', 'body', { type: PropertyType.property });
163
- registry.registerProperty('Comment', 'taskId', { type: PropertyType.reference, nullable: true });
164
- registry.registerProperty('Comment', 'organizationId', { type: PropertyType.property });
165
- registry.registerProperty('SlideDeck', 'title', { type: PropertyType.property });
166
- registry.registerProperty('SlideDeck', 'organizationId', { type: PropertyType.property });
167
- registry.registerProperty('Slide', 'order', { type: PropertyType.property });
168
- registry.registerProperty('Slide', 'deckId', { type: PropertyType.reference, nullable: true });
169
- registry.registerProperty('Slide', 'organizationId', { type: PropertyType.property });
170
- registry.registerProperty('SlideLayer', 'slideId', { type: PropertyType.reference, nullable: true });
171
- registry.registerProperty('SlideLayer', 'zIndex', { type: PropertyType.property });
172
- registry.registerProperty('SlideLayer', 'type', { type: PropertyType.property });
173
- registry.registerProperty('SlideLayer', 'content', { type: PropertyType.property });
174
- registry.registerProperty('SlideLayer', 'organizationId', { type: PropertyType.property });
175
- // Register back-references for cascade-aware transaction handling
176
- registry.registerBackReference('Task', { parentModel: 'Project', foreignKey: 'projectId', cascadeDelete: true });
177
- registry.registerBackReference('Comment', { parentModel: 'Task', foreignKey: 'taskId', cascadeDelete: true });
178
- registry.registerBackReference('Slide', { parentModel: 'SlideDeck', foreignKey: 'deckId', cascadeDelete: true });
179
- registry.registerBackReference('SlideLayer', { parentModel: 'Slide', foreignKey: 'slideId', cascadeDelete: true });
180
- registry.endBatch();
181
- }
182
- // ─────────────────────────────────────────────
183
- // Test SyncEngineConfig factory
184
- // ─────────────────────────────────────────────
185
- /**
186
- * Builds a sync engine configuration pre-loaded with the test models'
187
- * creation priorities and related settings.
188
- */
189
- export function createTestConfig() {
190
- return {
191
- modelCreatePriority: TEST_MODEL_PRIORITIES,
192
- defaultCreatePriority: 40,
193
- defaultNonCreatePriority: 50,
194
- essentialFields: {
195
- Task: ['title', 'projectId'],
196
- Slide: ['deckId', 'order'],
197
- },
198
- classNameFallbackMap: {
199
- TestProject: 'Project',
200
- TestTask: 'Task',
201
- TestComment: 'Comment',
202
- TestSlideDeck: 'SlideDeck',
203
- TestSlide: 'Slide',
204
- TestSlideLayer: 'SlideLayer',
205
- },
206
- };
207
- }
208
- // ─────────────────────────────────────────────
209
- // Fixture factories
210
- // ─────────────────────────────────────────────
211
- let fixtureCounter = 0;
212
- /** Reset the counter (call in beforeEach for deterministic IDs) */
213
- export function resetFixtureCounter() {
214
- fixtureCounter = 0;
215
- }
216
- export function createProjectFixture(overrides = {}) {
217
- fixtureCounter++;
218
- return new TestProject({
219
- id: `project-${fixtureCounter}`,
220
- name: `Test Project ${fixtureCounter}`,
221
- organizationId: 'test-org',
222
- ...overrides,
223
- });
224
- }
225
- export function createTaskFixture(overrides = {}) {
226
- fixtureCounter++;
227
- return new TestTask({
228
- id: `task-${fixtureCounter}`,
229
- title: `Test Task ${fixtureCounter}`,
230
- status: 'todo',
231
- organizationId: 'test-org',
232
- ...overrides,
233
- });
234
- }
235
- export function createCommentFixture(overrides = {}) {
236
- fixtureCounter++;
237
- return new TestComment({
238
- id: `comment-${fixtureCounter}`,
239
- body: `Test comment ${fixtureCounter}`,
240
- organizationId: 'test-org',
241
- ...overrides,
242
- });
243
- }
244
- export function createSlideDeckFixture(overrides = {}) {
245
- fixtureCounter++;
246
- return new TestSlideDeck({
247
- id: `deck-${fixtureCounter}`,
248
- title: `Test Deck ${fixtureCounter}`,
249
- organizationId: 'test-org',
250
- ...overrides,
251
- });
252
- }
253
- export function createSlideFixture(overrides = {}) {
254
- fixtureCounter++;
255
- return new TestSlide({
256
- id: `slide-${fixtureCounter}`,
257
- order: fixtureCounter,
258
- organizationId: 'test-org',
259
- ...overrides,
260
- });
261
- }
262
- export function createSlideLayerFixture(overrides = {}) {
263
- fixtureCounter++;
264
- return new TestSlideLayer({
265
- id: `layer-${fixtureCounter}`,
266
- zIndex: fixtureCounter,
267
- type: 'text',
268
- content: `Layer content ${fixtureCounter}`,
269
- organizationId: 'test-org',
270
- ...overrides,
271
- });
272
- }
@@ -1,69 +0,0 @@
1
- /**
2
- * React testing helpers for this package. They wire the package's
3
- * `SyncProvider` into `@testing-library/react` so you can test components
4
- * and hooks built on `useModel`, `useModels`, and `useMutations` against a
5
- * mock store, with no live server.
6
- */
7
- import * as React from 'react';
8
- import { type SyncStoreContract } from '../../react/context.js';
9
- import { MockSyncStore, createMockSyncStore } from '../mocks/MockSyncStore.js';
10
- export interface TestWrapperOptions {
11
- /** Mock sync store. If omitted, a new MockSyncStore is created. */
12
- store?: SyncStoreContract;
13
- /** Organization ID. Default: "test-org-id". */
14
- organizationId?: string;
15
- }
16
- /**
17
- * Builds a wrapper component for `@testing-library/react`'s `renderHook`
18
- * and `render`. It wraps the children in the package's `SyncProvider`,
19
- * backed by a mock store, so the hooks and components under test can read
20
- * from it. Pass your own store to seed specific data, or let one be created.
21
- *
22
- * @example
23
- * import { renderHook } from '@testing-library/react';
24
- * import { createReactTestWrapper, createMockSyncStore } from '@abloatai/ablo/testing';
25
- *
26
- * const mockStore = createMockSyncStore();
27
- * mockStore.setModels(Task, [task1, task2]);
28
- *
29
- * const { result } = renderHook(
30
- * () => useModels(Task),
31
- * { wrapper: createReactTestWrapper({ store: mockStore }) }
32
- * );
33
- */
34
- export declare function createReactTestWrapper(options?: TestWrapperOptions): React.FC<{
35
- children: React.ReactNode;
36
- }>;
37
- /**
38
- * A drop-in replacement for `@testing-library/react`'s `renderHook` that
39
- * wraps the hook in the package's `SyncProvider` and a mock store for you,
40
- * so you don't build the wrapper by hand.
41
- *
42
- * `@testing-library/react` is loaded lazily, so projects that don't use
43
- * these helpers never have to install it.
44
- *
45
- * @example
46
- * import { renderSyncHook, createMockSyncStore } from '@abloatai/ablo/testing';
47
- *
48
- * const mockStore = createMockSyncStore();
49
- * mockStore.addModel(Task, myTask);
50
- *
51
- * const { result } = renderSyncHook(
52
- * () => useModel(Task, myTask.id),
53
- * { store: mockStore }
54
- * );
55
- * expect(result.current?.id).toBe(myTask.id);
56
- */
57
- export declare function renderSyncHook<TProps, TResult>(callback: (props: TProps) => TResult, options?: TestWrapperOptions & {
58
- initialProps?: TProps;
59
- }): {
60
- result: {
61
- current: TResult;
62
- };
63
- rerender: (props?: TProps) => void;
64
- unmount: () => void;
65
- };
66
- /**
67
- * Re-export MockSyncStore for convenience.
68
- */
69
- export { MockSyncStore, createMockSyncStore };
@@ -1,67 +0,0 @@
1
- /**
2
- * React testing helpers for this package. They wire the package's
3
- * `SyncProvider` into `@testing-library/react` so you can test components
4
- * and hooks built on `useModel`, `useModels`, and `useMutations` against a
5
- * mock store, with no live server.
6
- */
7
- import * as React from 'react';
8
- import { SyncProvider } from '../../react/context.js';
9
- import { MockSyncStore, createMockSyncStore } from '../mocks/MockSyncStore.js';
10
- /**
11
- * Builds a wrapper component for `@testing-library/react`'s `renderHook`
12
- * and `render`. It wraps the children in the package's `SyncProvider`,
13
- * backed by a mock store, so the hooks and components under test can read
14
- * from it. Pass your own store to seed specific data, or let one be created.
15
- *
16
- * @example
17
- * import { renderHook } from '@testing-library/react';
18
- * import { createReactTestWrapper, createMockSyncStore } from '@abloatai/ablo/testing';
19
- *
20
- * const mockStore = createMockSyncStore();
21
- * mockStore.setModels(Task, [task1, task2]);
22
- *
23
- * const { result } = renderHook(
24
- * () => useModels(Task),
25
- * { wrapper: createReactTestWrapper({ store: mockStore }) }
26
- * );
27
- */
28
- export function createReactTestWrapper(options = {}) {
29
- const store = options.store ?? createMockSyncStore();
30
- const organizationId = options.organizationId ?? 'test-org-id';
31
- const Wrapper = ({ children }) => React.createElement(SyncProvider, { store, organizationId }, children);
32
- return Wrapper;
33
- }
34
- /**
35
- * A drop-in replacement for `@testing-library/react`'s `renderHook` that
36
- * wraps the hook in the package's `SyncProvider` and a mock store for you,
37
- * so you don't build the wrapper by hand.
38
- *
39
- * `@testing-library/react` is loaded lazily, so projects that don't use
40
- * these helpers never have to install it.
41
- *
42
- * @example
43
- * import { renderSyncHook, createMockSyncStore } from '@abloatai/ablo/testing';
44
- *
45
- * const mockStore = createMockSyncStore();
46
- * mockStore.addModel(Task, myTask);
47
- *
48
- * const { result } = renderSyncHook(
49
- * () => useModel(Task, myTask.id),
50
- * { store: mockStore }
51
- * );
52
- * expect(result.current?.id).toBe(myTask.id);
53
- */
54
- export function renderSyncHook(callback, options = {}) {
55
- // Load @testing-library/react lazily so projects that never call these
56
- // helpers don't have to install it.
57
- // eslint-disable-next-line @typescript-eslint/no-require-imports
58
- const rtl = require('@testing-library/react');
59
- return rtl.renderHook(callback, {
60
- wrapper: createReactTestWrapper(options),
61
- initialProps: options.initialProps,
62
- });
63
- }
64
- /**
65
- * Re-export MockSyncStore for convenience.
66
- */
67
- export { MockSyncStore, createMockSyncStore };
@@ -1,54 +0,0 @@
1
- /**
2
- * An integration-test harness that assembles a full sync engine stack —
3
- * the model registry, object pool, transaction queue, and related parts —
4
- * from the real implementations, but with mocked input and output. It lets
5
- * tests exercise the engine end to end without a network or a live server.
6
- */
7
- import { ModelRegistry } from '../../ModelRegistry.js';
8
- import { InstanceCache } from '../../InstanceCache.js';
9
- import { MockMutationExecutor } from '../mocks/MockMutationExecutor.js';
10
- import { MockNetworkMonitor } from '../mocks/MockNetworkMonitor.js';
11
- import { MockWebSocket } from '../mocks/MockWebSocket.js';
12
- import type { TestContextResult } from '../mocks/MockSyncContext.js';
13
- export interface TestHarness {
14
- /** A model registry pre-loaded with the test models. */
15
- registry: ModelRegistry;
16
- /** A real object pool with the test foreign-key indexes configured. */
17
- pool: InstanceCache;
18
- /** A mock WebSocket for injecting deltas into the engine. */
19
- webSocket: MockWebSocket;
20
- /** The dependency-injection context holding every mock. */
21
- context: TestContextResult;
22
- /** Shorthand for the mock mutation executor on {@link context}. */
23
- mutationExecutor: MockMutationExecutor;
24
- /** Shorthand for the mock network monitor on {@link context}. */
25
- networkMonitor: MockNetworkMonitor;
26
- /** Tears down the harness and resets its counters. */
27
- cleanup: () => void;
28
- }
29
- export interface TestHarnessOptions {
30
- /** Start offline (default: false) */
31
- startOffline?: boolean;
32
- /** Initial sync ID for mutation executor */
33
- initialSyncId?: number;
34
- /** InstanceCache config overrides */
35
- poolConfig?: {
36
- maxSize?: number;
37
- maxAge?: number;
38
- gcInterval?: number;
39
- useWeakRefs?: boolean;
40
- };
41
- }
42
- /**
43
- * Builds a full test harness: real sync engine components wired to mocked
44
- * input and output. Reset the counters and tear everything down through the
45
- * returned {@link TestHarness.cleanup}.
46
- *
47
- * Usage:
48
- * ```ts
49
- * let harness: TestHarness;
50
- * beforeEach(() => { harness = createTestHarness(); });
51
- * afterEach(() => { harness.cleanup(); });
52
- * ```
53
- */
54
- export declare function createTestHarness(options?: TestHarnessOptions): TestHarness;
@@ -1,73 +0,0 @@
1
- /**
2
- * An integration-test harness that assembles a full sync engine stack —
3
- * the model registry, object pool, transaction queue, and related parts —
4
- * from the real implementations, but with mocked input and output. It lets
5
- * tests exercise the engine end to end without a network or a live server.
6
- */
7
- import { ModelRegistry, setActiveRegistry } from '../../ModelRegistry.js';
8
- import { InstanceCache } from '../../InstanceCache.js';
9
- import { MockMutationExecutor } from '../mocks/MockMutationExecutor.js';
10
- import { MockNetworkMonitor } from '../mocks/MockNetworkMonitor.js';
11
- import { MockWebSocket } from '../mocks/MockWebSocket.js';
12
- import { createTestContext } from '../mocks/MockSyncContext.js';
13
- import { registerTestModels, createTestConfig, resetFixtureCounter, } from '../fixtures/models.js';
14
- import { resetDeltaCounter } from '../fixtures/deltas.js';
15
- /**
16
- * Builds a full test harness: real sync engine components wired to mocked
17
- * input and output. Reset the counters and tear everything down through the
18
- * returned {@link TestHarness.cleanup}.
19
- *
20
- * Usage:
21
- * ```ts
22
- * let harness: TestHarness;
23
- * beforeEach(() => { harness = createTestHarness(); });
24
- * afterEach(() => { harness.cleanup(); });
25
- * ```
26
- */
27
- export function createTestHarness(options = {}) {
28
- // Reset counters for deterministic tests
29
- resetFixtureCounter();
30
- resetDeltaCounter();
31
- // Create and register test models
32
- const registry = new ModelRegistry();
33
- setActiveRegistry(registry);
34
- registerTestModels(registry);
35
- // Create the dependency-injection context with the test config
36
- const testConfig = createTestConfig();
37
- const context = createTestContext({
38
- config: testConfig,
39
- startOffline: options.startOffline,
40
- mutationExecutorOptions: {
41
- initialSyncId: options.initialSyncId ?? 1,
42
- },
43
- });
44
- // Create the real object pool with foreign-key indexes
45
- const pool = new InstanceCache({
46
- maxSize: options.poolConfig?.maxSize ?? 10000,
47
- maxAge: options.poolConfig?.maxAge ?? 5 * 60 * 1000,
48
- gcInterval: options.poolConfig?.gcInterval ?? 0, // Disable auto-GC in tests
49
- useWeakRefs: options.poolConfig?.useWeakRefs ?? false, // Disable WeakRefs for predictable tests
50
- }, registry);
51
- // Register foreign-key indexes for the test models
52
- pool.registerForeignKey('Task', 'projectId');
53
- pool.registerForeignKey('Comment', 'taskId');
54
- pool.registerForeignKey('Slide', 'deckId');
55
- pool.registerForeignKey('SlideLayer', 'slideId');
56
- // Create mock WebSocket
57
- const webSocket = new MockWebSocket();
58
- return {
59
- registry,
60
- pool,
61
- webSocket,
62
- context,
63
- mutationExecutor: context.mocks.mutationExecutor,
64
- networkMonitor: context.mocks.networkMonitor,
65
- cleanup: () => {
66
- pool.clear();
67
- webSocket.reset();
68
- context.cleanup();
69
- resetFixtureCounter();
70
- resetDeltaCounter();
71
- },
72
- };
73
- }
@@ -1,30 +0,0 @@
1
- /**
2
- * Asynchronous helpers for tests whose assertions depend on timing — for
3
- * flushing pending work, polling for a condition, or waiting a fixed delay.
4
- */
5
- /**
6
- * Flushes all pending microtasks, such as resolved promises and
7
- * `queueMicrotask` callbacks. Useful for testing work the transaction queue
8
- * batches on the microtask queue.
9
- */
10
- export declare function flushMicrotasks(): Promise<void>;
11
- /**
12
- * Polls `condition` until it returns true, checking every `interval`
13
- * milliseconds. Rejects with an {@link AbloConnectionError} if `maxWait`
14
- * milliseconds pass first.
15
- */
16
- export declare function waitFor(condition: () => boolean, options?: {
17
- maxWait?: number;
18
- interval?: number;
19
- }): Promise<void>;
20
- /**
21
- * Waits for the given number of milliseconds. Use sparingly; prefer
22
- * {@link flushMicrotasks} or {@link waitFor}, which don't tie tests to
23
- * wall-clock time.
24
- */
25
- export declare function delay(ms: number): Promise<void>;
26
- /**
27
- * Flushes pending microtasks, then runs `fn` and returns its result. Handy
28
- * for asserting state after the transaction queue processes a batch.
29
- */
30
- export declare function afterMicrotasks<T>(fn: () => T): Promise<T>;
@@ -1,49 +0,0 @@
1
- /**
2
- * Asynchronous helpers for tests whose assertions depend on timing — for
3
- * flushing pending work, polling for a condition, or waiting a fixed delay.
4
- */
5
- import { AbloConnectionError } from '../../transaction/errors.js';
6
- /**
7
- * Flushes all pending microtasks, such as resolved promises and
8
- * `queueMicrotask` callbacks. Useful for testing work the transaction queue
9
- * batches on the microtask queue.
10
- */
11
- export function flushMicrotasks() {
12
- return new Promise((resolve) => {
13
- // Use setTimeout(0) to yield to the microtask queue
14
- setTimeout(resolve, 0);
15
- });
16
- }
17
- /**
18
- * Polls `condition` until it returns true, checking every `interval`
19
- * milliseconds. Rejects with an {@link AbloConnectionError} if `maxWait`
20
- * milliseconds pass first.
21
- */
22
- export async function waitFor(condition, options = {}) {
23
- const { maxWait = 5000, interval = 10 } = options;
24
- const start = Date.now();
25
- while (!condition()) {
26
- if (Date.now() - start > maxWait) {
27
- throw new AbloConnectionError(`waitFor timed out after ${maxWait}ms`, {
28
- code: 'wait_for_timeout',
29
- });
30
- }
31
- await new Promise((resolve) => setTimeout(resolve, interval));
32
- }
33
- }
34
- /**
35
- * Waits for the given number of milliseconds. Use sparingly; prefer
36
- * {@link flushMicrotasks} or {@link waitFor}, which don't tie tests to
37
- * wall-clock time.
38
- */
39
- export function delay(ms) {
40
- return new Promise((resolve) => setTimeout(resolve, ms));
41
- }
42
- /**
43
- * Flushes pending microtasks, then runs `fn` and returns its result. Handy
44
- * for asserting state after the transaction queue processes a batch.
45
- */
46
- export async function afterMicrotasks(fn) {
47
- await flushMicrotasks();
48
- return fn();
49
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * @abloatai/ablo/testing — Public test utilities for SDK consumers.
3
- *
4
- * Provides mock implementations, fixture factories, and test harnesses
5
- * for writing integration tests against the sync engine.
6
- */
7
- export { MockMutationExecutor } from './mocks/MockMutationExecutor.js';
8
- export type { CapturedMutation, MockMutationExecutorOptions } from './mocks/MockMutationExecutor.js';
9
- export { MockNetworkMonitor } from './mocks/MockNetworkMonitor.js';
10
- export { MockWebSocket } from './mocks/MockWebSocket.js';
11
- export type { MockDelta, MockBootstrapHint } from './mocks/MockWebSocket.js';
12
- export { fakeDatabase } from './mocks/FakeDatabase.js';
13
- export type { FakeDatabaseOverrides } from './mocks/FakeDatabase.js';
14
- export { createTestContext, } from './mocks/MockSyncContext.js';
15
- export type { TestContextOptions, TestContextResult } from './mocks/MockSyncContext.js';
16
- export { TestProject, TestTask, TestComment, TestSlideDeck, TestSlide, TestSlideLayer, TEST_MODEL_PRIORITIES, registerTestModels, createTestConfig, resetFixtureCounter, createProjectFixture, createTaskFixture, createCommentFixture, createSlideDeckFixture, createSlideFixture, createSlideLayerFixture, } from './fixtures/models.js';
17
- export { createDelta, createInsertDelta, createUpdateDelta, createDeleteDelta, createArchiveDelta, createUnarchiveDelta, createCoveringDelta, createGroupAddedDelta, createLegacyGroupChangeDelta, createGroupRemovedDelta, createDeltaBatch, createConfirmationDelta, resetDeltaCounter, } from './fixtures/deltas.js';
18
- export { createFullBootstrapResponse, createPartialBootstrapResponse, createTestBootstrapResponse, } from './fixtures/bootstrap.js';
19
- export type { BootstrapModelData, BootstrapResponse } from './fixtures/bootstrap.js';
20
- export { createTestHarness, } from './helpers/syncEngineHarness.js';
21
- export type { TestHarness, TestHarnessOptions } from './helpers/syncEngineHarness.js';
22
- export { flushMicrotasks, waitFor, delay, afterMicrotasks, } from './helpers/wait.js';
23
- export { createReactTestWrapper, renderSyncHook, MockSyncStore, createMockSyncStore, type TestWrapperOptions, } from './helpers/reactWrapper.js';
@@ -1,33 +0,0 @@
1
- /**
2
- * @abloatai/ablo/testing — Public test utilities for SDK consumers.
3
- *
4
- * Provides mock implementations, fixture factories, and test harnesses
5
- * for writing integration tests against the sync engine.
6
- */
7
- // ─────────────────────────────────────────────
8
- // Mocks
9
- // ─────────────────────────────────────────────
10
- export { MockMutationExecutor } from './mocks/MockMutationExecutor.js';
11
- export { MockNetworkMonitor } from './mocks/MockNetworkMonitor.js';
12
- export { MockWebSocket } from './mocks/MockWebSocket.js';
13
- export { fakeDatabase } from './mocks/FakeDatabase.js';
14
- export { createTestContext, } from './mocks/MockSyncContext.js';
15
- // ─────────────────────────────────────────────
16
- // Fixtures: Models
17
- // ─────────────────────────────────────────────
18
- export { TestProject, TestTask, TestComment, TestSlideDeck, TestSlide, TestSlideLayer, TEST_MODEL_PRIORITIES, registerTestModels, createTestConfig, resetFixtureCounter, createProjectFixture, createTaskFixture, createCommentFixture, createSlideDeckFixture, createSlideFixture, createSlideLayerFixture, } from './fixtures/models.js';
19
- // ─────────────────────────────────────────────
20
- // Fixtures: Deltas
21
- // ─────────────────────────────────────────────
22
- export { createDelta, createInsertDelta, createUpdateDelta, createDeleteDelta, createArchiveDelta, createUnarchiveDelta, createCoveringDelta, createGroupAddedDelta, createLegacyGroupChangeDelta, createGroupRemovedDelta, createDeltaBatch, createConfirmationDelta, resetDeltaCounter, } from './fixtures/deltas.js';
23
- // ─────────────────────────────────────────────
24
- // Fixtures: Bootstrap
25
- // ─────────────────────────────────────────────
26
- export { createFullBootstrapResponse, createPartialBootstrapResponse, createTestBootstrapResponse, } from './fixtures/bootstrap.js';
27
- // ─────────────────────────────────────────────
28
- // Helpers
29
- // ─────────────────────────────────────────────
30
- export { createTestHarness, } from './helpers/syncEngineHarness.js';
31
- export { flushMicrotasks, waitFor, delay, afterMicrotasks, } from './helpers/wait.js';
32
- // React testing helpers
33
- export { createReactTestWrapper, renderSyncHook, MockSyncStore, createMockSyncStore, } from './helpers/reactWrapper.js';