@abloatai/ablo 0.26.0 → 0.27.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 (398) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +101 -85
  3. package/dist/BaseSyncedStore.d.ts +85 -88
  4. package/dist/BaseSyncedStore.js +131 -147
  5. package/dist/Database.d.ts +54 -68
  6. package/dist/Database.js +97 -113
  7. package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
  8. package/dist/{ObjectPool.js → InstanceCache.js} +85 -83
  9. package/dist/LazyReferenceCollection.d.ts +11 -15
  10. package/dist/LazyReferenceCollection.js +12 -16
  11. package/dist/Model.d.ts +37 -52
  12. package/dist/Model.js +46 -61
  13. package/dist/ModelRegistry.d.ts +21 -19
  14. package/dist/ModelRegistry.js +23 -27
  15. package/dist/NetworkMonitor.d.ts +5 -6
  16. package/dist/NetworkMonitor.js +5 -6
  17. package/dist/SyncClient.d.ts +112 -112
  18. package/dist/SyncClient.js +165 -172
  19. package/dist/adapters/alwaysOnline.d.ts +6 -8
  20. package/dist/adapters/alwaysOnline.js +6 -8
  21. package/dist/adapters/inMemoryStorage.d.ts +9 -9
  22. package/dist/adapters/inMemoryStorage.js +9 -9
  23. package/dist/agent/Agent.d.ts +27 -32
  24. package/dist/agent/Agent.js +18 -19
  25. package/dist/agent/index.d.ts +4 -4
  26. package/dist/agent/index.js +5 -5
  27. package/dist/agent/session.d.ts +47 -44
  28. package/dist/agent/session.js +37 -48
  29. package/dist/agent/types.d.ts +26 -31
  30. package/dist/agent/types.js +6 -7
  31. package/dist/ai-sdk/coordinatedTool.d.ts +108 -0
  32. package/dist/ai-sdk/{coordinated-tool.js → coordinatedTool.js} +44 -38
  33. package/dist/ai-sdk/coordinationContext.d.ts +46 -0
  34. package/dist/ai-sdk/{coordination-context.js → coordinationContext.js} +26 -33
  35. package/dist/ai-sdk/index.d.ts +25 -22
  36. package/dist/ai-sdk/index.js +25 -22
  37. package/dist/ai-sdk/wrap.d.ts +6 -7
  38. package/dist/ai-sdk/wrap.js +1 -1
  39. package/dist/auth/credentialPolicy.d.ts +69 -74
  40. package/dist/auth/credentialPolicy.js +51 -56
  41. package/dist/auth/credentialSource.d.ts +6 -5
  42. package/dist/auth/credentialSource.js +9 -10
  43. package/dist/auth/index.d.ts +59 -58
  44. package/dist/auth/index.js +31 -37
  45. package/dist/auth/schemas.d.ts +5 -4
  46. package/dist/auth/schemas.js +5 -4
  47. package/dist/batching/index.d.ts +19 -21
  48. package/dist/batching/index.js +14 -17
  49. package/dist/cli.cjs +167 -119
  50. package/dist/client/Ablo.d.ts +73 -73
  51. package/dist/client/Ablo.js +125 -160
  52. package/dist/client/ApiClient.d.ts +30 -19
  53. package/dist/client/ApiClient.js +133 -38
  54. package/dist/client/auth.d.ts +47 -47
  55. package/dist/client/auth.js +108 -117
  56. package/dist/client/claimHeartbeatLoop.d.ts +50 -0
  57. package/dist/client/claimHeartbeatLoop.js +88 -0
  58. package/dist/client/consoleLogger.d.ts +5 -6
  59. package/dist/client/consoleLogger.js +5 -6
  60. package/dist/client/createInternalComponents.d.ts +14 -17
  61. package/dist/client/createInternalComponents.js +25 -30
  62. package/dist/client/createModelProxy.d.ts +130 -120
  63. package/dist/client/createModelProxy.js +152 -122
  64. package/dist/client/credentialEndpoint.d.ts +40 -42
  65. package/dist/client/credentialEndpoint.js +35 -36
  66. package/dist/client/functionalUpdate.d.ts +29 -27
  67. package/dist/client/functionalUpdate.js +21 -21
  68. package/dist/client/hostedEndpoints.d.ts +9 -12
  69. package/dist/client/hostedEndpoints.js +9 -12
  70. package/dist/client/httpClient.d.ts +57 -53
  71. package/dist/client/httpClient.js +29 -31
  72. package/dist/client/identity.d.ts +15 -20
  73. package/dist/client/identity.js +47 -58
  74. package/dist/client/modelRegistration.d.ts +5 -9
  75. package/dist/client/modelRegistration.js +67 -87
  76. package/dist/client/options.d.ts +134 -157
  77. package/dist/client/options.js +3 -7
  78. package/dist/client/registerDataSource.d.ts +9 -9
  79. package/dist/client/registerDataSource.js +15 -16
  80. package/dist/client/resourceTypes.d.ts +64 -75
  81. package/dist/client/resourceTypes.js +4 -10
  82. package/dist/client/schemaConfig.d.ts +31 -43
  83. package/dist/client/schemaConfig.js +38 -50
  84. package/dist/client/sessionMint.d.ts +16 -12
  85. package/dist/client/sessionMint.js +26 -31
  86. package/dist/client/validateAbloOptions.d.ts +12 -14
  87. package/dist/client/validateAbloOptions.js +8 -9
  88. package/dist/client/writeOptionsSchema.d.ts +18 -16
  89. package/dist/client/writeOptionsSchema.js +23 -20
  90. package/dist/client/wsMutationExecutor.d.ts +15 -20
  91. package/dist/client/wsMutationExecutor.js +17 -23
  92. package/dist/context.d.ts +6 -4
  93. package/dist/context.js +6 -4
  94. package/dist/coordination/index.d.ts +10 -8
  95. package/dist/coordination/index.js +14 -12
  96. package/dist/coordination/schema.d.ts +176 -128
  97. package/dist/coordination/schema.js +197 -133
  98. package/dist/coordination/trace.d.ts +9 -10
  99. package/dist/coordination/trace.js +13 -14
  100. package/dist/core/DatabaseManager.d.ts +5 -7
  101. package/dist/core/DatabaseManager.js +15 -19
  102. package/dist/core/QueryProcessor.d.ts +7 -9
  103. package/dist/core/QueryProcessor.js +22 -28
  104. package/dist/core/QueryView.d.ts +8 -8
  105. package/dist/core/QueryView.js +2 -2
  106. package/dist/core/StoreManager.d.ts +12 -14
  107. package/dist/core/StoreManager.js +21 -24
  108. package/dist/core/ViewRegistry.d.ts +5 -5
  109. package/dist/core/ViewRegistry.js +4 -4
  110. package/dist/core/index.d.ts +17 -12
  111. package/dist/core/index.js +32 -26
  112. package/dist/core/openIDBWithTimeout.d.ts +38 -36
  113. package/dist/core/openIDBWithTimeout.js +42 -43
  114. package/dist/core/queryUtils.d.ts +45 -0
  115. package/dist/core/queryUtils.js +69 -0
  116. package/dist/core/storeContract.d.ts +63 -61
  117. package/dist/core/storeContract.js +8 -12
  118. package/dist/environment.d.ts +28 -0
  119. package/dist/environment.js +21 -0
  120. package/dist/errorCodes.d.ts +107 -99
  121. package/dist/errorCodes.js +131 -132
  122. package/dist/errors.d.ts +160 -166
  123. package/dist/errors.js +155 -158
  124. package/dist/index.d.ts +30 -27
  125. package/dist/index.js +89 -86
  126. package/dist/interfaces/index.d.ts +102 -113
  127. package/dist/interfaces/index.js +5 -4
  128. package/dist/keys/index.d.ts +27 -29
  129. package/dist/keys/index.js +41 -40
  130. package/dist/mutators/RecordingTransaction.d.ts +16 -16
  131. package/dist/mutators/RecordingTransaction.js +31 -37
  132. package/dist/mutators/Transaction.d.ts +18 -26
  133. package/dist/mutators/Transaction.js +14 -20
  134. package/dist/mutators/UndoManager.d.ts +122 -131
  135. package/dist/mutators/UndoManager.js +145 -156
  136. package/dist/mutators/defineMutators.d.ts +23 -34
  137. package/dist/mutators/defineMutators.js +14 -20
  138. package/dist/mutators/inverseOp.d.ts +12 -15
  139. package/dist/mutators/inverseOp.js +12 -15
  140. package/dist/mutators/mutateActions.d.ts +10 -9
  141. package/dist/mutators/mutateActions.js +1 -1
  142. package/dist/mutators/readerActions.d.ts +9 -8
  143. package/dist/mutators/readerActions.js +2 -2
  144. package/dist/mutators/undoApply.d.ts +31 -27
  145. package/dist/mutators/undoApply.js +26 -24
  146. package/dist/policy/index.d.ts +5 -3
  147. package/dist/policy/index.js +5 -3
  148. package/dist/policy/types.d.ts +104 -100
  149. package/dist/policy/types.js +67 -66
  150. package/dist/query/client.d.ts +28 -23
  151. package/dist/query/client.js +45 -43
  152. package/dist/query/types.d.ts +37 -60
  153. package/dist/query/types.js +13 -33
  154. package/dist/react/AbloProvider.d.ts +1 -1
  155. package/dist/react/AbloProvider.js +2 -2
  156. package/dist/react/context.d.ts +25 -28
  157. package/dist/react/context.js +9 -10
  158. package/dist/react/index.d.ts +41 -42
  159. package/dist/react/index.js +37 -38
  160. package/dist/react/internalContext.d.ts +17 -19
  161. package/dist/react/useAblo.d.ts +23 -22
  162. package/dist/react/useAblo.js +16 -14
  163. package/dist/react/useCurrentUserId.d.ts +8 -7
  164. package/dist/react/useCurrentUserId.js +8 -7
  165. package/dist/react/useErrorListener.d.ts +7 -7
  166. package/dist/react/useErrorListener.js +10 -11
  167. package/dist/react/useMutationFailureListener.d.ts +8 -8
  168. package/dist/react/useMutationFailureListener.js +8 -8
  169. package/dist/react/useMutators.d.ts +11 -11
  170. package/dist/react/useMutators.js +3 -3
  171. package/dist/react/useReactive.js +2 -2
  172. package/dist/react/useSyncStatus.d.ts +4 -6
  173. package/dist/react/useUndoScope.d.ts +7 -9
  174. package/dist/react/useUndoScope.js +1 -1
  175. package/dist/schema/coordination.d.ts +21 -25
  176. package/dist/schema/coordination.js +21 -25
  177. package/dist/schema/ddl.d.ts +43 -39
  178. package/dist/schema/ddl.js +75 -68
  179. package/dist/schema/ddlLock.d.ts +20 -24
  180. package/dist/schema/ddlLock.js +18 -23
  181. package/dist/schema/diff.d.ts +99 -61
  182. package/dist/schema/diff.js +43 -34
  183. package/dist/schema/field.d.ts +37 -42
  184. package/dist/schema/field.js +35 -48
  185. package/dist/schema/generate.d.ts +12 -12
  186. package/dist/schema/generate.js +12 -12
  187. package/dist/schema/index.d.ts +2 -2
  188. package/dist/schema/index.js +21 -23
  189. package/dist/schema/model.d.ts +118 -143
  190. package/dist/schema/model.js +22 -33
  191. package/dist/schema/openapi.d.ts +10 -9
  192. package/dist/schema/openapi.js +5 -3
  193. package/dist/schema/queries.d.ts +29 -31
  194. package/dist/schema/queries.js +23 -25
  195. package/dist/schema/relation.d.ts +89 -99
  196. package/dist/schema/relation.js +13 -13
  197. package/dist/schema/residency.d.ts +16 -13
  198. package/dist/schema/residency.js +16 -13
  199. package/dist/schema/roles.d.ts +36 -43
  200. package/dist/schema/roles.js +31 -37
  201. package/dist/schema/schema.d.ts +33 -42
  202. package/dist/schema/schema.js +31 -32
  203. package/dist/schema/select.d.ts +13 -13
  204. package/dist/schema/select.js +13 -13
  205. package/dist/schema/serialize.d.ts +28 -31
  206. package/dist/schema/serialize.js +27 -31
  207. package/dist/schema/sugar.d.ts +17 -32
  208. package/dist/schema/sugar.js +14 -29
  209. package/dist/schema/{sync-delta-row.d.ts → syncDeltaRow.d.ts} +26 -49
  210. package/dist/schema/syncDeltaRow.js +89 -0
  211. package/dist/schema/tenancy.d.ts +44 -46
  212. package/dist/schema/tenancy.js +46 -48
  213. package/dist/server/adapter.d.ts +58 -58
  214. package/dist/server/adapter.js +13 -14
  215. package/dist/server/commit.d.ts +60 -64
  216. package/dist/server/index.d.ts +9 -10
  217. package/dist/server/index.js +1 -1
  218. package/dist/server/readConfig.d.ts +70 -0
  219. package/dist/server/readConfig.js +8 -0
  220. package/dist/server/storageMode.d.ts +23 -0
  221. package/dist/server/storageMode.js +17 -0
  222. package/dist/source/adapter.d.ts +30 -25
  223. package/dist/source/adapter.js +10 -10
  224. package/dist/source/adapters/drizzle.d.ts +28 -23
  225. package/dist/source/adapters/drizzle.js +30 -25
  226. package/dist/source/adapters/kysely.d.ts +27 -25
  227. package/dist/source/adapters/kysely.js +24 -23
  228. package/dist/source/adapters/memory.d.ts +8 -7
  229. package/dist/source/adapters/memory.js +9 -8
  230. package/dist/source/adapters/prisma.d.ts +13 -12
  231. package/dist/source/adapters/prisma.js +22 -25
  232. package/dist/source/conformance.d.ts +18 -11
  233. package/dist/source/conformance.js +17 -11
  234. package/dist/source/connector.d.ts +31 -32
  235. package/dist/source/connector.js +28 -28
  236. package/dist/source/connectorProtocol.d.ts +160 -0
  237. package/dist/source/connectorProtocol.js +162 -0
  238. package/dist/source/contract.d.ts +26 -27
  239. package/dist/source/contract.js +28 -29
  240. package/dist/source/factory.d.ts +46 -58
  241. package/dist/source/factory.js +22 -27
  242. package/dist/source/index.d.ts +7 -9
  243. package/dist/source/index.js +12 -14
  244. package/dist/source/migrations.d.ts +9 -9
  245. package/dist/source/migrations.js +9 -9
  246. package/dist/source/next.d.ts +9 -10
  247. package/dist/source/next.js +6 -7
  248. package/dist/source/pushQueue.d.ts +69 -47
  249. package/dist/source/pushQueue.js +32 -28
  250. package/dist/source/signing.d.ts +46 -17
  251. package/dist/source/signing.js +28 -11
  252. package/dist/source/types.d.ts +121 -104
  253. package/dist/source/types.js +13 -14
  254. package/dist/stores/ObjectStore.d.ts +10 -11
  255. package/dist/stores/ObjectStore.js +11 -12
  256. package/dist/stores/ObjectStoreContract.d.ts +12 -15
  257. package/dist/stores/SyncActionStore.d.ts +7 -11
  258. package/dist/stores/SyncActionStore.js +13 -17
  259. package/dist/surface.d.ts +27 -20
  260. package/dist/surface.js +27 -20
  261. package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +36 -42
  262. package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +76 -76
  263. package/dist/sync/ConnectionManager.d.ts +39 -50
  264. package/dist/sync/ConnectionManager.js +55 -66
  265. package/dist/sync/NetworkProbe.d.ts +24 -29
  266. package/dist/sync/NetworkProbe.js +63 -69
  267. package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +42 -41
  268. package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +59 -54
  269. package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +43 -57
  270. package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +43 -51
  271. package/dist/sync/SyncWebSocket.d.ts +139 -165
  272. package/dist/sync/SyncWebSocket.js +191 -223
  273. package/dist/sync/awaitClaimGrant.d.ts +18 -18
  274. package/dist/sync/awaitClaimGrant.js +11 -11
  275. package/dist/sync/bootstrapApply.d.ts +34 -24
  276. package/dist/sync/bootstrapApply.js +27 -19
  277. package/dist/sync/commitFrames.d.ts +21 -20
  278. package/dist/sync/commitFrames.js +18 -18
  279. package/dist/sync/createClaimStream.d.ts +23 -22
  280. package/dist/sync/createClaimStream.js +105 -23
  281. package/dist/sync/createPresenceStream.d.ts +19 -18
  282. package/dist/sync/createPresenceStream.js +25 -26
  283. package/dist/sync/createSnapshot.d.ts +12 -14
  284. package/dist/sync/createSnapshot.js +20 -26
  285. package/dist/sync/credentialLifecycle.d.ts +104 -104
  286. package/dist/sync/credentialLifecycle.js +140 -147
  287. package/dist/sync/deltaPipeline.d.ts +36 -34
  288. package/dist/sync/deltaPipeline.js +64 -65
  289. package/dist/sync/groupChange.d.ts +63 -61
  290. package/dist/sync/groupChange.js +74 -78
  291. package/dist/sync/heartbeat.d.ts +34 -33
  292. package/dist/sync/heartbeat.js +31 -31
  293. package/dist/sync/participants.d.ts +19 -19
  294. package/dist/sync/schemas.d.ts +3 -2
  295. package/dist/sync/schemas.js +14 -10
  296. package/dist/sync/syncCursor.d.ts +17 -21
  297. package/dist/sync/syncCursor.js +17 -21
  298. package/dist/sync/syncPlan.d.ts +28 -36
  299. package/dist/sync/syncPlan.js +18 -19
  300. package/dist/sync/syncPosition.d.ts +54 -49
  301. package/dist/sync/syncPosition.js +57 -52
  302. package/dist/sync/wsFrameHandlers.d.ts +35 -36
  303. package/dist/sync/wsFrameHandlers.js +63 -67
  304. package/dist/testing/fixtures/bootstrap.d.ts +12 -6
  305. package/dist/testing/fixtures/bootstrap.js +12 -6
  306. package/dist/testing/fixtures/deltas.d.ts +30 -33
  307. package/dist/testing/fixtures/deltas.js +30 -33
  308. package/dist/testing/fixtures/models.d.ts +11 -10
  309. package/dist/testing/fixtures/models.js +11 -10
  310. package/dist/testing/helpers/{react-wrapper.d.ts → reactWrapper.d.ts} +13 -10
  311. package/dist/testing/helpers/{react-wrapper.js → reactWrapper.js} +15 -12
  312. package/dist/testing/helpers/{sync-engine-harness.d.ts → syncEngineHarness.d.ts} +17 -15
  313. package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +12 -10
  314. package/dist/testing/helpers/wait.d.ts +13 -8
  315. package/dist/testing/helpers/wait.js +13 -8
  316. package/dist/testing/index.d.ts +3 -3
  317. package/dist/testing/index.js +2 -2
  318. package/dist/testing/mocks/MockMutationExecutor.d.ts +18 -17
  319. package/dist/testing/mocks/MockMutationExecutor.js +15 -14
  320. package/dist/testing/mocks/MockNetworkMonitor.d.ts +8 -8
  321. package/dist/testing/mocks/MockNetworkMonitor.js +8 -8
  322. package/dist/testing/mocks/MockSyncContext.d.ts +20 -17
  323. package/dist/testing/mocks/MockSyncContext.js +15 -13
  324. package/dist/testing/mocks/MockSyncStore.d.ts +10 -10
  325. package/dist/testing/mocks/MockSyncStore.js +11 -11
  326. package/dist/testing/mocks/MockWebSocket.d.ts +26 -22
  327. package/dist/testing/mocks/MockWebSocket.js +22 -21
  328. package/dist/transactions/TransactionQueue.d.ts +181 -176
  329. package/dist/transactions/TransactionQueue.js +338 -350
  330. package/dist/transactions/TransactionStore.d.ts +6 -4
  331. package/dist/transactions/TransactionStore.js +6 -4
  332. package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
  333. package/dist/transactions/UnconfirmedWrites.js +104 -0
  334. package/dist/transactions/coalesceRules.d.ts +41 -17
  335. package/dist/transactions/coalesceRules.js +40 -17
  336. package/dist/transactions/commitPayload.d.ts +48 -52
  337. package/dist/transactions/commitPayload.js +48 -57
  338. package/dist/transactions/deltaConfirmation.d.ts +20 -22
  339. package/dist/transactions/deltaConfirmation.js +37 -45
  340. package/dist/transactions/optimisticApply.d.ts +49 -0
  341. package/dist/transactions/optimisticApply.js +65 -0
  342. package/dist/transactions/{persistedReplay.d.ts → replayValidation.d.ts} +28 -22
  343. package/dist/transactions/{persistedReplay.js → replayValidation.js} +33 -27
  344. package/dist/types/global.d.ts +46 -41
  345. package/dist/types/global.js +20 -19
  346. package/dist/types/index.d.ts +71 -77
  347. package/dist/types/index.js +22 -22
  348. package/dist/types/modelData.d.ts +6 -8
  349. package/dist/types/modelData.js +5 -7
  350. package/dist/types/participant.d.ts +10 -11
  351. package/dist/types/participant.js +6 -8
  352. package/dist/types/streams.d.ts +208 -195
  353. package/dist/types/streams.js +7 -7
  354. package/dist/utils/asyncIterator.d.ts +25 -32
  355. package/dist/utils/asyncIterator.js +25 -32
  356. package/dist/utils/duration.d.ts +12 -15
  357. package/dist/utils/duration.js +12 -15
  358. package/dist/utils/mobxSetup.d.ts +53 -0
  359. package/dist/utils/{mobx-setup.js → mobxSetup.js} +42 -98
  360. package/dist/webhooks/events.d.ts +21 -16
  361. package/dist/webhooks/events.js +10 -8
  362. package/dist/webhooks/index.d.ts +5 -7
  363. package/dist/webhooks/index.js +5 -7
  364. package/dist/{schema/sync-delta-wire.d.ts → wire/delta.d.ts} +58 -41
  365. package/dist/wire/delta.js +114 -0
  366. package/dist/wire/errorEnvelope.d.ts +30 -31
  367. package/dist/wire/errorEnvelope.js +34 -40
  368. package/dist/wire/frames.d.ts +79 -86
  369. package/dist/wire/frames.js +26 -33
  370. package/dist/wire/index.d.ts +14 -12
  371. package/dist/wire/index.js +30 -26
  372. package/dist/wire/listEnvelope.d.ts +16 -23
  373. package/dist/wire/listEnvelope.js +7 -6
  374. package/dist/wire/protocol.d.ts +25 -32
  375. package/dist/wire/protocol.js +25 -32
  376. package/dist/wire/protocolVersion.d.ts +44 -40
  377. package/dist/wire/protocolVersion.js +44 -40
  378. package/docs/coordination.md +59 -0
  379. package/package.json +11 -10
  380. package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
  381. package/dist/ai-sdk/coordination-context.d.ts +0 -52
  382. package/dist/core/query-utils.d.ts +0 -34
  383. package/dist/core/query-utils.js +0 -59
  384. package/dist/schema/sync-delta-row.js +0 -103
  385. package/dist/schema/sync-delta-wire.js +0 -102
  386. package/dist/server/read-config.d.ts +0 -67
  387. package/dist/server/read-config.js +0 -8
  388. package/dist/server/storage-mode.d.ts +0 -8
  389. package/dist/server/storage-mode.js +0 -28
  390. package/dist/source/connector-protocol.d.ts +0 -159
  391. package/dist/source/connector-protocol.js +0 -161
  392. package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
  393. package/dist/transactions/OptimisticEchoTracker.js +0 -104
  394. package/dist/transactions/mutation-error-handler.d.ts +0 -5
  395. package/dist/transactions/mutation-error-handler.js +0 -39
  396. package/dist/transactions/optimistic.d.ts +0 -24
  397. package/dist/transactions/optimistic.js +0 -45
  398. package/dist/utils/mobx-setup.d.ts +0 -42
