@abloatai/humans 0.37.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 (404) hide show
  1. package/README.md +52 -0
  2. package/dist/Ablo.d.ts +208 -0
  3. package/dist/Ablo.js +120 -0
  4. package/dist/client.d.ts +317 -0
  5. package/dist/client.js +13 -0
  6. package/dist/core.d.ts +35 -0
  7. package/dist/core.js +48 -0
  8. package/dist/humans.d.ts +28 -0
  9. package/dist/humans.js +34 -0
  10. package/dist/index.d.ts +10 -0
  11. package/dist/index.js +6 -0
  12. package/dist/local/BaseSyncedStore.d.ts +807 -0
  13. package/dist/local/BaseSyncedStore.js +1516 -0
  14. package/dist/local/Database.d.ts +322 -0
  15. package/dist/local/Database.js +1589 -0
  16. package/dist/local/InstanceCache.d.ts +255 -0
  17. package/dist/local/InstanceCache.js +1263 -0
  18. package/dist/local/LazyReferenceCollection.d.ts +177 -0
  19. package/dist/local/LazyReferenceCollection.js +461 -0
  20. package/dist/local/Model.d.ts +475 -0
  21. package/dist/local/Model.js +950 -0
  22. package/dist/local/ModelRegistry.d.ts +225 -0
  23. package/dist/local/ModelRegistry.js +539 -0
  24. package/dist/local/NetworkMonitor.d.ts +28 -0
  25. package/dist/local/NetworkMonitor.js +79 -0
  26. package/dist/local/RuntimeContext.d.ts +52 -0
  27. package/dist/local/RuntimeContext.js +80 -0
  28. package/dist/local/SyncClient.d.ts +516 -0
  29. package/dist/local/SyncClient.js +1754 -0
  30. package/dist/local/adapters/alwaysOnline.d.ts +14 -0
  31. package/dist/local/adapters/alwaysOnline.js +17 -0
  32. package/dist/local/adapters/inMemoryStorage.d.ts +37 -0
  33. package/dist/local/adapters/inMemoryStorage.js +122 -0
  34. package/dist/local/client/clientPrelude.d.ts +52 -0
  35. package/dist/local/client/clientPrelude.js +60 -0
  36. package/dist/local/client/consoleLogger.d.ts +35 -0
  37. package/dist/local/client/consoleLogger.js +44 -0
  38. package/dist/local/client/createInternalComponents.d.ts +50 -0
  39. package/dist/local/client/createInternalComponents.js +98 -0
  40. package/dist/local/client/createModelProxy.d.ts +248 -0
  41. package/dist/local/client/createModelProxy.js +880 -0
  42. package/dist/local/client/modelRegistration.d.ts +10 -0
  43. package/dist/local/client/modelRegistration.js +316 -0
  44. package/dist/local/client/options.d.ts +440 -0
  45. package/dist/local/client/options.js +7 -0
  46. package/dist/local/client/reactiveEngine.d.ts +53 -0
  47. package/dist/local/client/reactiveEngine.js +705 -0
  48. package/dist/local/client/resourceTypes.d.ts +12 -0
  49. package/dist/local/client/resourceTypes.js +10 -0
  50. package/dist/local/client/schemaConfig.d.ts +44 -0
  51. package/dist/local/client/schemaConfig.js +185 -0
  52. package/dist/local/client/storeCluster.d.ts +46 -0
  53. package/dist/local/client/storeCluster.js +133 -0
  54. package/dist/local/client/storeLifecycle.d.ts +61 -0
  55. package/dist/local/client/storeLifecycle.js +236 -0
  56. package/dist/local/client/validateAbloOptions.d.ts +42 -0
  57. package/dist/local/client/validateAbloOptions.js +43 -0
  58. package/dist/local/client/wsMutationExecutor.d.ts +27 -0
  59. package/dist/local/client/wsMutationExecutor.js +72 -0
  60. package/dist/local/context.d.ts +42 -0
  61. package/dist/local/context.js +81 -0
  62. package/dist/local/coordination/ClaimLog.d.ts +26 -0
  63. package/dist/local/coordination/ClaimLog.js +32 -0
  64. package/dist/local/interfaces/index.d.ts +311 -0
  65. package/dist/local/interfaces/index.js +9 -0
  66. package/dist/local/localModelContract.d.ts +14 -0
  67. package/dist/local/localModelContract.js +1 -0
  68. package/dist/local/logPosition.d.ts +31 -0
  69. package/dist/local/logPosition.js +53 -0
  70. package/dist/local/mutationPersistence.d.ts +6 -0
  71. package/dist/local/mutationPersistence.js +1 -0
  72. package/dist/local/mutators/RecordingMutation.d.ts +36 -0
  73. package/dist/local/mutators/RecordingMutation.js +182 -0
  74. package/dist/local/mutators/Transaction.d.ts +40 -0
  75. package/dist/local/mutators/Transaction.js +58 -0
  76. package/dist/local/mutators/UndoManager.d.ts +258 -0
  77. package/dist/local/mutators/UndoManager.js +665 -0
  78. package/dist/local/mutators/defineMutators.d.ts +60 -0
  79. package/dist/local/mutators/defineMutators.js +18 -0
  80. package/dist/local/mutators/inverseOp.d.ts +126 -0
  81. package/dist/local/mutators/inverseOp.js +71 -0
  82. package/dist/local/mutators/mutateActions.d.ts +45 -0
  83. package/dist/local/mutators/mutateActions.js +105 -0
  84. package/dist/local/mutators/readerActions.d.ts +33 -0
  85. package/dist/local/mutators/readerActions.js +57 -0
  86. package/dist/local/mutators/undoApply.d.ts +51 -0
  87. package/dist/local/mutators/undoApply.js +117 -0
  88. package/dist/local/persistence.d.ts +7 -0
  89. package/dist/local/persistence.js +9 -0
  90. package/dist/local/query/QueryProcessor.d.ts +75 -0
  91. package/dist/local/query/QueryProcessor.js +255 -0
  92. package/dist/local/query/client.d.ts +64 -0
  93. package/dist/local/query/client.js +138 -0
  94. package/dist/local/query/types.d.ts +85 -0
  95. package/dist/local/query/types.js +16 -0
  96. package/dist/local/schema/serialize.d.ts +1 -0
  97. package/dist/local/schema/serialize.js +1 -0
  98. package/dist/local/store/queryApi.d.ts +13 -0
  99. package/dist/local/store/queryApi.js +35 -0
  100. package/dist/local/storeContract.d.ts +145 -0
  101. package/dist/local/storeContract.js +12 -0
  102. package/dist/local/stores/DatabaseManager.d.ts +112 -0
  103. package/dist/local/stores/DatabaseManager.js +400 -0
  104. package/dist/local/stores/ObjectStore.d.ts +115 -0
  105. package/dist/local/stores/ObjectStore.js +393 -0
  106. package/dist/local/stores/ObjectStoreContract.d.ts +38 -0
  107. package/dist/local/stores/ObjectStoreContract.js +1 -0
  108. package/dist/local/stores/StoreManager.d.ts +114 -0
  109. package/dist/local/stores/StoreManager.js +304 -0
  110. package/dist/local/stores/SyncActionStore.d.ts +99 -0
  111. package/dist/local/stores/SyncActionStore.js +506 -0
  112. package/dist/local/stores/openIDBWithTimeout.d.ts +65 -0
  113. package/dist/local/stores/openIDBWithTimeout.js +153 -0
  114. package/dist/local/stores/persistenceCleanup.d.ts +7 -0
  115. package/dist/local/stores/persistenceCleanup.js +26 -0
  116. package/dist/local/stores/persistenceIdentity.d.ts +27 -0
  117. package/dist/local/stores/persistenceIdentity.js +38 -0
  118. package/dist/local/stores/syncAction.d.ts +26 -0
  119. package/dist/local/stores/syncAction.js +16 -0
  120. package/dist/local/stores/v1PersistenceDeletion.d.ts +8 -0
  121. package/dist/local/stores/v1PersistenceDeletion.js +16 -0
  122. package/dist/local/sync/BootstrapFetcher.d.ts +284 -0
  123. package/dist/local/sync/BootstrapFetcher.js +964 -0
  124. package/dist/local/sync/ConnectionManager.d.ts +8 -0
  125. package/dist/local/sync/ConnectionManager.js +8 -0
  126. package/dist/local/sync/OnDemandLoader.d.ts +231 -0
  127. package/dist/local/sync/OnDemandLoader.js +743 -0
  128. package/dist/local/sync/SubscriptionManager.d.ts +159 -0
  129. package/dist/local/sync/SubscriptionManager.js +243 -0
  130. package/dist/local/sync/SyncWebSocket.d.ts +173 -0
  131. package/dist/local/sync/SyncWebSocket.js +438 -0
  132. package/dist/local/sync/bootstrapApply.d.ts +73 -0
  133. package/dist/local/sync/bootstrapApply.js +73 -0
  134. package/dist/local/sync/commitFrames.d.ts +8 -0
  135. package/dist/local/sync/commitFrames.js +8 -0
  136. package/dist/local/sync/connectionManagerLifecycle.d.ts +23 -0
  137. package/dist/local/sync/connectionManagerLifecycle.js +126 -0
  138. package/dist/local/sync/contextPorts.d.ts +18 -0
  139. package/dist/local/sync/contextPorts.js +31 -0
  140. package/dist/local/sync/createClaimStream.d.ts +64 -0
  141. package/dist/local/sync/createClaimStream.js +475 -0
  142. package/dist/local/sync/createSnapshot.d.ts +29 -0
  143. package/dist/local/sync/createSnapshot.js +116 -0
  144. package/dist/local/sync/credentialLifecycle.d.ts +7 -0
  145. package/dist/local/sync/credentialLifecycle.js +7 -0
  146. package/dist/local/sync/deltaPipeline.d.ts +116 -0
  147. package/dist/local/sync/deltaPipeline.js +357 -0
  148. package/dist/local/sync/groupChange.d.ts +116 -0
  149. package/dist/local/sync/groupChange.js +244 -0
  150. package/dist/local/sync/initialize.d.ts +27 -0
  151. package/dist/local/sync/initialize.js +137 -0
  152. package/dist/local/sync/participants.d.ts +132 -0
  153. package/dist/local/sync/participants.js +342 -0
  154. package/dist/local/sync/persistedPrefix.d.ts +12 -0
  155. package/dist/local/sync/persistedPrefix.js +22 -0
  156. package/dist/local/sync/reconnect.d.ts +23 -0
  157. package/dist/local/sync/reconnect.js +55 -0
  158. package/dist/local/sync/schemaDrift.d.ts +55 -0
  159. package/dist/local/sync/schemaDrift.js +53 -0
  160. package/dist/local/sync/schemas.d.ts +71 -0
  161. package/dist/local/sync/schemas.js +94 -0
  162. package/dist/local/sync/socketEventWiring.d.ts +31 -0
  163. package/dist/local/sync/socketEventWiring.js +130 -0
  164. package/dist/local/sync/syncCursor.d.ts +40 -0
  165. package/dist/local/sync/syncCursor.js +55 -0
  166. package/dist/local/sync/syncPlan.d.ts +54 -0
  167. package/dist/local/sync/syncPlan.js +50 -0
  168. package/dist/local/sync/terminalSessionLifecycle.d.ts +20 -0
  169. package/dist/local/sync/terminalSessionLifecycle.js +50 -0
  170. package/dist/local/sync/wsFrameHandlers.d.ts +8 -0
  171. package/dist/local/sync/wsFrameHandlers.js +8 -0
  172. package/dist/local/transactions/databaseCommitOutbox.d.ts +15 -0
  173. package/dist/local/transactions/databaseCommitOutbox.js +16 -0
  174. package/dist/local/transactions/localMutation.d.ts +10 -0
  175. package/dist/local/transactions/localMutation.js +37 -0
  176. package/dist/local/transactions/mutations/MutationQueue.d.ts +511 -0
  177. package/dist/local/transactions/mutations/MutationQueue.js +1498 -0
  178. package/dist/local/transactions/mutations/MutationStore.d.ts +20 -0
  179. package/dist/local/transactions/mutations/MutationStore.js +53 -0
  180. package/dist/local/transactions/mutations/UnconfirmedWrites.d.ts +82 -0
  181. package/dist/local/transactions/mutations/UnconfirmedWrites.js +104 -0
  182. package/dist/local/transactions/mutations/batchProcessing.d.ts +64 -0
  183. package/dist/local/transactions/mutations/batchProcessing.js +349 -0
  184. package/dist/local/transactions/mutations/coalesceRules.d.ts +58 -0
  185. package/dist/local/transactions/mutations/coalesceRules.js +140 -0
  186. package/dist/local/transactions/mutations/commitApi.d.ts +19 -0
  187. package/dist/local/transactions/mutations/commitApi.js +74 -0
  188. package/dist/local/transactions/mutations/commitLane.d.ts +70 -0
  189. package/dist/local/transactions/mutations/commitLane.js +140 -0
  190. package/dist/local/transactions/mutations/commitLatency.d.ts +52 -0
  191. package/dist/local/transactions/mutations/commitLatency.js +130 -0
  192. package/dist/local/transactions/mutations/commitPayload.d.ts +165 -0
  193. package/dist/local/transactions/mutations/commitPayload.js +152 -0
  194. package/dist/local/transactions/mutations/commitTransport.d.ts +36 -0
  195. package/dist/local/transactions/mutations/commitTransport.js +104 -0
  196. package/dist/local/transactions/mutations/deltaConfirmation.d.ts +63 -0
  197. package/dist/local/transactions/mutations/deltaConfirmation.js +235 -0
  198. package/dist/local/transactions/mutations/durableCommitRestore.d.ts +17 -0
  199. package/dist/local/transactions/mutations/durableCommitRestore.js +95 -0
  200. package/dist/local/transactions/mutations/durableWriteStore.d.ts +14 -0
  201. package/dist/local/transactions/mutations/durableWriteStore.js +12 -0
  202. package/dist/local/transactions/mutations/executionSelection.d.ts +6 -0
  203. package/dist/local/transactions/mutations/executionSelection.js +42 -0
  204. package/dist/local/transactions/mutations/failureHandling.d.ts +16 -0
  205. package/dist/local/transactions/mutations/failureHandling.js +130 -0
  206. package/dist/local/transactions/mutations/failurePolicy.d.ts +13 -0
  207. package/dist/local/transactions/mutations/failurePolicy.js +48 -0
  208. package/dist/local/transactions/mutations/localMutation.d.ts +58 -0
  209. package/dist/local/transactions/mutations/localMutation.js +75 -0
  210. package/dist/local/transactions/mutations/modelOperations.d.ts +44 -0
  211. package/dist/local/transactions/mutations/modelOperations.js +144 -0
  212. package/dist/local/transactions/mutations/mutationPersistence.d.ts +25 -0
  213. package/dist/local/transactions/mutations/mutationPersistence.js +188 -0
  214. package/dist/local/transactions/mutations/pendingDrain.d.ts +33 -0
  215. package/dist/local/transactions/mutations/pendingDrain.js +112 -0
  216. package/dist/local/transactions/mutations/processingScheduler.d.ts +14 -0
  217. package/dist/local/transactions/mutations/processingScheduler.js +24 -0
  218. package/dist/local/transactions/mutations/queueCoalescing.d.ts +13 -0
  219. package/dist/local/transactions/mutations/queueCoalescing.js +35 -0
  220. package/dist/local/transactions/mutations/replayValidation.d.ts +187 -0
  221. package/dist/local/transactions/mutations/replayValidation.js +164 -0
  222. package/dist/local/transactions/reconnectDrain.d.ts +11 -0
  223. package/dist/local/transactions/reconnectDrain.js +13 -0
  224. package/dist/local/utils/mobxSetup.d.ts +53 -0
  225. package/dist/local/utils/mobxSetup.js +330 -0
  226. package/dist/local/views/QueryView.d.ts +79 -0
  227. package/dist/local/views/QueryView.js +218 -0
  228. package/dist/local/views/ViewRegistry.d.ts +20 -0
  229. package/dist/local/views/ViewRegistry.js +57 -0
  230. package/dist/local/views/incrementalView.d.ts +45 -0
  231. package/dist/local/views/incrementalView.js +69 -0
  232. package/dist/plugin.d.ts +285 -0
  233. package/dist/plugin.js +106 -0
  234. package/dist/presenceStream.d.ts +69 -0
  235. package/dist/presenceStream.js +200 -0
  236. package/dist/react/AbloProvider.d.ts +242 -0
  237. package/dist/react/AbloProvider.js +456 -0
  238. package/dist/react/ClientSideSuspense.d.ts +36 -0
  239. package/dist/react/ClientSideSuspense.js +17 -0
  240. package/dist/react/DefaultFallback.d.ts +24 -0
  241. package/dist/react/DefaultFallback.js +43 -0
  242. package/dist/react/context.d.ts +55 -0
  243. package/dist/react/context.js +29 -0
  244. package/dist/react/createAbloReact.d.ts +50 -0
  245. package/dist/react/createAbloReact.js +48 -0
  246. package/dist/react/internalContext.d.ts +33 -0
  247. package/dist/react/internalContext.js +3 -0
  248. package/dist/react/useAblo.d.ts +96 -0
  249. package/dist/react/useAblo.js +120 -0
  250. package/dist/react/useCurrentUserId.d.ts +2 -0
  251. package/dist/react/useCurrentUserId.js +12 -0
  252. package/dist/react/useErrorListener.d.ts +2 -0
  253. package/dist/react/useErrorListener.js +14 -0
  254. package/dist/react/useMutationFailureListener.d.ts +8 -0
  255. package/dist/react/useMutationFailureListener.js +19 -0
  256. package/dist/react/useMutators.d.ts +56 -0
  257. package/dist/react/useMutators.js +84 -0
  258. package/dist/react/useSyncStatus.d.ts +19 -0
  259. package/dist/react/useSyncStatus.js +37 -0
  260. package/dist/react/useUndoScope.d.ts +34 -0
  261. package/dist/react/useUndoScope.js +73 -0
  262. package/dist/react.d.ts +18 -0
  263. package/dist/react.js +14 -0
  264. package/dist/reactRuntime.d.ts +4 -0
  265. package/dist/reactRuntime.js +2 -0
  266. package/dist/surface.d.ts +36 -0
  267. package/dist/surface.js +77 -0
  268. package/dist/useReactive.d.ts +6 -0
  269. package/dist/useReactive.js +43 -0
  270. package/package.json +119 -0
  271. package/src/Ablo.ts +456 -0
  272. package/src/client.ts +374 -0
  273. package/src/core.ts +104 -0
  274. package/src/humans.ts +61 -0
  275. package/src/index.ts +40 -0
  276. package/src/local/BaseSyncedStore.ts +1991 -0
  277. package/src/local/Database.ts +2052 -0
  278. package/src/local/InstanceCache.ts +1503 -0
  279. package/src/local/LazyReferenceCollection.ts +563 -0
  280. package/src/local/Model.ts +1124 -0
  281. package/src/local/ModelRegistry.ts +762 -0
  282. package/src/local/NetworkMonitor.ts +88 -0
  283. package/src/local/RuntimeContext.ts +141 -0
  284. package/src/local/SyncClient.ts +2131 -0
  285. package/src/local/adapters/alwaysOnline.ts +20 -0
  286. package/src/local/adapters/inMemoryStorage.ts +141 -0
  287. package/src/local/client/clientPrelude.ts +112 -0
  288. package/src/local/client/consoleLogger.ts +60 -0
  289. package/src/local/client/createInternalComponents.ts +163 -0
  290. package/src/local/client/createModelProxy.ts +1390 -0
  291. package/src/local/client/modelRegistration.ts +350 -0
  292. package/src/local/client/options.ts +526 -0
  293. package/src/local/client/reactiveEngine.ts +935 -0
  294. package/src/local/client/resourceTypes.ts +37 -0
  295. package/src/local/client/schemaConfig.ts +194 -0
  296. package/src/local/client/storeCluster.ts +172 -0
  297. package/src/local/client/storeLifecycle.ts +343 -0
  298. package/src/local/client/validateAbloOptions.ts +95 -0
  299. package/src/local/client/wsMutationExecutor.ts +110 -0
  300. package/src/local/context.ts +96 -0
  301. package/src/local/coordination/ClaimLog.ts +39 -0
  302. package/src/local/interfaces/index.ts +468 -0
  303. package/src/local/localModelContract.ts +15 -0
  304. package/src/local/logPosition.ts +84 -0
  305. package/src/local/mutationPersistence.ts +7 -0
  306. package/src/local/mutators/RecordingMutation.ts +222 -0
  307. package/src/local/mutators/Transaction.ts +97 -0
  308. package/src/local/mutators/UndoManager.ts +741 -0
  309. package/src/local/mutators/defineMutators.ts +76 -0
  310. package/src/local/mutators/inverseOp.ts +83 -0
  311. package/src/local/mutators/mutateActions.ts +167 -0
  312. package/src/local/mutators/readerActions.ts +99 -0
  313. package/src/local/mutators/undoApply.ts +141 -0
  314. package/src/local/persistence.ts +16 -0
  315. package/src/local/query/QueryProcessor.ts +347 -0
  316. package/src/local/query/client.ts +197 -0
  317. package/src/local/query/types.ts +102 -0
  318. package/src/local/schema/serialize.ts +1 -0
  319. package/src/local/store/queryApi.ts +56 -0
  320. package/src/local/storeContract.ts +146 -0
  321. package/src/local/stores/DatabaseManager.ts +507 -0
  322. package/src/local/stores/ObjectStore.ts +449 -0
  323. package/src/local/stores/ObjectStoreContract.ts +48 -0
  324. package/src/local/stores/StoreManager.ts +388 -0
  325. package/src/local/stores/SyncActionStore.ts +579 -0
  326. package/src/local/stores/openIDBWithTimeout.ts +195 -0
  327. package/src/local/stores/persistenceCleanup.ts +43 -0
  328. package/src/local/stores/persistenceIdentity.ts +83 -0
  329. package/src/local/stores/syncAction.ts +21 -0
  330. package/src/local/stores/v1PersistenceDeletion.ts +21 -0
  331. package/src/local/sync/BootstrapFetcher.ts +1224 -0
  332. package/src/local/sync/ConnectionManager.ts +15 -0
  333. package/src/local/sync/OnDemandLoader.ts +927 -0
  334. package/src/local/sync/SubscriptionManager.ts +300 -0
  335. package/src/local/sync/SyncWebSocket.ts +584 -0
  336. package/src/local/sync/bootstrapApply.ts +130 -0
  337. package/src/local/sync/commitFrames.ts +16 -0
  338. package/src/local/sync/connectionManagerLifecycle.ts +158 -0
  339. package/src/local/sync/contextPorts.ts +37 -0
  340. package/src/local/sync/createClaimStream.ts +668 -0
  341. package/src/local/sync/createSnapshot.ts +160 -0
  342. package/src/local/sync/credentialLifecycle.ts +18 -0
  343. package/src/local/sync/deltaPipeline.ts +473 -0
  344. package/src/local/sync/groupChange.ts +343 -0
  345. package/src/local/sync/initialize.ts +205 -0
  346. package/src/local/sync/participants.ts +564 -0
  347. package/src/local/sync/persistedPrefix.ts +27 -0
  348. package/src/local/sync/reconnect.ts +88 -0
  349. package/src/local/sync/schemaDrift.ts +86 -0
  350. package/src/local/sync/schemas.ts +118 -0
  351. package/src/local/sync/socketEventWiring.ts +196 -0
  352. package/src/local/sync/syncCursor.ts +62 -0
  353. package/src/local/sync/syncPlan.ts +89 -0
  354. package/src/local/sync/terminalSessionLifecycle.ts +66 -0
  355. package/src/local/sync/wsFrameHandlers.ts +20 -0
  356. package/src/local/transactions/databaseCommitOutbox.ts +33 -0
  357. package/src/local/transactions/localMutation.ts +58 -0
  358. package/src/local/transactions/mutations/MutationQueue.ts +1998 -0
  359. package/src/local/transactions/mutations/MutationStore.ts +65 -0
  360. package/src/local/transactions/mutations/UnconfirmedWrites.ts +133 -0
  361. package/src/local/transactions/mutations/batchProcessing.ts +469 -0
  362. package/src/local/transactions/mutations/coalesceRules.ts +192 -0
  363. package/src/local/transactions/mutations/commitApi.ts +97 -0
  364. package/src/local/transactions/mutations/commitLane.ts +191 -0
  365. package/src/local/transactions/mutations/commitLatency.ts +164 -0
  366. package/src/local/transactions/mutations/commitPayload.ts +281 -0
  367. package/src/local/transactions/mutations/commitTransport.ts +174 -0
  368. package/src/local/transactions/mutations/deltaConfirmation.ts +298 -0
  369. package/src/local/transactions/mutations/durableCommitRestore.ts +128 -0
  370. package/src/local/transactions/mutations/durableWriteStore.ts +21 -0
  371. package/src/local/transactions/mutations/executionSelection.ts +42 -0
  372. package/src/local/transactions/mutations/failureHandling.ts +154 -0
  373. package/src/local/transactions/mutations/failurePolicy.ts +61 -0
  374. package/src/local/transactions/mutations/localMutation.ts +135 -0
  375. package/src/local/transactions/mutations/modelOperations.ts +210 -0
  376. package/src/local/transactions/mutations/mutationPersistence.ts +231 -0
  377. package/src/local/transactions/mutations/pendingDrain.ts +160 -0
  378. package/src/local/transactions/mutations/processingScheduler.ts +35 -0
  379. package/src/local/transactions/mutations/queueCoalescing.ts +45 -0
  380. package/src/local/transactions/mutations/replayValidation.ts +192 -0
  381. package/src/local/transactions/reconnectDrain.ts +24 -0
  382. package/src/local/utils/mobxSetup.ts +388 -0
  383. package/src/local/views/QueryView.ts +311 -0
  384. package/src/local/views/ViewRegistry.ts +61 -0
  385. package/src/local/views/incrementalView.ts +92 -0
  386. package/src/plugin.ts +396 -0
  387. package/src/presenceStream.ts +279 -0
  388. package/src/react/AbloProvider.tsx +744 -0
  389. package/src/react/ClientSideSuspense.tsx +57 -0
  390. package/src/react/DefaultFallback.tsx +60 -0
  391. package/src/react/context.ts +89 -0
  392. package/src/react/createAbloReact.ts +116 -0
  393. package/src/react/internalContext.ts +38 -0
  394. package/src/react/useAblo.ts +280 -0
  395. package/src/react/useCurrentUserId.ts +17 -0
  396. package/src/react/useErrorListener.ts +22 -0
  397. package/src/react/useMutationFailureListener.ts +34 -0
  398. package/src/react/useMutators.ts +184 -0
  399. package/src/react/useSyncStatus.ts +42 -0
  400. package/src/react/useUndoScope.ts +143 -0
  401. package/src/react.ts +69 -0
  402. package/src/reactRuntime.ts +10 -0
  403. package/src/surface.ts +106 -0
  404. package/src/useReactive.ts +51 -0
