@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,57 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState, type ReactNode } from 'react';
4
+ import { useSyncStatus } from './useSyncStatus.js';
5
+
6
+ /**
7
+ * Nested bootstrap gate for a subtree. `<AbloProvider>` already ships
8
+ * its own built-in gate (via its `fallback` prop) that handles the
9
+ * common "wait for first bootstrap" case. Use `ClientSideSuspense`
10
+ * only when you need a SEPARATE gate inside an already-ready provider —
11
+ * for example, rendering app chrome immediately while gating a single
12
+ * heavy product surface on its own query resolving.
13
+ *
14
+ * Like the provider-level gate, this component latches open on the
15
+ * first `connected` / `reconnecting` / `disconnected` transition and
16
+ * stays open. Subsequent transient `connecting` states (hard reconnect
17
+ * after offline) do NOT re-show the fallback — the app has already
18
+ * rendered once and its own reconnect UI should take over.
19
+ *
20
+ * v0.3.x implementation is non-Suspense: reads `useSyncStatus()` and
21
+ * conditionally renders. v0.3.x+ will ship a
22
+ * the React suspense entry point where `useQuery` / `useOne`
23
+ * actually throw Promises; this component becomes a thin wrapper around
24
+ * React's real `<Suspense>` at that point.
25
+ *
26
+ * @example
27
+ * <AbloProvider fallback={<AppSkeleton />}>
28
+ * <AppChrome />
29
+ * <ClientSideSuspense fallback={<CanvasSkeleton />}>
30
+ * <HeavyCanvas />
31
+ * </ClientSideSuspense>
32
+ * </AbloProvider>
33
+ */
34
+ export interface ClientSideSuspenseProps {
35
+ /** What to render while the nested subtree is waiting for first bootstrap. */
36
+ fallback: ReactNode;
37
+ /** What to render once the subtree is cleared to render. */
38
+ children: ReactNode;
39
+ }
40
+
41
+ export function ClientSideSuspense({ fallback, children }: ClientSideSuspenseProps) {
42
+ const status = useSyncStatus();
43
+ const [everConnected, setEverConnected] = useState(false);
44
+
45
+ useEffect(() => {
46
+ if (
47
+ status.name === 'connected' ||
48
+ status.name === 'reconnecting' ||
49
+ status.name === 'disconnected'
50
+ ) {
51
+ setEverConnected(true);
52
+ }
53
+ }, [status.name]);
54
+
55
+ const showFallback = !everConnected && status.name === 'connecting';
56
+ return <>{showFallback ? fallback : children}</>;
57
+ }
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Neutral loading placeholder — the default value of `<AbloProvider>`'s
5
+ * `fallback` prop. Rendered during the first bootstrap pass when the
6
+ * consumer hasn't supplied their own skeleton.
7
+ *
8
+ * Design goals:
9
+ * - Zero design-system dependency. Inline styles only; no CSS file,
10
+ * no UI-lib imports, no Tailwind assumptions.
11
+ * - Theme-adaptive. Uses `currentColor` for the ring so the spinner
12
+ * inherits the text color from whichever ancestor defines it —
13
+ * works in light + dark contexts without a prop.
14
+ * - Self-centering. Flex-centered in a full-parent container so the
15
+ * common case (provider at the layout root) renders a spinner in
16
+ * the middle of the viewport. Consumers who need different
17
+ * positioning compose their own fallback and pass it explicitly.
18
+ * - Minimal bundle footprint. The whole component + keyframe is ~50
19
+ * bytes gzipped.
20
+ *
21
+ * Consumers wanting a branded loader should pass `fallback={<YourSkeleton />}`
22
+ * on `<AbloProvider>`. Consumers wanting NO visual during bootstrap
23
+ * pass `fallback={null}`. Consumers who want to skip the gate entirely
24
+ * pass `fallback="passthrough"`.
25
+ */
26
+ export function DefaultFallback() {
27
+ return (
28
+ <div
29
+ role="status"
30
+ aria-live="polite"
31
+ aria-label="Loading"
32
+ style={{
33
+ display: 'flex',
34
+ alignItems: 'center',
35
+ justifyContent: 'center',
36
+ width: '100%',
37
+ minHeight: '100vh',
38
+ color: 'currentColor',
39
+ }}
40
+ >
41
+ <div
42
+ style={{
43
+ width: 24,
44
+ height: 24,
45
+ border: '2px solid currentColor',
46
+ borderTopColor: 'transparent',
47
+ borderRadius: '50%',
48
+ opacity: 0.6,
49
+ animation: 'ablo-default-fallback-spin 0.8s linear infinite',
50
+ }}
51
+ />
52
+ {/*
53
+ Keyframe ships inline so the component has zero external-CSS
54
+ dependencies. Name is prefixed so it can't collide with
55
+ consumer-defined animations.
56
+ */}
57
+ <style>{`@keyframes ablo-default-fallback-spin { to { transform: rotate(360deg); } }`}</style>
58
+ </div>
59
+ );
60
+ }
@@ -0,0 +1,89 @@
1
+ 'use client';
2
+
3
+ import { createContext, createElement, useContext, type ReactNode } from 'react';
4
+ import type { Schema } from '@abloatai/transaction/schema/schema';
5
+ import type { SyncStoreContract } from '../local/storeContract.js';
6
+ import { AbloValidationError } from '@abloatai/transaction/errors';
7
+
8
+ // `SyncStoreContract` and `LocalMutation` are defined in a React-free module,
9
+ // so code that never touches React can still implement the store. They are
10
+ // re-exported here for the convenience of React consumers.
11
+ export type {
12
+ SyncStoreContract,
13
+ LocalMutation,
14
+ } from '../local/storeContract.js';
15
+
16
+ export interface SyncReactContext {
17
+ store: SyncStoreContract;
18
+ /** The organization id used as the default scope for reads and writes. */
19
+ organizationId: string;
20
+ /**
21
+ * An optional schema. When provided, hooks that take a model by name (such as
22
+ * `useQuery('tasks')`) read that model's metadata from this schema, so
23
+ * callers don't pass a schema at every call site. When omitted, those hooks
24
+ * require the schema as an argument instead.
25
+ *
26
+ * The field is loosely typed here because a single runtime context value is
27
+ * shared by every hook. Precise per-model types come from your `Register`
28
+ * module augmentation
29
+ * (`declare module '@abloatai/ablo' { interface Register { Schema: typeof schema } }`),
30
+ * not from this reference.
31
+ */
32
+ schema?: Schema;
33
+ }
34
+
35
+ export const SyncContext = createContext<SyncReactContext | null>(null);
36
+
37
+ /**
38
+ * Reads the sync store context from inside a provider subtree, throwing a clear
39
+ * error when no provider is mounted above. `<AbloProvider>` supplies this
40
+ * context by rendering the internal {@link SyncProvider}; you wire
41
+ * `<AbloProvider client={ablo}>` rather than touching this directly.
42
+ */
43
+ export function useSyncContext(): SyncReactContext {
44
+ const ctx = useContext(SyncContext);
45
+ if (!ctx) {
46
+ throw new AbloValidationError('Sync hooks must be used within an <AbloProvider>.', {
47
+ code: 'sync_context_missing_provider',
48
+ });
49
+ }
50
+ return ctx;
51
+ }
52
+
53
+ /**
54
+ * Props for SyncProvider.
55
+ */
56
+ export interface SyncProviderProps {
57
+ /** The sync store, which must implement {@link SyncStoreContract}. */
58
+ store: SyncStoreContract;
59
+ /** The organization id used as the default scope for reads and writes. */
60
+ organizationId: string;
61
+ /**
62
+ * An optional schema. Provide it to enable hooks that take a model by name
63
+ * (such as `useQuery('tasks')`); the model types also narrow through your
64
+ * `Register` augmentation. Omit it to pass the schema to those hooks directly
65
+ * instead.
66
+ */
67
+ schema?: Schema;
68
+ children?: ReactNode;
69
+ }
70
+
71
+ /**
72
+ * A low-level provider that places a built sync store on React context so the
73
+ * data hooks can reach it. This is an internal building block: it is not part
74
+ * of the package's public entry point. Reach for `<AbloProvider>` instead,
75
+ * which builds the store from your `Ablo({ schema, apiKey })` client and
76
+ * renders this provider underneath.
77
+ */
78
+ export function SyncProvider({
79
+ store,
80
+ organizationId,
81
+ schema,
82
+ children,
83
+ }: SyncProviderProps) {
84
+ return createElement(
85
+ SyncContext.Provider,
86
+ { value: { store, organizationId, schema } },
87
+ children
88
+ );
89
+ }
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * The typed react binding — the schema generic is captured ONCE, at a factory
5
+ * call in app code, and every hook the factory returns is born typed. This is
6
+ * a schema-bound shape with no module augmentation or generic parameters at call sites,
7
+ * and — once the legacy generic erasure retires — no casts anywhere on the
8
+ * path from context to component.
9
+ *
10
+ * The app's one binding file, by convention:
11
+ *
12
+ * ```ts
13
+ * // lib/ablo.ts
14
+ * import { createAbloReact } from '@abloatai/ablo/react';
15
+ * import { schema } from './schema';
16
+ *
17
+ * export const { AbloProvider, useAblo } = createAbloReact(schema);
18
+ * ```
19
+ *
20
+ * Components then import `useAblo` from `lib/ablo` and never spell a type
21
+ * argument; `useAblo()` is `Ablo<S> | null`, and a selector's `ablo`
22
+ * parameter is the reactive-read view of the same `S`.
23
+ */
24
+
25
+ import { createContext, createElement, useContext, type ReactElement } from 'react';
26
+ import {
27
+ AbloProvider,
28
+ type AbloProviderProps,
29
+ } from './AbloProvider.js';
30
+ import {
31
+ useAbloImpl,
32
+ type AbloSelector,
33
+ type ModelClientSelector,
34
+ type UseAbloHydratedModelResult,
35
+ type UseAbloModelOptions,
36
+ type UseAbloModelResult,
37
+ } from './useAblo.js';
38
+ import type { AbloClient as Ablo } from '../client.js';
39
+ import type { ModelOperations } from '../local/client/createModelProxy.js';
40
+ import type { Schema, SchemaRecord } from '@abloatai/transaction/schema/schema';
41
+
42
+ /** What a binding returns: the provider and the hook, with `S` fixed. */
43
+ export interface AbloReactBinding<S extends SchemaRecord> {
44
+ /** `AbloProvider` with its `client` prop typed `Ablo<S>` — same component,
45
+ * no per-app generics. */
46
+ AbloProvider: (props: AbloProviderProps<S>) => ReactElement;
47
+ /** `useAblo` with the schema bound — the same overloads as the global
48
+ * hook, minus the type arguments. */
49
+ useAblo: {
50
+ (): Ablo<S> | null;
51
+ <T>(select: AbloSelector<S, T>): T | undefined;
52
+ <T, C>(
53
+ modelClientOrSelect: ModelOperations<T, C> | ModelClientSelector<S, T, C>,
54
+ id: string,
55
+ options: UseAbloModelOptions<T> & { readonly initial: T },
56
+ ): UseAbloHydratedModelResult<T>;
57
+ <T, C>(
58
+ modelClientOrSelect: ModelOperations<T, C> | ModelClientSelector<S, T, C>,
59
+ id: string,
60
+ options?: UseAbloModelOptions<T>,
61
+ ): UseAbloModelResult<T>;
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Bind the react surface to one schema. The schema value is taken for
67
+ * inference — write `createAbloReact(schema)`, never a hand-spelled type
68
+ * argument — and it is the seam where the binding's own typed context arrives
69
+ * when the legacy erasure retires (docs/plans/typed-react-binding.md, step 3).
70
+ */
71
+ export function createAbloReact<S extends SchemaRecord>(
72
+ schema: Schema<S>,
73
+ ): AbloReactBinding<S> {
74
+ void schema;
75
+
76
+ // The binding's own context — created here, AFTER the schema generic is
77
+ // known, so it is typed `Ablo<S>` from birth. A hook that reads it never rebinds and
78
+ // never casts; a binding hook mounted under a legacy provider (no bound
79
+ // provider in the tree) reads `null` here and falls through to the shared
80
+ // implementation's internal-context fallback.
81
+ const BoundClientContext = createContext<Ablo<S> | null>(null);
82
+
83
+ function BoundAbloProvider(props: AbloProviderProps<S>): ReactElement {
84
+ return createElement(
85
+ BoundClientContext.Provider,
86
+ { value: props.client },
87
+ createElement(AbloProvider<S>, props),
88
+ );
89
+ }
90
+
91
+ function useBoundAblo(): Ablo<S> | null;
92
+ function useBoundAblo<T>(select: AbloSelector<S, T>): T | undefined;
93
+ function useBoundAblo<T, C>(
94
+ modelClientOrSelect: ModelOperations<T, C> | ModelClientSelector<S, T, C>,
95
+ id: string,
96
+ options: UseAbloModelOptions<T> & { readonly initial: T },
97
+ ): UseAbloHydratedModelResult<T>;
98
+ function useBoundAblo<T, C>(
99
+ modelClientOrSelect: ModelOperations<T, C> | ModelClientSelector<S, T, C>,
100
+ id: string,
101
+ options?: UseAbloModelOptions<T>,
102
+ ): UseAbloModelResult<T>;
103
+ function useBoundAblo<T, C>(
104
+ modelOrSelect?:
105
+ | ModelOperations<T, C>
106
+ | ModelClientSelector<S, T, C>
107
+ | AbloSelector<S, T>,
108
+ id?: string,
109
+ options?: UseAbloModelOptions<T>,
110
+ ): Ablo<S> | null | UseAbloModelResult<T> | T | undefined {
111
+ const bound = useContext(BoundClientContext);
112
+ return useAbloImpl<S, T, C>(bound, modelOrSelect, id, options);
113
+ }
114
+
115
+ return { AbloProvider: BoundAbloProvider, useAblo: useBoundAblo };
116
+ }
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+
3
+ import { createContext } from 'react';
4
+ import type { AbloClient as Ablo } from '../client.js';
5
+ import type { SchemaRecord } from '@abloatai/transaction/schema/schema';
6
+
7
+ /**
8
+ * The context that `<AbloProvider>` populates for its own hooks. It is kept
9
+ * separate from the data-hook context, which carries the store and schema,
10
+ * because these fields belong to the provider rather than to the store. Read
11
+ * them through the typed hooks such as `useCurrentUserId` and
12
+ * `useErrorListener` rather than reaching into this context directly.
13
+ */
14
+ export interface AbloInternalContextValue {
15
+ /**
16
+ * The application user id, when your app passed one to `<AbloProvider>`. Sync
17
+ * identity is derived on the server from the API key, so this is `null`
18
+ * unless you set it, and it is not required for sync to work.
19
+ */
20
+ currentUserId: string | null;
21
+ /** Subscribe to provider-level errors: engine errors, bootstrap failures, and session issues. */
22
+ subscribeError: (listener: (error: Error) => void) => () => void;
23
+ /** Emit an error to every subscribed listener. The provider calls this for you. */
24
+ emitError: (error: Error) => void;
25
+ /**
26
+ * The typed `Ablo` client for this provider, or `null` until the first sync
27
+ * bootstrap resolves. It is held here so `useSync()` can return it without
28
+ * reaching into the store; the client and the store are sibling objects, and
29
+ * neither is derived from the other.
30
+ *
31
+ * It is typed loosely as `Ablo<SchemaRecord>` because generics do not flow
32
+ * through React context. `useSync<R>()` restores the precise type through its
33
+ * own generic; the runtime value is the fully typed client.
34
+ */
35
+ engine: Ablo<SchemaRecord> | null;
36
+ }
37
+
38
+ export const AbloInternalContext = createContext<AbloInternalContextValue | null>(null);
@@ -0,0 +1,280 @@
1
+ 'use client';
2
+
3
+ import { useContext, useEffect, useState } from 'react';
4
+ import { AbloInternalContext } from './internalContext.js';
5
+ import type { AbloClient as Ablo, AbloReads } from '../client.js';
6
+ import type { ModelClaim } from '@abloatai/transaction/coordination';
7
+ import {
8
+ getModelClientMeta,
9
+ type ModelOperations,
10
+ } from '../local/client/createModelProxy.js';
11
+ import { Model } from '../local/Model.js';
12
+ import type { SchemaRecord } from '@abloatai/transaction/schema/schema';
13
+ import type { ResolveSchema } from '@abloatai/transaction/types/global';
14
+ import { useReactive } from '../useReactive.js';
15
+
16
+ /**
17
+ * The app's resolved schema-record type. It reads your `Register` module
18
+ * augmentation when you declare one and falls back to the loose
19
+ * {@link SchemaRecord} otherwise, so `useAblo()` returns a fully typed client
20
+ * without you passing `<(typeof schema)['models']>` at every call site.
21
+ */
22
+ type DefaultModels = ResolveSchema extends { models: infer M }
23
+ ? M extends SchemaRecord
24
+ ? M
25
+ : SchemaRecord
26
+ : SchemaRecord;
27
+
28
+ const EMPTY_CLAIMS: readonly ModelClaim[] = Object.freeze([]);
29
+
30
+ /**
31
+ * Restore the caller's schema generics on the context-held engine. React
32
+ * context erases generics (see `AbloInternalContextValue.engine`), so this is
33
+ * the one deliberate rebind point: the runtime value is the fully typed
34
+ * client, and `R` is the compile-time view the calling hook declared.
35
+ */
36
+ function rebindEngine<R extends SchemaRecord>(engine: Ablo<SchemaRecord>): Ablo<R> {
37
+ return engine as Ablo<R>;
38
+ }
39
+
40
+ /**
41
+ * The reactive-read view of a client — the identical runtime object, with
42
+ * model reads typed as snapshot rows, because everything a selector returns
43
+ * is converted through `snapshotValue` before the hook hands it back. Same
44
+ * generic in and out, so this compiles with no schema rebinding.
45
+ */
46
+ function reactiveReads<R extends SchemaRecord>(engine: Ablo<R>): AbloReads<R> {
47
+ return engine as AbloReads<R>;
48
+ }
49
+
50
+ // Selectors receive the reactive-read client: model reads are typed as
51
+ // snapshot rows (data fields + computeds, no relation accessors), which is the
52
+ // shape the hook actually returns after `toReactiveSnapshot()`. This makes the
53
+ // selector's inferred result type honest — `row.layers` fails to compile here
54
+ // instead of reading `undefined` at runtime.
55
+ export type ModelClientSelector<R extends SchemaRecord, T, C> =
56
+ (ablo: AbloReads<R>) => ModelOperations<T, C>;
57
+ export type AbloSelector<R extends SchemaRecord, T> = (ablo: AbloReads<R>) => T;
58
+
59
+ export interface UseAbloModelOptions<T> {
60
+ /**
61
+ * An initial row, usually from a server component or a route loader. The hook
62
+ * returns it until sync delivers a newer row for the same id.
63
+ */
64
+ readonly initial?: T;
65
+ }
66
+
67
+ export interface UseAbloModelResult<T> {
68
+ /** The current row for the id, or `initial` until the row has synced. */
69
+ readonly data: T | undefined;
70
+ /** The work claims currently held on this row by any participant. */
71
+ readonly claims: readonly ModelClaim[];
72
+ /** True while another participant holds a claim — handy for disabling UI. */
73
+ readonly claimed: boolean;
74
+ }
75
+
76
+ export type UseAbloHydratedModelResult<T> =
77
+ Omit<UseAbloModelResult<T>, 'data'> & { readonly data: T };
78
+
79
+ function readModelResult<R extends SchemaRecord, T, C>(
80
+ engine: Ablo<R> | null,
81
+ modelClient: ModelOperations<T, C> | undefined,
82
+ id: string | undefined,
83
+ initial: T | undefined,
84
+ ): UseAbloModelResult<T> {
85
+ if (!modelClient || id === undefined) {
86
+ return { data: initial, claims: EMPTY_CLAIMS, claimed: false };
87
+ }
88
+
89
+ const data = snapshotValue(modelClient.local.get(id) ?? initial);
90
+ const meta = getModelClientMeta(modelClient);
91
+ const claims = meta && engine
92
+ ? engine.claims.list({ model: meta.key, id })
93
+ : EMPTY_CLAIMS;
94
+
95
+ return { data, claims, claimed: claims.length > 0 };
96
+ }
97
+
98
+ /**
99
+ * Projects a reactive read into the value that `useReactive` caches and
100
+ * returns.
101
+ *
102
+ * For a `Model`, this reads the row's fields through `toReactiveSnapshot`
103
+ * rather than returning the instance itself. Property access is what subscribes
104
+ * the reaction to those fields, so the read has to happen inside this tracked
105
+ * function; returning the live instance without reading its fields would leave
106
+ * the component blind to later edits. The fresh object it produces also lets
107
+ * `useReactive`'s equality check detect an in-place update.
108
+ */
109
+ function snapshotValue<T>(value: T): T {
110
+ if (value instanceof Model) {
111
+ return value.toReactiveSnapshot<T>();
112
+ }
113
+ if (Array.isArray(value)) {
114
+ return value.map((item) => snapshotValue(item)) as T;
115
+ }
116
+ return value;
117
+ }
118
+
119
+ /**
120
+ * Reads Ablo from inside an `<AbloProvider>` subtree. Called with no arguments
121
+ * it returns the typed client for use in callbacks and effects; called with a
122
+ * selector it subscribes the component to a reactive read — such as one
123
+ * `ablo.<model>` row — and re-renders when that read changes.
124
+ *
125
+ * You can call it with no type arguments once you declare the `Register` module
126
+ * augmentation (`declare module '@abloatai/ablo' { interface Register {
127
+ * Schema: typeof schema } }`); the default type then resolves through your
128
+ * schema's models, so call sites stay clean:
129
+ *
130
+ * **Prefer the binding.** `createAbloReact(schema)` captures the schema once
131
+ * in your app's binding file and returns a `useAblo` that needs none of the
132
+ * typing arrangements below — no type argument, no `Register` declaration
133
+ * (see `react.md`). Passing an explicit schema type argument to THIS hook is
134
+ * deprecated in favor of that binding; it keeps working for shared packages
135
+ * that cannot bind a concrete schema.
136
+ *
137
+ * ```ts
138
+ * // With the Register augmentation (recommended):
139
+ * const ablo = useAblo();
140
+ * if (!ablo) return <Loading />;
141
+ * const doc = await ablo.documents.get({ id }); // async server read
142
+ *
143
+ * // Reactive selector (a synchronous local snapshot). The selector's reads
144
+ * // are typed as snapshot rows — data fields + computeds, no relation
145
+ * // accessors — matching what the hook actually returns:
146
+ * const doc = useAblo((ablo) => ablo.documents.local.get(id)) ?? serverDoc;
147
+ * const active = useAblo((ablo) => ablo.documents.claim.state({ id }));
148
+ *
149
+ * // Without the augmentation, pass the schema as a type argument:
150
+ * const ablo = useAblo<(typeof schema)['models']>();
151
+ * ```
152
+ *
153
+ * The no-argument form returns `null` while the engine is still bootstrapping.
154
+ * Branch on `null` and render a loading state — or gate on `useSyncStatus()`
155
+ * reaching `'connected'` — before calling model methods.
156
+ */
157
+ export function useAblo<R extends SchemaRecord = DefaultModels>(): Ablo<R> | null;
158
+ export function useAblo<
159
+ R extends SchemaRecord = DefaultModels,
160
+ T = unknown,
161
+ >(
162
+ select: AbloSelector<R, T>,
163
+ ): T | undefined;
164
+ export function useAblo<T, C>(
165
+ modelClient: ModelOperations<T, C>,
166
+ id: string,
167
+ options: UseAbloModelOptions<T> & { readonly initial: T },
168
+ ): UseAbloHydratedModelResult<T>;
169
+ export function useAblo<
170
+ R extends SchemaRecord = DefaultModels,
171
+ T = Record<string, unknown>,
172
+ C = unknown,
173
+ >(
174
+ select: ModelClientSelector<R, T, C>,
175
+ id: string,
176
+ options: UseAbloModelOptions<T> & { readonly initial: T },
177
+ ): UseAbloHydratedModelResult<T>;
178
+ export function useAblo<T, C>(
179
+ modelClient: ModelOperations<T, C>,
180
+ id: string,
181
+ options?: UseAbloModelOptions<T>,
182
+ ): UseAbloModelResult<T>;
183
+ export function useAblo<
184
+ R extends SchemaRecord = DefaultModels,
185
+ T = Record<string, unknown>,
186
+ C = unknown,
187
+ >(
188
+ select: ModelClientSelector<R, T, C>,
189
+ id: string,
190
+ options?: UseAbloModelOptions<T>,
191
+ ): UseAbloModelResult<T>;
192
+ export function useAblo<
193
+ R extends SchemaRecord = DefaultModels,
194
+ T = Record<string, unknown>,
195
+ C = unknown,
196
+ >(
197
+ modelOrSelect?: ModelOperations<T, C> | ModelClientSelector<R, T, C> | AbloSelector<R, T>,
198
+ id?: string,
199
+ options?: UseAbloModelOptions<T>,
200
+ ): Ablo<R> | null | UseAbloModelResult<T> | T | undefined {
201
+ return useAbloImpl<R, T, C>(null, modelOrSelect, id, options);
202
+ }
203
+
204
+ /**
205
+ * @internal The one implementation behind `useAblo` and the bound hooks a
206
+ * `createAbloReact` binding returns — written once so the reactive read path
207
+ * cannot fork between the global hook and a factory's.
208
+ *
209
+ * `boundClient` is a binding's own context value — typed `Ablo<S>` at the
210
+ * factory, so that path never rebinds and never casts. `null` means "no
211
+ * binding provider in this tree": the global hook always passes it, and a
212
+ * binding hook mounted under a legacy provider falls through to the erased
213
+ * internal context, which is what keeps both mounts working while the last
214
+ * legacy mount migrates.
215
+ */
216
+ export function useAbloImpl<
217
+ R extends SchemaRecord,
218
+ T = Record<string, unknown>,
219
+ C = unknown,
220
+ >(
221
+ boundClient: Ablo<R> | null,
222
+ modelOrSelect?: ModelOperations<T, C> | ModelClientSelector<R, T, C> | AbloSelector<R, T>,
223
+ id?: string,
224
+ options?: UseAbloModelOptions<T>,
225
+ ): Ablo<R> | null | UseAbloModelResult<T> | T | undefined {
226
+ const ctx = useContext(AbloInternalContext);
227
+ // The bound client wins — it is already `Ablo<R>`, no rebinding. The
228
+ // fallback is the ONE remaining schema rebind in the SDK; it retires with
229
+ // the last legacy provider mount (docs/plans/typed-react-binding.md).
230
+ const engine: Ablo<R> | null =
231
+ boundClient ?? (ctx?.engine ? rebindEngine<R>(ctx.engine) : null);
232
+ const initial = options?.initial;
233
+ const isSelectorOnly = typeof modelOrSelect === 'function' && id === undefined;
234
+ const modelClient: ModelOperations<T, C> | undefined =
235
+ typeof modelOrSelect === 'function' && id !== undefined
236
+ ? engine
237
+ ? (modelOrSelect(reactiveReads<R>(engine)) as ModelOperations<T, C>)
238
+ : undefined
239
+ : typeof modelOrSelect === 'function'
240
+ ? undefined
241
+ : modelOrSelect;
242
+
243
+ // Claims arrive through an event emitter (engine.claims), not through MobX, so
244
+ // the useReactive reactions below cannot track them; we bridge changes with a
245
+ // setState bump instead. Only the model-row form (`id !== undefined`) reads
246
+ // claims, so we subscribe only when `id` is set. The selector-only form never
247
+ // reads claims, and subscribing it to the workspace-wide claim stream would
248
+ // re-render and recompute it on every claim or presence change anywhere — a
249
+ // real storm during AI editing or live collaboration — for a value that cannot
250
+ // change.
251
+ const [claimVersion, setClaimVersion] = useState(0);
252
+ useEffect(() => {
253
+ if (!engine || id === undefined) return;
254
+ return engine.claims.onChange(() => { setClaimVersion((version) => version + 1); });
255
+ }, [engine, id]);
256
+
257
+ const selected = useReactive<T | undefined>(
258
+ () => {
259
+ if (!engine || !isSelectorOnly || typeof modelOrSelect !== 'function') {
260
+ return undefined;
261
+ }
262
+ // The selector runs against the real engine — reads inside it return the
263
+ // pool's model instances. `snapshotValue` then converts the RESULT to
264
+ // plain snapshot rows, which is what the selector's `AbloReads`
265
+ // parameter type already promised.
266
+ return snapshotValue(modelOrSelect(reactiveReads<R>(engine)) as T);
267
+ },
268
+ );
269
+
270
+ const modelResult = useReactive<UseAbloModelResult<T>>(
271
+ () => {
272
+ void claimVersion;
273
+ return readModelResult(engine, modelClient, id, initial);
274
+ },
275
+ );
276
+
277
+ if (isSelectorOnly) return selected;
278
+ if (modelOrSelect) return modelResult;
279
+ return engine;
280
+ }
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+
3
+ import { useContext } from 'react';
4
+ import { AbloInternalContext } from './internalContext.js';
5
+ import { AbloValidationError } from '@abloatai/transaction/errors';
6
+
7
+ /** Returns the application user id supplied to the nearest AbloProvider. */
8
+ export function useCurrentUserId(): string | null {
9
+ const context = useContext(AbloInternalContext);
10
+ if (!context) {
11
+ throw new AbloValidationError(
12
+ 'useCurrentUserId: no <AbloProvider> mounted above this component.',
13
+ { code: 'no_ablo_provider' },
14
+ );
15
+ }
16
+ return context.currentUserId;
17
+ }
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+
3
+ import { useContext, useEffect, useRef } from 'react';
4
+ import { AbloInternalContext } from './internalContext.js';
5
+ import { AbloValidationError } from '@abloatai/transaction/errors';
6
+
7
+ /** Subscribe to provider-level errors without causing component re-renders. */
8
+ export function useErrorListener(listener: (error: Error) => void): void {
9
+ const context = useContext(AbloInternalContext);
10
+ if (!context) {
11
+ throw new AbloValidationError(
12
+ 'useErrorListener: no <AbloProvider> mounted above this component.',
13
+ { code: 'no_ablo_provider' },
14
+ );
15
+ }
16
+ const listenerRef = useRef(listener);
17
+ listenerRef.current = listener;
18
+ useEffect(
19
+ () => context.subscribeError((error) => listenerRef.current(error)),
20
+ [context],
21
+ );
22
+ }