@@ -1,14 +1,14 @@
1
1
  /**
2
- * SyncClient - Mutation and offline queue manager
3
- *
4
- * Responsibilities:
5
- * - Handle model mutations (create, update, delete, archive)
6
- * - Manage offline mutation queue with persistence
7
- * - Send mutations to server via API client
8
- * - Handle conflict resolution for local changes
2
+ * Applies model mutations and manages the offline write queue. The
3
+ * SyncClient turns local create, update, delete, and archive calls into
4
+ * optimistic changes, holds them while the client is offline, sends them to
5
+ * the server when connectivity returns, and resolves conflicts when the
6
+ * server's version of a row disagrees with the local one. It sits between the
7
+ * reactive object pool and the {@link TransactionQueue} that delivers writes
8
+ * over the network.
9
9
  */
10
10
  import { runInAction } from 'mobx';
11
- import { ObjectPool, ModelScope } from './ObjectPool.js';
11
+ import { InstanceCache, ModelScope } from './InstanceCache.js';
12
12
  import { Model } from './Model.js';
13
13
  // ModelRegistry instance accessed via this.objectPool.registry
14
14
  import { LoadStrategy } from './types/index.js';
@@ -17,17 +17,18 @@ import { AbloAuthenticationError, AbloError, AbloValidationError } from './error
17
17
  import { EventEmitter } from 'events';