@@ -0,0 +1,95 @@
1
+ import { AbloIdempotencyError, AbloValidationError } from '@abloatai/transaction/errors';
2
+ import { durableCommitEnvelopeSchema } from '@abloatai/transaction/transactions/settlement/commitEnvelope';
3
+ export async function restoreDurableCommits(ctx) {
4
+ if (!ctx.config.enablePersistence)
5
+ return new Set();
6
+ const sourceMutationIds = new Set();
7
+ try {
8
+ if (!ctx.commitOutbox)
9
+ return sourceMutationIds;
10
+ const rows = await ctx.commitOutbox.list();
11
+ const envelopes = [];
12
+ for (const row of rows) {
13
+ if (typeof row !== 'object' ||
14
+ row === null ||
15
+ row.type !== 'commit_envelope')
16
+ continue;
17
+ const parsed = durableCommitEnvelopeSchema.safeParse(row);
18
+ if (parsed.success) {
19
+ envelopes.push(parsed.data);
20
+ }
21
+ else {
22
+ ctx.runtime.logger.warn('A saved local write is unreadable and was held for review.');
23
+ ctx.runtime.observability.captureMutationFailure({
24
+ context: 'restore-commit-envelope',
25
+ error: parsed.error,
26
+ });
27
+ throw new AbloValidationError('A saved commit envelope is unreadable; replay stopped before newer writes were sent.', { code: 'write_options_invalid', cause: parsed.error });
28
+ }
29
+ }
30
+ envelopes.sort((a, b) => (a.sequence ?? a.sealedAt * 1_000) -
31
+ (b.sequence ?? b.sealedAt * 1_000) ||
32
+ a.id.localeCompare(b.id));
33
+ for (const envelope of envelopes) {
34
+ for (const mutationId of envelope.sourceMutationIds) {
35
+ sourceMutationIds.add(mutationId);
36
+ }
37
+ if (envelope.acceptedAt === undefined &&
38
+ Date.now() - envelope.sealedAt >=
39
+ ctx.durableReplayWindowMs) {
40
+ ctx.runtime.logger.warn('A saved local write is too old to retry safely and was held for review.');
41
+ ctx.runtime.observability.captureMutationFailure({
42
+ context: 'quarantine-expired-commit-envelope',
43
+ error: `Envelope ${envelope.idempotencyKey} is too old to replay safely`,
44
+ });
45
+ throw new AbloIdempotencyError('A saved commit is older than the server idempotency window and cannot be replayed safely.', { code: 'idempotency_conflict' });
46
+ }
47
+ if (ctx.commitOutboxScope &&
48
+ (!envelope.scope || // eslint-disable-line @typescript-eslint/prefer-optional-chain -- missing scope must quarantine
49
+ envelope.scope.organizationId !== ctx.commitOutboxScope.organizationId ||
50
+ envelope.scope.participantId !== ctx.commitOutboxScope.participantId ||
51
+ envelope.scope.namespace !== ctx.commitOutboxScope.namespace)) {
52
+ ctx.runtime.logger.warn('A saved local write belongs to a different account or server and was held for review.');
53
+ continue;
54
+ }
55
+ if (ctx.commitStore.has(envelope.idempotencyKey))
56
+ continue;
57
+ const transaction = {
58
+ id: envelope.idempotencyKey,
59
+ kind: 'commit',
60
+ operations: envelope.operations.map((operation) => ({ ...operation })),
61
+ ...(envelope.commitOptions.reads
62
+ ? { reads: [...envelope.commitOptions.reads] }
63
+ : {}),
64
+ ...(envelope.commitOptions.track
65
+ ? { track: [...envelope.commitOptions.track] }
66
+ : {}),
67
+ status: 'pending',
68
+ createdAt: envelope.createdAt,
69
+ sealedAt: envelope.sealedAt,
70
+ sequence: envelope.sequence ?? envelope.sealedAt * 1_000,
71
+ attempts: 0,
72
+ ...(envelope.correlationId
73
+ ? { correlationId: envelope.correlationId }
74
+ : {}),
75
+ sourceMutationIds: [...envelope.sourceMutationIds],
76
+ durableEnvelope: envelope,
77
+ };
78
+ ctx.commitStore.set(transaction.id, transaction);
79
+ ctx.commitLane.push(transaction);
80
+ }
81
+ if (ctx.commitLane.length > 0)
82
+ void ctx.processCommitLane();
83
+ }
84
+ catch (error) {
85
+ ctx.runtime.logger.debug('[MutationQueue] Failed to restore durable writes', {
86
+ error: error instanceof Error ? error.message : String(error),
87
+ });
88
+ ctx.runtime.observability.captureMutationFailure({
89
+ context: 'restore-commit-envelopes',
90
+ error: error instanceof Error ? error : String(error),
91
+ });
92
+ throw error;
93
+ }
94
+ return sourceMutationIds;
95
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The durable-write port moved to the settlement core (ADR 0016): it is a
3
+ * contract over commit envelopes and holds no local rows. Re-exported here so
4
+ * the existing `transactions/mutations/durableWriteStore` import path keeps
5
+ * resolving for the queue, the outbox, and the client options.
6
+ *
7
+ * The port and its config live in the core's `durableWrites` module (a behavior
8
+ * contract, not a persisted shape); the records that cross it are owned by
9
+ * `transactions/settlement/pendingWrite`.
10
+ */
11
+ export { durableWriteStoreSchema, durableWritesConfigSchema, } from '@abloatai/transaction/durableWrites';
12
+ export type { DurableWriteStore, DurableWritesConfig, } from '@abloatai/transaction/durableWrites';
13
+ export { pendingWriteSchema } from '@abloatai/transaction/transactions/settlement/pendingWrite';
14
+ export type { PendingWrite } from '@abloatai/transaction/transactions/settlement/pendingWrite';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The durable-write port moved to the settlement core (ADR 0016): it is a
3
+ * contract over commit envelopes and holds no local rows. Re-exported here so
4
+ * the existing `transactions/mutations/durableWriteStore` import path keeps
5
+ * resolving for the queue, the outbox, and the client options.
6
+ *
7
+ * The port and its config live in the core's `durableWrites` module (a behavior
8
+ * contract, not a persisted shape); the records that cross it are owned by
9
+ * `transactions/settlement/pendingWrite`.
10
+ */
11
+ export { durableWriteStoreSchema, durableWritesConfigSchema, } from '@abloatai/transaction/durableWrites';
12
+ export { pendingWriteSchema } from '@abloatai/transaction/transactions/settlement/pendingWrite';
@@ -0,0 +1,6 @@
1
+ import type { QueuedMutation } from './commitPayload.js';
2
+ export declare function takeNextExecutionBatch(executionQueue: QueuedMutation[], maxBatchSize: number): {
3
+ batch: QueuedMutation[];
4
+ remaining: QueuedMutation[];
5
+ };
6
+ export declare function takePendingDrainBatch(pending: QueuedMutation[], maxBatchSize: number): QueuedMutation[];
@@ -0,0 +1,42 @@
1
+ export function takeNextExecutionBatch(executionQueue, maxBatchSize) {
2
+ const retryGroups = new Map();
3
+ for (const tx of executionQueue) {
4
+ const envelope = tx.commitEnvelope;
5
+ if (!envelope)
6
+ continue;
7
+ const group = retryGroups.get(envelope.idempotencyKey) ?? new Map();
8
+ group.set(tx.id, tx);
9
+ retryGroups.set(envelope.idempotencyKey, group);
10
+ }
11
+ for (const [idempotencyKey, byId] of retryGroups) {
12
+ const members = [...byId.values()];
13
+ const expectedCount = members[0]?.commitEnvelope?.operationCount;
14
+ if (expectedCount === undefined || members.length !== expectedCount)
15
+ continue;
16
+ const remaining = executionQueue.filter((tx) => tx.commitEnvelope?.idempotencyKey !== idempotencyKey);
17
+ members.sort((a, b) => (a.commitEnvelope?.operationIndex ?? 0) - (b.commitEnvelope?.operationIndex ?? 0));
18
+ return { batch: members, remaining };
19
+ }
20
+ const fresh = executionQueue.filter((tx) => !tx.commitEnvelope);
21
+ const firstFresh = fresh[0];
22
+ if (!firstFresh)
23
+ return { batch: [], remaining: executionQueue };
24
+ const explicitIndex = fresh.findIndex((tx) => typeof tx.writeOptions?.idempotencyKey === 'string');
25
+ const selected = explicitIndex === 0
26
+ ? [firstFresh]
27
+ : fresh.slice(0, Math.min(maxBatchSize, explicitIndex > 0 ? explicitIndex : fresh.length));
28
+ const selectedIds = new Set(selected.map((tx) => tx.id));
29
+ return { batch: selected, remaining: executionQueue.filter((tx) => !selectedIds.has(tx.id)) };
30
+ }
31
+ export function takePendingDrainBatch(pending, maxBatchSize) {
32
+ const first = pending[0];
33
+ if (!first)
34
+ return [];
35
+ const envelope = first.commitEnvelope;
36
+ if (envelope)
37
+ return pending.filter((tx) => tx.commitEnvelope?.idempotencyKey === envelope.idempotencyKey);
38
+ if (typeof first.writeOptions?.idempotencyKey === 'string')
39
+ return [first];
40
+ const explicitIndex = pending.findIndex((tx) => typeof tx.writeOptions?.idempotencyKey === 'string');
41
+ return pending.slice(0, Math.min(maxBatchSize, explicitIndex > 0 ? explicitIndex : pending.length));
42
+ }
@@ -0,0 +1,16 @@
1
+ import type { RuntimeContext } from '../../RuntimeContext.js';
2
+ import type { MutationQueueConfig } from './MutationQueue.js';
3
+ import type { QueuedMutation } from './commitPayload.js';
4
+ import type { MutationStore } from './MutationStore.js';
5
+ export interface FailureHandlingContext {
6
+ readonly runtime: RuntimeContext;
7
+ readonly config: Pick<MutationQueueConfig, 'enableOptimistic' | 'maxRetries' | 'retryBackoff'>;
8
+ readonly store: MutationStore;
9
+ readonly isPermanentError: (error: Error) => boolean;
10
+ readonly rollbackOptimistic: (transaction: QueuedMutation, reason: string, error?: Error) => Promise<void>;
11
+ readonly enqueue: (transaction: QueuedMutation) => void;
12
+ readonly getLastPermanentErrorSignature: () => string | undefined;
13
+ readonly setLastPermanentErrorSignature: (signature: string) => void;
14
+ readonly emit: (event: string, payload: object) => boolean;
15
+ }
16
+ export declare function handleFailure(ctx: FailureHandlingContext, transaction: QueuedMutation, error: Error): Promise<void>;
@@ -0,0 +1,130 @@
1
+ import { AbloError } from '@abloatai/transaction/errors';
2
+ import { extractStatusCode } from './commitPayload.js';
3
+ export async function handleFailure(ctx, transaction, error) {
4
+ transaction.attempts++;
5
+ // Check whether this is a permanent error that should not be retried.
6
+ if (ctx.isPermanentError(error)) {
7
+ // Logged at warn: a permanent error means the server rejected the write,
8
+ // so the developer should see the reason in the console. The typed
9
+ // AbloError fields (`type`, `code`, `httpStatus`) are included so the
10
+ // cause is visible — for example a foreign-key violation
11
+ // (AbloValidationError) versus expired authentication
12
+ // (AbloAuthenticationError).
13
+ try {
14
+ const abloErr = error instanceof AbloError ? error : undefined;
15
+ const details = {
16
+ txId: transaction.id.slice(0, 8),
17
+ type: transaction.type,
18
+ model: transaction.modelName,
19
+ modelId: transaction.modelId.slice(0, 12),
20
+ errorType: abloErr?.type ?? error?.name,
21
+ errorCode: abloErr?.code,
22
+ httpStatus: abloErr?.httpStatus,
23
+ requestId: abloErr?.requestId,
24
+ message: error?.message,
25
+ inputKeys: transaction.data ? Object.keys(transaction.data) : undefined,
26
+ };
27
+ // A `create` whose id already exists is the benign idempotency case:
28
+ // "this row is already there." It's the least alarming permanent
29
+ // error, so it doesn't warrant a `warn` — `info` keeps it visible
30
+ // without crying wolf. Everything else (FK violation, auth expiry,
31
+ // server 500) stays at `warn`.
32
+ const isBenignIdempotent = transaction.type === 'create' &&
33
+ (abloErr?.code === 'unique_violation' ||
34
+ abloErr?.type === 'AbloIdempotencyError');
35
+ // Demote exact repeats (same write rejected for the same reason on
36
+ // each reconnect replay) to `debug` so the loop logs once.
37
+ const sig = `${details.type}:${details.model}:${details.modelId}:${details.errorCode ?? details.errorType}`;
38
+ const isRepeat = sig === ctx.getLastPermanentErrorSignature();
39
+ ctx.setLastPermanentErrorSignature(sig);
40
+ const logger = ctx.runtime.logger;
41
+ // Two registers from one call site, split by log level (the default
42
+ // logger is gated at `warn`, so `debug` stays hidden unless
43
+ // ABLO_LOG_LEVEL=debug is set to inspect the engine):
44
+ // - the default-visible line speaks the application developer's
45
+ // language: their verb (such as `update`), their model, the typed
46
+ // error's own message, and the wire `code` for searching. It uses
47
+ // no engine jargon and prints no JSON dump, which would alarm
48
+ // without helping.
49
+ // - the forensic `details` ride a companion `debug` line for anyone
50
+ // debugging the engine internals.
51
+ const revertNote = ctx.config.enableOptimistic
52
+ ? ' The local change was reverted.'
53
+ : '';
54
+ const reason = abloErr?.message ? ` — ${abloErr.message}` : '';
55
+ const code = abloErr?.code ? ` (code: ${abloErr.code})` : '';
56
+ // An optimistic write resolves before the server answers, so a later
57
+ // rejection has no caller left to return to and this log is the only
58
+ // place it appears. That reads to an application developer as their own
59
+ // save silently failing — the write showed, then vanished — and sends
60
+ // them into their editor instead of here. Name the subscription that
61
+ // hands them the same typed error, so the application can say what
62
+ // happened rather than only the console.
63
+ const channelNote = ctx.config.enableOptimistic
64
+ ? ' To surface this in your app, subscribe with `ablo.onMutationFailure(…)`.'
65
+ : '';
66
+ const headline = `Your ${transaction.type} to "${transaction.modelName}" was not saved${reason}${code}.${revertNote}${channelNote}`;
67
+ if (isRepeat) {
68
+ // Same write rejected for the same reason on each reconnect replay —
69
+ // log the forensics once, stay quiet after.
70
+ logger.debug('write rejected again (same reason)', details);
71
+ }
72
+ else if (isBenignIdempotent) {
73
+ // Already-exists on a `create` is expected on replay, not a problem.
74
+ logger.info(`Your ${transaction.type} to "${transaction.modelName}" was skipped — this row already exists.`);
75
+ logger.debug('idempotent skip — details', details);
76
+ }
77
+ else {
78
+ logger.warn(headline);
79
+ logger.debug('write rejection — details', details);
80
+ }
81
+ }
82
+ catch { }
83
+ // Mark as failed immediately and rollback
84
+ ctx.store.updateStatus(transaction.id, 'failed');
85
+ if (ctx.config.enableOptimistic) {
86
+ await ctx.rollbackOptimistic(transaction, 'permanent_error', error);
87
+ }
88
+ ctx.emit('transaction:failed', { transaction, error, permanent: true });
89
+ // The id-suffixed event is what `waitForConfirmation` (the
90
+ // `wait:'confirmed'` path) listens on — without it a permanently
91
+ // rejected write left the caller's promise hanging forever.
92
+ ctx.emit(`transaction:failed:${transaction.id}`, { error });
93
+ return;
94
+ }
95
+ if (transaction.attempts < ctx.config.maxRetries) {
96
+ // Exponential backoff with full jitter on every transient retry:
97
+ // `sleep = random(0, min(cap, base * 2^attempt))`. Throttling responses
98
+ // (429/503) use a longer base than other transient errors. The re-enqueue
99
+ // is scheduled rather than awaited, so one backing-off transaction cannot
100
+ // stall unrelated commits.
101
+ const { baseMs, capMs } = ctx.config.retryBackoff;
102
+ let base = baseMs;
103
+ try {
104
+ const status = extractStatusCode(error);
105
+ if (status === 429 || status === 503)
106
+ base = Math.max(baseMs, 1_000);
107
+ }
108
+ catch { }
109
+ const ceiling = Math.min(capMs, base * Math.pow(2, transaction.attempts - 1));
110
+ const delay = Math.floor(Math.random() * ceiling);
111
+ ctx.store.updateStatus(transaction.id, 'pending');
112
+ setTimeout(() => {
113
+ // The queue may have shut down or the tx may have been settled
114
+ // (e.g. delta-confirmed) while we backed off.
115
+ if (ctx.store.get(transaction.id)?.status !== 'pending')
116
+ return;
117
+ ctx.enqueue(transaction);
118
+ }, delay);
119
+ }
120
+ else {
121
+ // Mark as failed and rollback
122
+ ctx.store.updateStatus(transaction.id, 'failed');
123
+ if (ctx.config.enableOptimistic) {
124
+ await ctx.rollbackOptimistic(transaction, 'max_retries_exhausted', error);
125
+ }
126
+ ctx.emit('transaction:failed', { transaction, error });
127
+ // Settle `waitForConfirmation` waiters (see the permanent branch above).
128
+ ctx.emit(`transaction:failed:${transaction.id}`, { error });
129
+ }
130
+ }
@@ -0,0 +1,13 @@
1
+ import type { MutationInput, QueuedMutation } from './commitPayload.js';
2
+ import type { MutationQueueConfig } from './MutationQueue.js';
3
+ import type { MutationStore } from './MutationStore.js';
4
+ export declare function isPermanentError(error: Error): boolean;
5
+ export declare function isDefinitiveRejection(error: Error): boolean;
6
+ export interface ConflictPolicyContext {
7
+ readonly config: Pick<MutationQueueConfig, 'conflictResolution'>;
8
+ readonly store: MutationStore;
9
+ readonly rollbackOptimistic: (transaction: QueuedMutation, reason: string) => Promise<void>;
10
+ readonly mergeData: (local: MutationInput | undefined, remote: MutationInput) => MutationInput;
11
+ readonly enqueue: (transaction: QueuedMutation) => void;
12
+ }
13
+ export declare function handleConflict(ctx: ConflictPolicyContext, transaction: QueuedMutation, serverData: MutationInput): Promise<void>;
@@ -0,0 +1,48 @@
1
+ import { asTransportError, extractStatusCode } from './commitPayload.js';
2
+ import { mergeUpdateData } from './coalesceRules.js';
3
+ import { AbloConnectionError, errorCodeSpec } from '@abloatai/transaction/errors';
4
+ export function isPermanentError(error) {
5
+ if (error instanceof AbloConnectionError)
6
+ return false;
7
+ const code = error.code;
8
+ const spec = code ? errorCodeSpec(code) : undefined;
9
+ if (spec)
10
+ return !spec.retryable;
11
+ const message = error.message.toLowerCase();
12
+ if (['failed to fetch', 'network error', 'networkerror', 'connection refused', 'connection reset', 'timeout', 'econnrefused', 'econnreset', 'etimedout', 'socket hang up'].some((value) => message.includes(value)))
13
+ return false;
14
+ const status = extractStatusCode(error);
15
+ if (status !== undefined)
16
+ return status < 500 && status !== 429;
17
+ return !(asTransportError(error).response?.errors?.length);
18
+ }
19
+ export function isDefinitiveRejection(error) {
20
+ const code = error.code;
21
+ const spec = code ? errorCodeSpec(code) : undefined;
22
+ if (spec)
23
+ return !spec.retryable;
24
+ const status = extractStatusCode(error);
25
+ return status !== undefined && status >= 400 && status < 500 && status !== 429;
26
+ }
27
+ export async function handleConflict(ctx, transaction, serverData) {
28
+ const { strategy, resolver } = ctx.config.conflictResolution;
29
+ switch (strategy) {
30
+ case 'last-write-wins':
31
+ ctx.store.updateStatus(transaction.id, 'rolled_back');
32
+ await ctx.rollbackOptimistic(transaction, 'conflict_server_wins');
33
+ break;
34
+ case 'merge':
35
+ transaction.data = ctx.mergeData(transaction.data, serverData);
36
+ ctx.enqueue(transaction);
37
+ break;
38
+ case 'reject':
39
+ ctx.enqueue(transaction);
40
+ break;
41
+ case 'custom':
42
+ if (resolver) {
43
+ transaction.data = resolver(transaction.data, serverData);
44
+ ctx.enqueue(transaction);
45
+ }
46
+ break;
47
+ }
48
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Local-first apply and rollback bookkeeping for pending mutations.
3
+ *
4
+ * When a change is written before the server confirms it, these functions
5
+ * record it in a ledger and emit an `optimistic:*` event; separate listeners
6
+ * apply the change to the in-memory data and, on rollback, restore the value
7
+ * it replaced. Each function takes the ledger and a small event emitter rather
8
+ * than any larger object, so the rules have no dependencies of their own and
9
+ * can be tested in isolation.
10
+ */
11
+ import type { LocalModel as Model } from '../../localModelContract.js';
12
+ import type { MutationInput, QueuedMutation } from './commitPayload.js';
13
+ /**
14
+ * One tracked optimistic mutation: the live model plus the value it held
15
+ * before the change, kept so a rollback can restore it.
16
+ */
17
+ export interface OptimisticUpdateEntry {
18
+ model: Model;
19
+ previousState: MutationInput | null | undefined;
20
+ transaction: QueuedMutation;
21
+ }
22
+ /**
23
+ * The event emitter these functions use to announce each optimistic change and
24
+ * its rollback. You supply the implementation.
25
+ */
26
+ export interface OptimisticEmitter {
27
+ emit(event: string, payload: unknown): void;
28
+ }
29
+ /** Materializer capability consumed by settlement; implementation belongs to the local layer. */
30
+ export interface LocalMutationPort {
31
+ readonly updates: Map<string, OptimisticUpdateEntry>;
32
+ applyCreate(model: Model, transaction: QueuedMutation): void;
33
+ applyUpdate(model: Model, transaction: QueuedMutation): void;
34
+ applyDelete(model: Model, transaction: QueuedMutation): void;
35
+ rollback(transaction: QueuedMutation, reason?: string, error?: Error): Promise<void>;
36
+ }
37
+ export declare function createLocalMutationPort(emitter: OptimisticEmitter): LocalMutationPort;
38
+ /**
39
+ * Record an optimistic create and announce it. There is no prior value to
40
+ * restore, so the rollback pre-image is `null`.
41
+ */
42
+ export declare function applyOptimisticCreate(optimisticUpdates: Map<string, OptimisticUpdateEntry>, emitter: OptimisticEmitter, model: Model, transaction: QueuedMutation): void;
43
+ /**
44
+ * Record an optimistic update and announce it, keeping the row's prior value so
45
+ * a rollback can put it back.
46
+ */
47
+ export declare function applyOptimisticUpdate(optimisticUpdates: Map<string, OptimisticUpdateEntry>, emitter: OptimisticEmitter, model: Model, transaction: QueuedMutation): void;
48
+ /**
49
+ * Record an optimistic delete and announce it, keeping the deleted row so a
50
+ * rollback can restore it.
51
+ */
52
+ export declare function applyOptimisticDelete(optimisticUpdates: Map<string, OptimisticUpdateEntry>, emitter: OptimisticEmitter, model: Model, transaction: QueuedMutation): void;
53
+ /**
54
+ * Undo an optimistic mutation by its transaction id: emit `optimistic:rollback`
55
+ * with the saved pre-image so listeners can restore the prior value, then drop
56
+ * the ledger entry. Does nothing if the transaction was never tracked.
57
+ */
58
+ export declare function rollbackOptimistic(optimisticUpdates: Map<string, OptimisticUpdateEntry>, emitter: OptimisticEmitter, transaction: QueuedMutation, reason?: string, error?: Error): Promise<void>;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Local-first apply and rollback bookkeeping for pending mutations.
3
+ *
4
+ * When a change is written before the server confirms it, these functions
5
+ * record it in a ledger and emit an `optimistic:*` event; separate listeners
6
+ * apply the change to the in-memory data and, on rollback, restore the value
7
+ * it replaced. Each function takes the ledger and a small event emitter rather
8
+ * than any larger object, so the rules have no dependencies of their own and
9
+ * can be tested in isolation.
10
+ */
11
+ export function createLocalMutationPort(emitter) {
12
+ const updates = new Map();
13
+ return {
14
+ updates,
15
+ applyCreate: (model, transaction) => applyOptimisticCreate(updates, emitter, model, transaction),
16
+ applyUpdate: (model, transaction) => applyOptimisticUpdate(updates, emitter, model, transaction),
17
+ applyDelete: (model, transaction) => applyOptimisticDelete(updates, emitter, model, transaction),
18
+ rollback: (transaction, reason, error) => rollbackOptimistic(updates, emitter, transaction, reason, error),
19
+ };
20
+ }
21
+ /**
22
+ * Record an optimistic create and announce it. There is no prior value to
23
+ * restore, so the rollback pre-image is `null`.
24
+ */
25
+ export function applyOptimisticCreate(optimisticUpdates, emitter, model, transaction) {
26
+ optimisticUpdates.set(transaction.id, {
27
+ model,
28
+ previousState: null,
29
+ transaction,
30
+ });
31
+ emitter.emit('optimistic:create', { model, transaction });
32
+ }
33
+ /**
34
+ * Record an optimistic update and announce it, keeping the row's prior value so
35
+ * a rollback can put it back.
36
+ */
37
+ export function applyOptimisticUpdate(optimisticUpdates, emitter, model, transaction) {
38
+ optimisticUpdates.set(transaction.id, {
39
+ model,
40
+ previousState: transaction.previousData,
41
+ transaction,
42
+ });
43
+ emitter.emit('optimistic:update', { model, transaction });
44
+ }
45
+ /**
46
+ * Record an optimistic delete and announce it, keeping the deleted row so a
47
+ * rollback can restore it.
48
+ */
49
+ export function applyOptimisticDelete(optimisticUpdates, emitter, model, transaction) {
50
+ optimisticUpdates.set(transaction.id, {
51
+ model,
52
+ previousState: transaction.previousData,
53
+ transaction,
54
+ });
55
+ emitter.emit('optimistic:delete', { model, transaction });
56
+ }
57
+ /**
58
+ * Undo an optimistic mutation by its transaction id: emit `optimistic:rollback`
59
+ * with the saved pre-image so listeners can restore the prior value, then drop
60
+ * the ledger entry. Does nothing if the transaction was never tracked.
61
+ */
62
+ export function rollbackOptimistic(optimisticUpdates, emitter, transaction, reason, error) {
63
+ const optimistic = optimisticUpdates.get(transaction.id);
64
+ if (!optimistic)
65
+ return Promise.resolve();
66
+ emitter.emit('optimistic:rollback', {
67
+ model: optimistic.model,
68
+ previousState: optimistic.previousState,
69
+ transaction,
70
+ reason: reason ?? 'unknown',
71
+ error,
72
+ });
73
+ optimisticUpdates.delete(transaction.id);
74
+ return Promise.resolve();
75
+ }
@@ -0,0 +1,44 @@
1
+ import type { WriteOptions } from '../../interfaces/index.js';
2
+ import type { LocalModel as Model } from '../../localModelContract.js';
3
+ import type { QueuedMutation, UserContext, MutationInput } from './commitPayload.js';
4
+ export interface ModelMutationContext {
5
+ readonly enableOptimistic: boolean;
6
+ readonly persistenceReady: boolean;
7
+ readonly assertDurableReplayOpen: () => void;
8
+ readonly generateId: () => string;
9
+ readonly normalizeModelKey: (modelName: string) => string;
10
+ readonly computePriorityScore: (type: QueuedMutation['type'], modelName: string) => number;
11
+ readonly extractCreateData: (model: Model) => Record<string, unknown>;
12
+ readonly extractUpdateData: (model: Model) => MutationInput;
13
+ readonly extractPreviousData: (model: Model, input: MutationInput) => Record<string, unknown> | null;
14
+ readonly mapChangesToInput: (modelName: string, changes: Record<string, unknown>) => MutationInput;
15
+ readonly isReorderPayload: (input: MutationInput) => boolean;
16
+ readonly attachConfirmation: (transaction: QueuedMutation) => void;
17
+ readonly add: (transaction: QueuedMutation) => void;
18
+ readonly applyOptimisticCreate: (model: Model, transaction: QueuedMutation) => void;
19
+ readonly applyOptimisticUpdate: (model: Model, transaction: QueuedMutation) => void;
20
+ readonly applyOptimisticDelete: (model: Model, transaction: QueuedMutation) => void;
21
+ readonly takeUnsentCreateForModel: (modelName: string, modelId: string) => QueuedMutation | undefined;
22
+ readonly cancelUnsentCreateForDelete: (transaction: QueuedMutation) => Promise<void>;
23
+ readonly completeLocalDelete: (model: Model, context: UserContext, writeOptions?: WriteOptions, sourceMutationIds?: string[]) => QueuedMutation | Promise<QueuedMutation>;
24
+ readonly cancelTransactionsForModel: (modelId: string, type: 'update') => QueuedMutation[];
25
+ readonly pendingMergeByModel: Map<string, {
26
+ data: MutationInput;
27
+ sourceMutationIds: string[];
28
+ }>;
29
+ readonly inFlightByModel: Set<string>;
30
+ readonly findCreateBarrierForDelete: (modelName: string, modelId: string) => QueuedMutation | undefined;
31
+ readonly deferDeleteUntilCreateSettles: (create: QueuedMutation, transaction: QueuedMutation) => void;
32
+ readonly logger: {
33
+ debug: (message: string, fields?: Record<string, string>) => void;
34
+ };
35
+ readonly persistAndStage: (transaction: QueuedMutation, modelData: Record<string, unknown>) => Promise<void>;
36
+ readonly persistQueuedTransaction: (transaction: QueuedMutation, modelData: Record<string, unknown>) => Promise<void>;
37
+ readonly stageTransaction: (transaction: QueuedMutation) => void;
38
+ readonly emit: (event: string, payload: object) => void;
39
+ }
40
+ export declare function remove(ctx: ModelMutationContext, model: Model, context: UserContext, writeOptions?: WriteOptions, sourceMutationId?: string): Promise<QueuedMutation>;
41
+ export declare function create(ctx: ModelMutationContext, model: Model, context: UserContext, writeOptions?: WriteOptions, sourceMutationId?: string): Promise<QueuedMutation> | QueuedMutation;
42
+ export declare function update(ctx: ModelMutationContext, model: Model, context: UserContext, precomputedChanges?: Record<string, unknown>, writeOptions?: WriteOptions, sourceMutationId?: string): Promise<QueuedMutation> | QueuedMutation;
43
+ export declare function archive(ctx: ModelMutationContext, model: Model, context: UserContext, writeOptions?: WriteOptions, sourceMutationId?: string): Promise<QueuedMutation> | QueuedMutation;
44
+ export declare function unarchive(ctx: ModelMutationContext, model: Model, context: UserContext): Promise<QueuedMutation> | QueuedMutation;