18
18
  import { NetworkMonitor } from './NetworkMonitor.js';
19
19
  import { TransactionQueue } from './transactions/TransactionQueue.js';
20
- import { persistedMutationSchema } from './transactions/persistedReplay.js';
21
- import { OptimisticEchoTracker, } from './transactions/OptimisticEchoTracker.js';
20
+ import { persistedMutationSchema } from './transactions/replayValidation.js';
21
+ import { UnconfirmedWrites, } from './transactions/UnconfirmedWrites.js';
22
22
  import { SyncPosition } from './sync/syncPosition.js';
23
23
  /**
24
- * Is the raw snapshot record strictly newer than the pooled model? Compares
25
- * server-stamped `updatedAt` (the engine has no numeric row version; the delta
26
- * pipeline is arrival-ordered last-write-wins). An undefined incoming time is
27
- * treated as NOT newer (don't clobber a known row); an undefined existing time
28
- * means the pool row is unversioned, so the incoming record wins. Used by the
29
- * scoped hydrate-on-enter apply to drop snapshot rows a live delta already
30
- * advanced past the snapshot watermark.
24
+ * Reports whether an incoming snapshot record is strictly newer than the
25
+ * model already in the pool. The comparison uses the server-stamped
26
+ * `updatedAt` timestamp, since rows carry no numeric version and the delta
27
+ * pipeline resolves order by arrival (last write wins). An undefined incoming
28
+ * timestamp counts as not newer, so a known row is never clobbered; an
29
+ * undefined existing timestamp means the pooled row is unversioned, so the
30
+ * incoming record wins. The scoped hydrate-on-enter path uses this to drop
31
+ * snapshot rows that a live delta has already advanced past.
31
32
  */
32
33
  function rawRecordIsNewer(data, existing) {
33
34
  const raw = data.updatedAt;
@@ -46,10 +47,10 @@ function rawRecordIsNewer(data, existing) {
46
47
  return inMs > exMs;
47
48
  }
48
49
  /**
49
- * Narrow an untyped server `updatedAt` (ISO string / epoch / Date, off a
50
- * `Record<string, unknown>` row) to epoch ms for LWW comparison. Falsy /
51
- * non-date values 0, matching the conflict resolver's historical
52
- * "no timestamp = epoch" behavior.
50
+ * Converts an untyped server `updatedAt` value — an ISO string, epoch number,
51
+ * or Date read off an untyped row into epoch milliseconds for
52
+ * last-write-wins comparison. Falsy or non-date values become 0, matching the
53
+ * conflict resolver's rule that a missing timestamp sorts as the epoch.
53
54
  */
54
55
  function toEpochMs(value) {
55
56
  if (!value)
@@ -74,32 +75,28 @@ export class SyncClient extends EventEmitter {
74
75
  // Pending mutations queue
75
76
  pendingMutations = [];
76
77
  /**
77
- * Tracks transaction ids the client has optimistically applied but
78
- * the server has not yet confirmed. The receive path consults it
79
- * to recognize delta echoes of own mutations and suppress the
80
- * (otherwise-redundant) pool mutation the IDB write still runs
81
- * because the delta is the authoritative version of the row.
78
+ * Tracks the ids of transactions the client has applied optimistically but
79
+ * the server has not yet confirmed. When a delta arrives, the receive path
80
+ * consults this set to recognize the echo of the client's own mutation and
81
+ * skip the now-redundant pool update; the IndexedDB write still runs,
82
+ * because the delta is the authoritative version of the row. Without this
83
+ * discriminator, an optimistically applied delete followed by a
84
+ * server-confirmed create echo would resurrect the row for the window
85
+ * between the two confirmations.
82
86
  *
83
- * The receive-layer discriminator named in
84
- * `apps/sync-server/docs/OPTIMISTIC_RECONCILIATION.md`. Without
85
- * it, an optimistically-applied DELETE followed by a
86
- * server-confirming CREATE echo resurrects the row for the window
87
- * between the two confirmations (the chart-delete flicker).
88
- *
89
- * Bounded with FIFO eviction; observability via `getEchoMetrics()`.
87
+ * The set is bounded with first-in-first-out eviction, and
88
+ * {@link SyncClient.getEchoMetrics} exposes its counters.
90
89
  */
91
- echoTracker = new OptimisticEchoTracker();
90
+ echoTracker = new UnconfirmedWrites();
92
91
  // Connection state
93
92
  connectionState = 'disconnected';
94
- offlineSince;
95
93
  // Configuration
96
- maxRetries = 3;
97
94
  isDisposed = false;
98
95
  /**
99
- * THE client's place in the global delta order the one canonical
100
- * instance (see `sync/syncPosition.ts`). The store advances
101
- * `applied`/`persisted` as deltas land; the queue advances `acked` on
102
- * commit responses; snapshots/claims read `readFloor`.
96
+ * The client's position in the global delta order, held as the single
97
+ * canonical {@link SyncPosition} instance. The store advances `applied` and
98
+ * `persisted` as deltas land, the queue advances `acked` on commit
99
+ * responses, and snapshots and claims read `readFloor`.
103
100
  */
104
101
  position = new SyncPosition();
105
102
  constructor(objectPool, database) {
@@ -110,8 +107,8 @@ export class SyncClient extends EventEmitter {
110
107
  // Initialize TransactionQueue with proper configuration
111
108
  this.transactionQueue = new TransactionQueue({
112
109
  position: this.position,
113
- maxBatchSize: 50, // Increased from 10 to reduce batch count for large operations
114
- // Lower delay for snappier dev UX; batching still happens via coalescing
110
+ maxBatchSize: 50, // Larger batches keep the batch count low for bulk operations
111
+ // A short delay keeps writes responsive; coalescing still groups them
115
112
  batchDelay: 150,
116
113
  maxRetries: 3,
117
114
  enableOptimistic: true,
@@ -122,16 +119,17 @@ export class SyncClient extends EventEmitter {
122
119
  });
123
120
  // Provide connection state to TransactionQueue - prevents rollbacks during disconnection
124
121
  this.transactionQueue.setConnectionChecker(() => this.connectionState === 'connected');
125
- // LINEAR PATTERN: Subscribe to rollback events to restore ObjectPool state
126
- // When a transaction fails (server rejects or timeout), we need to restore the model
127
- // Since we no longer write to IndexedDB optimistically, IndexedDB already has correct state
122
+ // Restore object-pool state when a transaction is rolled back. If the
123
+ // server rejects a write or it times out, the model's previous state is
124
+ // put back. Because writes are no longer applied to IndexedDB
125
+ // optimistically, that store already holds the correct state.
128
126
  this.setupTransactionRollbackHandling();
129
- // REPLICACHE PATTERN: Forward reconciliation requests from TransactionQueue
130
- // When delta confirmation times out, instead of rolling back we request the sync layer
131
- // to cycle the WebSocket connection, triggering a delta catch-up from the server
127
+ // Forward reconciliation requests from the transaction queue. When delta
128
+ // confirmation times out, the client cycles the WebSocket connection to
129
+ // trigger a catch-up from the server rather than rolling the write back.
132
130
  this.setupReconciliationForwarding();
133
- // LINEAR PATTERN: Persist unconfirmed transactions to IndexedDB
134
- // When delta retries exhaust, cache in IDB so they survive tab close
131
+ // Persist unconfirmed transactions to IndexedDB. When delta retries are
132
+ // exhausted, the write is cached so it survives a tab close.
135
133
  this.setupAwaitingTransactionPersistence();
136
134
  // Setup network monitoring
137
135
  this.setupNetworkMonitoring();
@@ -289,10 +287,10 @@ export class SyncClient extends EventEmitter {
289
287
  });
290
288
  }
291
289
  /**
292
- * Forward reconciliation requests from TransactionQueue to the sync layer.
293
- * When delta confirmation times out, TransactionQueue emits 'reconciliation:needed'
294
- * instead of rolling back following the Replicache/PowerSync pattern of never
295
- * destroying optimistic state that the server may have committed.
290
+ * Forward reconciliation requests from the {@link TransactionQueue} to the
291
+ * sync layer. When delta confirmation times out, the queue emits
292
+ * `reconciliation:needed` instead of rolling back, so optimistic state the
293
+ * server may already have committed is never destroyed.
296
294
  */
297
295
  setupReconciliationForwarding() {
298
296
  this.transactionQueue.on('reconciliation:needed', (event) => {
@@ -310,10 +308,10 @@ export class SyncClient extends EventEmitter {
310
308
  });
311
309
  }
312
310
  /**
313
- * LINEAR PATTERN: Persist unconfirmed transactions to IndexedDB.
314
- * When delta confirmation retries exhaust, the transaction data is cached in IDB
315
- * so it survives tab close. On next session, WebSocket reconnect + delta catch-up
316
- * will deliver the missing deltas and naturally confirm the transaction.
311
+ * Persist unconfirmed transactions to IndexedDB. When delta-confirmation
312
+ * retries are exhausted, the transaction is cached so it survives a tab
313
+ * close. On the next session, a WebSocket reconnect and delta catch-up
314
+ * deliver the missing deltas and confirm the transaction.
317
315
  */
318
316
  setupAwaitingTransactionPersistence() {
319
317
  this.transactionQueue.on('transaction:persist_awaiting', (event) => {
@@ -341,7 +339,7 @@ export class SyncClient extends EventEmitter {
341
339
  this.echoTracker.drainOnRollback(event.transaction.id);
342
340
  });
343
341
  }
344
- /** Persist an unconfirmed transaction to IDB (never rejects — failures are captured). */
342
+ /** Persist an unconfirmed transaction to IndexedDB (never rejects — failures are captured). */
345
343
  async persistAwaitingTransaction(event) {
346
344
  if (!this.database)
347
345
  return;
@@ -392,19 +390,17 @@ export class SyncClient extends EventEmitter {
392
390
  getContext().observability.setContext(userId, organizationId);
393
391
  // Restore queued mutations from previous session
394
392
  await this.restoreMutationQueue();
395
- // Check network status via the DI'd OnlineStatusProvider (see interfaces.ts:192).
396
- // In the browser this is wired to the service worker's connectivity signal via
397
- // abloOnlineStatus in ablo-sync-adapters.ts; in Node it returns true (assume
398
- // online) via the browserOnlineStatus fallback. NetworkMonitor still drives
399
- // event-based online/offline transitions below; this read is just the initial
400
- // status snapshot at registerUser() time.
393
+ // Read the initial network status from the injected OnlineStatusProvider.
394
+ // In the browser this reflects the host's connectivity signal; in Node it
395
+ // reports online by default. NetworkMonitor drives the ongoing
396
+ // online/offline transitions below this read is only the initial
397
+ // snapshot taken when identity is set.
401
398
  if (getContext().onlineStatus.isOnline()) {
402
399
  this.setConnectionState('connected');
403
400
  }
404
401
  else {
405
402
  // Offline - start in offline mode
406
403
  this.setConnectionState('disconnected');
407
- this.offlineSince = new Date();
408
404
  this.emit('sync:offline');
409
405
  }
410
406
  }
@@ -420,7 +416,7 @@ export class SyncClient extends EventEmitter {
420
416
  * Self-healing helper for individual model records.
421
417
  *
422
418
  * Two registry-driven repair passes run on every row hydrated from
423
- * IDB or merged from a delta:
419
+ * IndexedDB or merged from a delta:
424
420
  *
425
421
  * 1. **Auto-fill** — for each `autoFill` rule the consumer's schema
426
422
  * declares on this model, copy the corresponding identity value
@@ -476,7 +472,7 @@ export class SyncClient extends EventEmitter {
476
472
  return { data: result, healed };
477
473
  }
478
474
  /**
479
- * Hydrate ObjectPool with data from Database
475
+ * Hydrate InstanceCache with data from Database
480
476
  * Called after bootstrap is complete
481
477
  */
482
478
  async hydrateFromDatabase() {
@@ -591,7 +587,7 @@ export class SyncClient extends EventEmitter {
591
587
  catch { }
592
588
  }
593
589
  /**
594
- * Re-hydrate ObjectPool from IndexedDB when the pool already has data.
590
+ * Re-hydrate InstanceCache from IndexedDB when the pool already has data.
595
591
  *
596
592
  * Unlike hydrateFromDatabase() (which uses addBatch and skips existing IDs),
597
593
  * this method properly:
@@ -726,13 +722,14 @@ export class SyncClient extends EventEmitter {
726
722
  return stats;
727
723
  }
728
724
  /**
729
- * Mutate model optimistically and queue for server sync.
730
- * IndexedDB is only updated when server confirms via delta packet.
731
- *
732
- * CRITICAL: Changes are captured BEFORE poolAction to prevent data loss.
733
- * The captured changes are frozen and passed to queueMutation.
725
+ * Apply a mutation to a model optimistically and queue it for server sync.
726
+ * IndexedDB is updated only once the server confirms the change with a delta
727
+ * packet.
734
728
  *
735
- * @see src/sync-engine/types/TrackableModel.ts for change capture pattern
729
+ * A model's changes are captured before the pool action runs, because a pool
730
+ * operation such as an upsert can clear the model's local change set;
731
+ * capturing first ensures those changes are never lost. The captured set is
732
+ * frozen and handed to {@link queueMutation}.
736
733
  */
737
734
  mutate(type, model, poolAction, writeOptions) {
738
735
  // No-op UPDATE guard (O(1)). An update with no dirty fields would travel
@@ -749,9 +746,9 @@ export class SyncClient extends EventEmitter {
749
746
  // real write. Only a genuine Model with an empty dirty-set is skipped.
750
747
  if (type === 'update' && model.hasChanges === false)
751
748
  return;
752
- // CRITICAL FIX: Capture changes BEFORE pool action
753
- // Pool operations (especially upsert) can clear _local changes
754
- // By capturing first, we ensure changes are never lost
749
+ // Capture changes before the pool action runs. Pool operations —
750
+ // upsert in particular can clear the model's local changes, so
751
+ // capturing first ensures they are never lost.
755
752
  const capturedChanges = type === 'update' || type === 'create' ? this.captureModelChanges(model) : undefined;
756
753
  poolAction();
757
754
  this.queueMutation({ type, model, timestamp: new Date(), capturedChanges, writeOptions });
@@ -862,8 +859,9 @@ export class SyncClient extends EventEmitter {
862
859
  }
863
860
  }
864
861
  /**
865
- * Upload file and create attachment (UPLOAD operation)
866
- * Uses Linear-style pattern with immediate URL generation
862
+ * Upload a file and create its attachment record. The upload runs through
863
+ * the {@link TransactionQueue}, and a model is built from the server's
864
+ * response and added to the pool.
867
865
  */
868
866
  async uploadFile(file, options) {
869
867
  if (!this.userId || !this.organizationId) {
@@ -955,16 +953,17 @@ export class SyncClient extends EventEmitter {
955
953
  this.mutate('archive', model, () => { this.objectPool.updateScope(model.id, ModelScope.archived); });
956
954
  }
957
955
  /**
958
- * Append a mutation and schedule its sync work.
956
+ * Append a mutation to the pending queue and schedule its sync work.
959
957
  *
960
- * IDB persistence and the server push are deferred to a microtask so N
961
- * pushes inside the same tick collapse into ONE IDB serialization + ONE
962
- * process call. Without the deferral, queueing 100 mutations (paste,
963
- * PPTX import, AI sandbox layer creation) reserializes the entire
964
- * growing queue 100× O(N²) `model.toJSON()`.
958
+ * IndexedDB persistence and the server push are deferred to a microtask, so
959
+ * many pushes within the same tick collapse into a single serialization and
960
+ * a single process call. Without the deferral, queueing a hundred mutations
961
+ * at once — a large paste, a document import, bulk layer creation would
962
+ * reserialize the whole growing queue a hundred times, an O(N²) cost in
963
+ * `model.toJSON()`.
965
964
  *
966
- * @param mutation.capturedChanges - Pre-captured changes (frozen), used
967
- * to avoid re-reading changes after pool ops that might clear them.
965
+ * @param mutation.capturedChanges - Pre-captured, frozen changes, used to
966
+ * avoid re-reading a model after pool operations that might clear them.
968
967
  */
969
968
  queueMutation(mutation) {
970
969
  this.pendingMutations.push(mutation);
@@ -1018,12 +1017,13 @@ export class SyncClient extends EventEmitter {
1018
1017
  }
1019
1018
  }
1020
1019
  /**
1021
- * Restore mutation queue from IndexedDB.
1020
+ * Restore the mutation queue from IndexedDB.
1022
1021
  *
1023
- * The persisted record was written by a PREVIOUS session (possibly an older
1024
- * SDK build), so each entry is validated at this replay boundary (T1.8):
1025
- * corrupt entries are dropped + logged at debug, and a failure never
1026
- * vanishes into an empty catch offline write survival must be observable.
1022
+ * The persisted record was written by an earlier session, possibly by an
1023
+ * older build of the SDK, so each entry is validated as it is replayed:
1024
+ * corrupt entries are dropped and logged at debug level, and a failure is
1025
+ * never swallowed silently, because the survival of offline writes must be
1026
+ * observable.
1027
1027
  */
1028
1028
  async restoreMutationQueue() {
1029
1029
  if (!this.database || !this.userId)
@@ -1106,8 +1106,8 @@ export class SyncClient extends EventEmitter {
1106
1106
  this.pendingMutations = [];
1107
1107
  // Clear persisted queue before processing
1108
1108
  await this.persistMutationQueue();
1109
- // LINEAR PATTERN: Stage all mutations synchronously in same event loop tick
1110
- // TransactionQueue's microtask will batch and send them together
1109
+ // Stage every mutation synchronously within the same event-loop tick;
1110
+ // the transaction queue's microtask batches and sends them together.
1111
1111
  for (const mutation of mutations) {
1112
1112
  // Skip mutations for deleted models (prevents "not found" errors)
1113
1113
  if (mutation.type !== 'delete' && !this.objectPool.get(mutation.model.id)) {
@@ -1149,11 +1149,10 @@ export class SyncClient extends EventEmitter {
1149
1149
  }
1150
1150
  }
1151
1151
  /**
1152
- * Resolve conflicts between local and server data
1153
- * Used when processing deltas from WebSocket
1154
- *
1155
- * CRITICAL: Always respects certain server states (deletes, deactivations)
1156
- * even when there are local changes, to maintain data consistency.
1152
+ * Resolve a conflict between the local model and incoming server data,
1153
+ * called while processing deltas from the WebSocket. Certain server states,
1154
+ * such as deletions and deactivations, always take precedence even when the
1155
+ * local model has unsynced changes, so the two sides stay consistent.
1157
1156
  */
1158
1157
  resolveConflicts(localModel, serverData) {
1159
1158
  const hasLocalChanges = localModel.hasChanges;
@@ -1218,9 +1217,9 @@ export class SyncClient extends EventEmitter {
1218
1217
  return localModel;
1219
1218
  }
1220
1219
  /**
1221
- * Extract critical state fields from server data
1222
- * These are states that must always be respected, even with local changes.
1223
- * The conflict brain reads exactly these known fields nothing else.
1220
+ * Extract the critical state fields from server data. These are the states
1221
+ * that must be honored even when the local model has unsynced changes. The
1222
+ * conflict resolver reads exactly these fields and no others.
1224
1223
  */
1225
1224
  extractCriticalState(serverData) {
1226
1225
  const critical = {};
@@ -1267,8 +1266,6 @@ export class SyncClient extends EventEmitter {
1267
1266
  await this.processPendingMutations();
1268
1267
  this.setConnectionState('connected');
1269
1268
  this.emit('sync:reconnected');
1270
- // Clear offline timestamp
1271
- this.offlineSince = undefined;
1272
1269
  }
1273
1270
  catch (error) {
1274
1271
  getContext().observability.captureTransactionFailure({
@@ -1284,7 +1281,6 @@ export class SyncClient extends EventEmitter {
1284
1281
  async handleDisconnection() {
1285
1282
  getContext().observability.breadcrumb('Network disconnected', 'sync.offline');
1286
1283
  this.setConnectionState('disconnected');
1287
- this.offlineSince = new Date();
1288
1284
  this.emit('sync:offline');
1289
1285
  }
1290
1286
  /**
@@ -1381,9 +1377,10 @@ export class SyncClient extends EventEmitter {
1381
1377
  this.removeAllListeners();
1382
1378
  }
1383
1379
  /**
1384
- * LINEAR PATTERN: Notify TransactionQueue of incoming delta for sync ID threshold confirmation.
1385
- * Transactions are confirmed when any delta with id >= their lastSyncId threshold arrives.
1386
- * @param syncId - The sync ID of the received delta
1380
+ * Notify the {@link TransactionQueue} of an incoming delta so it can confirm
1381
+ * transactions by sync-id threshold. A transaction is confirmed once any
1382
+ * delta with an id at or beyond its `lastSyncId` threshold arrives.
1383
+ * @param syncId - The sync id of the received delta.
1387
1384
  */
1388
1385
  onDeltaReceived(syncId) {
1389
1386
  try {
@@ -1396,22 +1393,21 @@ export class SyncClient extends EventEmitter {
1396
1393
  }
1397
1394
  }
1398
1395
  /**
1399
- * LINEAR PATTERN: Cancel transactions for orphaned child entities
1400
- *
1401
- * Called by SyncedStore when a DELETE delta arrives for a parent entity.
1402
- * Cancels pending transactions for children that reference the deleted parent.
1396
+ * Cancel pending transactions for child entities orphaned by a parent's
1397
+ * deletion. The store calls this when a delete delta arrives for a parent,
1398
+ * cancelling any queued writes on children that reference it.
1403
1399
  *
1404
- * @param childModelName - The child model type (e.g., 'SlideLayer')
1405
- * @param foreignKey - The FK property name (e.g., 'slideId')
1406
- * @param parentId - The deleted parent's ID
1407
- * @returns Number of transactions cancelled
1400
+ * @param childModelName - The child model type (for example, `SlideLayer`).
1401
+ * @param foreignKey - The foreign-key property name (for example, `slideId`).
1402
+ * @param parentId - The id of the deleted parent.
1403
+ * @returns The number of transactions cancelled.
1408
1404
  */
1409
1405
  cancelTransactionsByForeignKey(childModelName, foreignKey, parentId) {
1410
1406
  return this.transactionQueue.cancelTransactionsByForeignKey(childModelName, foreignKey, parentId);
1411
1407
  }
1412
1408
  /**
1413
- * Wait for a transaction to be confirmed via delta echo (Linear pattern)
1414
- * Delegates to TransactionQueue which already handles timeouts
1409
+ * Wait for a transaction to be confirmed by its delta echo. Delegates to the
1410
+ * {@link TransactionQueue}, which handles the confirmation timeout.
1415
1411
  */
1416
1412
  waitForDeltaConfirmation(transactionId) {
1417
1413
  return this.transactionQueue.waitForConfirmation(transactionId);
@@ -1425,7 +1421,7 @@ export class SyncClient extends EventEmitter {
1425
1421
  /**
1426
1422
  * Get sync statistics. Return type is inferred from the literal so
1427
1423
  * the call site sees the actual shape — `connectionState` narrowed
1428
- * to its three states, `objectPoolStats` typed by `ObjectPool.getStats`.
1424
+ * to its three states, `objectPoolStats` typed by `InstanceCache.getStats`.
1429
1425
  */
1430
1426
  getSyncStats() {
1431
1427
  return {
@@ -1460,25 +1456,26 @@ export class SyncClient extends EventEmitter {
1460
1456
  * can render typed UI (toast keyed by `AbloError.type`, route-level
1461
1457
  * "this entity reverted" boundaries, telemetry).
1462
1458
  *
1463
- * Distinct from `onTransactionEvent('failed', cb)`, which exists only
1464
- * for the legacy parameterless `pendingChanges` counter and intentionally
1465
- * drops the payload. The two coexist — keep the counter callback fast
1466
- * and the typed listener for user-visible surfaces.
1459
+ * Distinct from `onTransactionEvent('failed', cb)`, which serves the
1460
+ * parameterless `pendingChanges` counter and intentionally drops the
1461
+ * payload. The two coexist: the counter callback stays lightweight, while
1462
+ * this typed listener drives user-visible surfaces.
1467
1463
  */
1468
1464
  onMutationFailure(listener) {
1469
1465
  this.transactionQueue.on('transaction:failed', listener);
1470
1466
  return () => this.transactionQueue.off('transaction:failed', listener);
1471
1467
  }
1472
1468
  /**
1473
- * Subscribe to LOCAL transaction creation with the full {@link Transaction}
1469
+ * Subscribe to local transaction creation with the full {@link Transaction}
1474
1470
  * payload (`type`, `modelName`, `modelId`, `data`, `previousData`). This is
1475
- * the feed `BaseSyncedStore.subscribeLocalMutations` taps for undo recording.
1471
+ * the feed the store's local-mutation subscription taps for undo recording.
1476
1472
  *
1477
- * MUST subscribe to the TransactionQueue's emitter directly — that is the
1478
- * ONLY emitter that fires `transaction:created`. SyncClient's own emitter
1479
- * (reached via `subscribe()`) never re-broadcasts it, so routing undo through
1480
- * `subscribe('transaction:created')` silently records nothing. Mirrors
1481
- * `onMutationFailure`, which taps the queue for the same reason.
1473
+ * It subscribes to the {@link TransactionQueue}'s emitter directly, since
1474
+ * that is the only emitter that fires `transaction:created`. The SyncClient's
1475
+ * own emitter (reached through {@link subscribe}) never rebroadcasts that
1476
+ * event, so routing undo through `subscribe('transaction:created')` would
1477
+ * record nothing. {@link onMutationFailure} taps the queue for the same
1478
+ * reason.
1482
1479
  */
1483
1480
  onLocalTransaction(listener) {
1484
1481
  this.transactionQueue.on('transaction:created', listener);
@@ -1574,11 +1571,11 @@ export class SyncClient extends EventEmitter {
1574
1571
  assigneeId,
1575
1572
  });
1576
1573
  }
1577
- // ── Delta + Bootstrap application (owns ObjectPool writes) ──────────────
1574
+ // ── Delta + Bootstrap application (owns InstanceCache writes) ──────────────
1578
1575
  /**
1579
- * Apply a batch of delta results from Database to the ObjectPool.
1576
+ * Apply a batch of delta results from Database to the InstanceCache.
1580
1577
  * Owns: model creation, upsert, remove, archive, conflict resolution.
1581
- * Returns: nothing — ObjectPool is updated in place.
1578
+ * Returns: nothing — InstanceCache is updated in place.
1582
1579
  */
1583
1580
  /**
1584
1581
  * Mark a local transaction as optimistically applied. The matching
@@ -1600,12 +1597,12 @@ export class SyncClient extends EventEmitter {
1600
1597
  return this.echoTracker.getMetrics();
1601
1598
  }
1602
1599
  /**
1603
- * Package-internal accessor for the TransactionQueue. Used by
1604
- * `Ablo.commits.create()` to route raw multi-op envelopes through the
1605
- * same retry-on-reconnect lane as the Model proxy path, and by tests
1606
- * to exercise the queue markTransactionPending wiring on the real
1607
- * instance the SyncClient subscribes to. NOT re-exported to SDK
1608
- * consumers `Ablo` itself is the public surface.
1600
+ * Package-internal accessor for the {@link TransactionQueue}. Used by
1601
+ * `Ablo.commits.create()` to route raw multi-operation envelopes through the
1602
+ * same retry-on-reconnect lane as the model proxy path, and by tests to
1603
+ * exercise the queue's interaction with {@link markTransactionPending} on the
1604
+ * real instance the SyncClient subscribes to. It is not re-exported to SDK
1605
+ * consumers; `Ablo` is the public surface.
1609
1606
  */
1610
1607
  getTransactionQueue() {
1611
1608
  return this.transactionQueue;
@@ -1633,16 +1630,14 @@ export class SyncClient extends EventEmitter {
1633
1630
  }
1634
1631
  for (const result of dbResults) {
1635
1632
  const { modelName, modelId, action, transactionId } = result;
1636
- // ECHO DETECTION. If this delta carries a transaction id that
1637
- // matches one we've optimistically applied locally, the pool
1638
- // already reflects this mutation skip the pool op. The
1639
- // upstream IDB write (in `Database.processDeltaBatch`) still
1640
- // runs; only the in-memory pool mutation is suppressed. This is
1641
- // the architectural fix for the chart-delete flicker: a
1642
- // server-confirmed CREATE arriving AFTER the user has
1643
- // optimistically deleted the row would otherwise re-add the row
1644
- // for the ~2s window before the matching DELETE confirmation
1645
- // lands. See `OPTIMISTIC_RECONCILIATION.md` for the framing.
1633
+ // Echo detection: if this delta carries a transaction id that matches
1634
+ // one already applied optimistically, the pool already reflects the
1635
+ // mutation, so the pool operation is skipped. The IndexedDB write in
1636
+ // Database.processDeltaBatch still runs; only the in-memory pool update
1637
+ // is suppressed. This prevents a resurrection flicker: a server-confirmed
1638
+ // create arriving after the user has optimistically deleted the row would
1639
+ // otherwise re-add it for the brief window before the matching delete
1640
+ // confirmation lands.
1646
1641
  if (this.echoTracker.consumeEcho(transactionId)) {
1647
1642
  continue;
1648
1643
  }
@@ -1708,17 +1703,15 @@ export class SyncClient extends EventEmitter {
1708
1703
  break;
1709
1704
  }
1710
1705
  }
1711
- // Reveal the whole frame in ONE MobX action. `addBatch`/`upsertBatch`/
1712
- // `removeBatch`/`updateScope` are each individually `action`-wrapped,
1713
- // so calling them sequentially flushes reactions at every action
1714
- // boundary — a catch-up frame that adds + updates + removes would fire
1715
- // every dependent reaction (the decks gallery, each open editor) 3-
1716
- // in a row, re-rendering and re-sorting on each. Wrapping them in a
1717
- // single outer `runInAction` defers all reaction flushes to ONE
1718
- // boundary: dependents recompute exactly once regardless of how many
1719
- // models or how many op-kinds the frame touched. This is the MobX
1720
- // equivalent of Replicache's "atomically reveal the new state" — the
1721
- // app never observes a partially-applied frame.
1706
+ // Reveal the whole frame in a single MobX action. `addBatch`,
1707
+ // `upsertBatch`, `removeBatch`, and `updateScope` are each individually
1708
+ // wrapped in an action, so calling them in sequence flushes reactions at
1709
+ // every action boundary — a catch-up frame that adds, updates, and removes
1710
+ // would fire every dependent reaction several times in a row, re-rendering
1711
+ // and re-sorting on each. Wrapping them in one outer `runInAction` defers
1712
+ // all reaction flushes to a single boundary, so dependents recompute
1713
+ // exactly once regardless of how many models or operation kinds the frame
1714
+ // touched. The app therefore never observes a partially applied frame.
1722
1715
  runInAction(() => {
1723
1716
  if (modelsToAdd.length > 0)
1724
1717
  this.objectPool.addBatch(modelsToAdd, ModelScope.live);
@@ -1737,7 +1730,7 @@ export class SyncClient extends EventEmitter {
1737
1730
  });
1738
1731
  }
1739
1732
  /**
1740
- * Apply bootstrap data to the ObjectPool with ghost removal.
1733
+ * Apply bootstrap data to the InstanceCache with ghost removal.
1741
1734
  * Owns: model creation, batch upsert, ghost detection + removal.
1742
1735
  */
1743
1736
  applyBootstrapDataToPool(bootstrapData, protectedIds, options) {
@@ -1775,12 +1768,12 @@ export class SyncClient extends EventEmitter {
1775
1768
  const recordId = data.id;
1776
1769
  if (recordId)
1777
1770
  idsForType.add(recordId);
1778
- // Scoped backfill (P4 hydrate-on-enter): a subset snapshot is taken at
1779
- // a server watermark. If a concurrent live delta already advanced this
1780
- // row past the snapshot, skip it `createFromData` mutates the pooled
1781
- // model IN PLACE (the "keep instances alive" Linear pattern), so this
1782
- // version guard MUST run BEFORE it; an upsert-layer guard would be too
1783
- // late, the row would already be clobbered.
1771
+ // Scoped backfill for the hydrate-on-enter path: a subset snapshot is
1772
+ // taken at a server watermark. If a concurrent live delta already
1773
+ // advanced this row past the snapshot, skip it. `createFromData`
1774
+ // mutates the pooled model in place to keep instances alive, so this
1775
+ // version guard has to run before it; a guard at the upsert layer would
1776
+ // be too late, because the row would already be clobbered.
1784
1777
  if (options?.scoped && recordId) {
1785
1778
  const existing = this.objectPool.get(recordId);
1786
1779
  if (existing && !rawRecordIsNewer(data, existing)) {
@@ -1804,10 +1797,10 @@ export class SyncClient extends EventEmitter {
1804
1797
  this.objectPool.upsertBatch(allModels, ModelScope.live);
1805
1798
  const addedCount = this.objectPool.size - beforeSize;
1806
1799
  const updatedCount = allModels.length - addedCount;
1807
- // Ghost removal remove pool entities not in the server snapshot. Only
1808
- // valid for a FULL bootstrap, where the snapshot is authoritative for each
1809
- // returned type. A SCOPED subset snapshot must NOT remove rows of the same
1810
- // type that belong to other (unhydrated) groups.
1800
+ // Ghost removal: drop pool entities absent from the server snapshot. This
1801
+ // is valid only for a full bootstrap, where the snapshot is authoritative
1802
+ // for each returned type. A scoped subset snapshot must not remove rows of
1803
+ // the same type that belong to other, unhydrated groups.
1811
1804
  let removedCount = 0;
1812
1805
  if (!options?.scoped) {
1813
1806
  const ghostIds = [];