@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,116 @@
1
+ /**
2
+ * The pipeline that takes incoming deltas from the server and lands them in
3
+ * local state. It has four stages: deduplicating deltas by state signature,
4
+ * per-delta bookkeeping and enqueue, a debounce for live traffic, and a flush
5
+ * that writes to the local store and then the in-memory pool. Every call it
6
+ * makes back into the surrounding store — resolving state fields, identifying
7
+ * custom entities, deduplicating, flushing, handling sync-group changes —
8
+ * routes through the {@link DeltaPipelineContext} interface, so a subclass that
9
+ * overrides any of those hooks still takes effect. The atomic frame-apply entry
10
+ * point lives on the store and drives this pipeline through {@link enqueueDelta}
11
+ * and {@link flushPendingDeltas}.
12
+ */
13
+ import type { RuntimeContext } from '../RuntimeContext.js';
14
+ import { ModelScope } from '../InstanceCache.js';
15
+ import type { Model } from '../Model.js';
16
+ import type { SyncDelta } from './SyncWebSocket.js';
17
+ import type { ModelData } from '@abloatai/transaction/types/modelData';
18
+ import { type AbloPlugin, type AppliedChange } from '../../plugin.js';
19
+ /**
20
+ * What the pipeline needs back from the surrounding store: the shared mutable
21
+ * pipeline state (backed by the store's own fields through accessors), narrow
22
+ * persistence and pool facades, and the store's overridable hooks, so a
23
+ * subclass's overrides continue to take effect.
24
+ */
25
+ export interface DeltaPipelineContext {
26
+ /** The owning client's runtime. Defaults to the module-global bridge. */
27
+ readonly runtime?: RuntimeContext;
28
+ /**
29
+ * The installed plugins, whose declared stage handlers this pipeline
30
+ * dispatches at each boundary. Empty (or absent) on directly-constructed
31
+ * stores, where the store's own apply is the whole pipeline.
32
+ */
33
+ readonly stagePlugins?: readonly AbloPlugin[];
34
+ pendingDeltas: SyncDelta[];
35
+ batchTimer: ReturnType<typeof setTimeout> | null;
36
+ /** Queue for deltas arriving during an active bootstrap; null when none. */
37
+ readonly bootstrapDeltaQueue: SyncDelta[] | null;
38
+ readonly smartSyncOptions: {
39
+ readonly batchingDelay: number;
40
+ readonly maxBatchSize: number;
41
+ };
42
+ /** Pool-applied cursor (`syncClient.position.applied`). */
43
+ readonly highestProcessedSyncId: number;
44
+ /** Resume/ack cursor (`syncClient.position.persisted`). */
45
+ readonly lastAckedId: number;
46
+ onDeltaReceived(syncId: number, transactionId?: string, correlationId?: string): void;
47
+ advanceApplied(syncId: number): void;
48
+ advancePersisted(syncId: number): void;
49
+ processDeltaBatch(deltas: {
50
+ syncId?: number;
51
+ actionType: SyncDelta['actionType'];
52
+ modelName: string;
53
+ modelId: string;
54
+ data: ModelData | null;
55
+ transactionId?: string;
56
+ }[]): Promise<{
57
+ results: AppliedChange[];
58
+ persistedSyncId: number;
59
+ }>;
60
+ /** Applies persisted delta results to the in-memory pool, with the host's relation enrichment bound. */
61
+ applyDeltaBatchToPool(results: AppliedChange[]): void;
62
+ /** Acknowledges a sync id back to the server; a no-op when the socket is down. */
63
+ acknowledge(syncId: number): void;
64
+ readonly objectPool: {
65
+ get(id: string): Model | undefined;
66
+ add(model: Model, scope: ModelScope): void;
67
+ remove(id: string): boolean;
68
+ /** Full in-memory clear — the revocation-failure fallback (see
69
+ * {@link handleGroupHandlerFailure}). */
70
+ clear(): void;
71
+ };
72
+ getStateFields(modelName: string): string[];
73
+ isCustomEntity(modelName: string): boolean;
74
+ createCustomEntity(modelName: string, modelId: string, data: Record<string, unknown>): Model | null;
75
+ deduplicateDeltas(deltas: SyncDelta[]): SyncDelta[];
76
+ flushPendingDeltas(): Promise<void>;
77
+ handleFlushError(error: unknown): void;
78
+ handleSyncGroupChange(delta: SyncDelta): Promise<void>;
79
+ handleGroupRemoved(delta: SyncDelta): Promise<void>;
80
+ /** Host's re-bootstrap trigger — the revocation-failure fallback. */
81
+ forceFullRebootstrap(): void;
82
+ cascadeCancelTransactionsForDeletedParent(parentModelName: string, parentId: string): void;
83
+ }
84
+ /**
85
+ * Handles a sync-group ('G' or 'S') delta whose handler rejected after the
86
+ * applied watermark had already advanced. That delta will never be redelivered,
87
+ * so a failed security clear — revoked data that must not stay cached — would
88
+ * otherwise fail silently. The safe fallback is blunt: drop the whole in-memory
89
+ * pool and force a full re-bootstrap, rebuilding local state from the server.
90
+ * It never throws, because it runs inside the pipeline's fire-and-forget path.
91
+ */
92
+ export declare function handleGroupHandlerFailure(ctx: DeltaPipelineContext, delta: SyncDelta, error: unknown): void;
93
+ /** Deduplicate deltas to the same entity — keep meaningful state transitions only */
94
+ export declare function deduplicateDeltas(ctx: DeltaPipelineContext, deltas: SyncDelta[]): SyncDelta[];
95
+ /**
96
+ * Performs per-delta bookkeeping and enqueues the delta. Returns `true` when
97
+ * the delta was pushed onto `pendingDeltas` — a regular batchable insert,
98
+ * update, covering, or delete that a later flush must drain — and `false` when
99
+ * it was skipped as a duplicate, deferred into the bootstrap queue, or handled
100
+ * immediately (a 'G'/'S' sync-group change). It does not schedule the flush
101
+ * itself; the caller chooses whether to debounce live traffic or flush a
102
+ * catch-up frame atomically.
103
+ */
104
+ export declare function enqueueDelta(ctx: DeltaPipelineContext, delta: SyncDelta, options?: {
105
+ authoritative?: boolean;
106
+ }): boolean;
107
+ /** Debounce a flush for live single-delta traffic. */
108
+ export declare function scheduleDeltaFlush(ctx: DeltaPipelineContext): void;
109
+ /** Apply an authoritative delta frame as one atomic flush. */
110
+ export declare function applyDeltaFrame(ctx: DeltaPipelineContext, deltas: SyncDelta[]): void;
111
+ /**
112
+ * Flushes the queued deltas: deduplicates them, applies custom-entity deltas
113
+ * straight to the pool, writes the rest to the local store and then the pool,
114
+ * and advances the acknowledgement cursor once the store write has committed.
115
+ */
116
+ export declare function flushPendingDeltas(ctx: DeltaPipelineContext): Promise<void>;
@@ -0,0 +1,357 @@
1
+ /**
2
+ * The pipeline that takes incoming deltas from the server and lands them in
3
+ * local state. It has four stages: deduplicating deltas by state signature,
4
+ * per-delta bookkeeping and enqueue, a debounce for live traffic, and a flush
5
+ * that writes to the local store and then the in-memory pool. Every call it
6
+ * makes back into the surrounding store — resolving state fields, identifying
7
+ * custom entities, deduplicating, flushing, handling sync-group changes —
8
+ * routes through the {@link DeltaPipelineContext} interface, so a subclass that
9
+ * overrides any of those hooks still takes effect. The atomic frame-apply entry
10
+ * point lives on the store and drives this pipeline through {@link enqueueDelta}
11
+ * and {@link flushPendingDeltas}.
12
+ */
13
+ import { runInAction } from 'mobx';
14
+ import { globalRuntime } from '../context.js';
15
+ import { ModelScope } from '../InstanceCache.js';
16
+ import { runStage, pluginsForStage, } from '../../plugin.js';
17
+ /**
18
+ * One drain per store. Incoming WebSocket frames may arrive while persistence
19
+ * and pool application are awaiting. Without a single-flight guard every
20
+ * frame starts another flush over the same mutable queue, duplicating work and
21
+ * allowing acknowledgements to race.
22
+ *
23
+ * The context object is memoized by BaseSyncedStore, so a WeakMap keeps this
24
+ * coordination private to the pipeline without adding lifecycle state to the
25
+ * public store surface.
26
+ */
27
+ const activeFlushes = new WeakMap();
28
+ /**
29
+ * Handles a sync-group ('G' or 'S') delta whose handler rejected after the
30
+ * applied watermark had already advanced. That delta will never be redelivered,
31
+ * so a failed security clear — revoked data that must not stay cached — would
32
+ * otherwise fail silently. The safe fallback is blunt: drop the whole in-memory
33
+ * pool and force a full re-bootstrap, rebuilding local state from the server.
34
+ * It never throws, because it runs inside the pipeline's fire-and-forget path.
35
+ */
36
+ export function handleGroupHandlerFailure(ctx, delta, error) {
37
+ (ctx.runtime ?? globalRuntime).logger.error('Your access changed but cached data could not be cleared — resetting local data.', {
38
+ syncId: delta.id,
39
+ error: error instanceof Error ? error.message : String(error),
40
+ });
41
+ try {
42
+ ctx.objectPool.clear();
43
+ }
44
+ catch {
45
+ // In-memory clear must never mask the re-bootstrap below.
46
+ }
47
+ try {
48
+ ctx.forceFullRebootstrap();
49
+ }
50
+ catch {
51
+ // Best-effort: the reconnect/bootstrap cycle self-heals on next connect.
52
+ }
53
+ }
54
+ /** Builds a small signature of a delta's state fields, used to detect no-op duplicate deltas. */
55
+ function extractStateSignature(ctx, delta) {
56
+ if (!delta.data || typeof delta.data !== 'object')
57
+ return null;
58
+ const data = typeof delta.data === 'string'
59
+ ? JSON.parse(delta.data)
60
+ : (delta.data);
61
+ // Generic state fields — subclasses can override getStateFields() for model-specific fields
62
+ const fieldsToCheck = ctx.getStateFields(delta.modelName);
63
+ const signature = {
64
+ actionType: delta.actionType,
65
+ modelName: delta.modelName,
66
+ };
67
+ for (const field of fieldsToCheck) {
68
+ if (field in data)
69
+ signature[field] = data[field];
70
+ }
71
+ return signature;
72
+ }
73
+ function isSameState(a, b) {
74
+ if (!a || !b)
75
+ return false;
76
+ const keys = Object.keys(a);
77
+ if (keys.length !== Object.keys(b).length)
78
+ return false;
79
+ return keys.every((k) => a[k] === b[k]);
80
+ }
81
+ /** Deduplicate deltas to the same entity — keep meaningful state transitions only */
82
+ export function deduplicateDeltas(ctx, deltas) {
83
+ const byEntity = new Map();
84
+ for (const d of deltas) {
85
+ const key = `${d.modelName}:${d.modelId}`;
86
+ if (!byEntity.has(key))
87
+ byEntity.set(key, []);
88
+ byEntity.get(key).push(d);
89
+ }
90
+ const result = [];
91
+ for (const entityDeltas of byEntity.values()) {
92
+ const sorted = entityDeltas.sort((a, b) => a.id - b.id);
93
+ // DELETE wins — it's the final state
94
+ const del = sorted.find((d) => d.actionType === 'D');
95
+ if (del) {
96
+ result.push(del);
97
+ continue;
98
+ }
99
+ // Keep deltas that represent different states
100
+ const unique = [];
101
+ let prev = null;
102
+ for (const d of sorted) {
103
+ const sig = extractStateSignature(ctx, d);
104
+ if (!isSameState(prev, sig)) {
105
+ unique.push(d);
106
+ prev = sig;
107
+ }
108
+ }
109
+ if (unique.length > 0) {
110
+ result.push(...unique);
111
+ }
112
+ else {
113
+ // `sorted` is never empty (every byEntity bucket gets at least one
114
+ // delta pushed) — the guard only narrows the indexed access.
115
+ const last = sorted.at(-1);
116
+ if (last)
117
+ result.push(last);
118
+ }
119
+ }
120
+ return result.sort((a, b) => a.id - b.id);
121
+ }
122
+ /**
123
+ * Performs per-delta bookkeeping and enqueues the delta. Returns `true` when
124
+ * the delta was pushed onto `pendingDeltas` — a regular batchable insert,
125
+ * update, covering, or delete that a later flush must drain — and `false` when
126
+ * it was skipped as a duplicate, deferred into the bootstrap queue, or handled
127
+ * immediately (a 'G'/'S' sync-group change). It does not schedule the flush
128
+ * itself; the caller chooses whether to debounce live traffic or flush a
129
+ * catch-up frame atomically.
130
+ */
131
+ export function enqueueDelta(ctx, delta, options = {}) {
132
+ // Dedup guard — skip already-processed deltas. The `applied` watermark is a
133
+ // valid skip threshold only for in-order live traffic; an authoritative
134
+ // catch-up frame bypasses it, so an out-of-order live delta that advanced the
135
+ // watermark can't cause the frame's lower ids to be dropped silently.
136
+ if (!options.authoritative && delta.id > 0 && delta.id <= ctx.highestProcessedSyncId) {
137
+ return false;
138
+ }
139
+ // Confirm awaiting transactions via sync ID threshold (before batching)
140
+ ctx.onDeltaReceived(delta.id, delta.transactionId, delta.correlationId);
141
+ // Queue during active bootstrap
142
+ if (ctx.bootstrapDeltaQueue !== null) {
143
+ ctx.bootstrapDeltaQueue.push(delta);
144
+ return false;
145
+ }
146
+ // Advance watermark
147
+ ctx.advanceApplied(delta.id);
148
+ // Sync group added — handle immediately. Accepts both the batched
149
+ // (addedGroups/removedGroups) and incremental (group/userId) payloads. This
150
+ // is deliberately not fire-and-forget: the watermark has already advanced, so
151
+ // a rejected handler (a failed security clear) must trigger the fallback
152
+ // rather than vanish.
153
+ if (delta.actionType === 'G') {
154
+ void ctx.handleSyncGroupChange(delta).catch((error) => {
155
+ handleGroupHandlerFailure(ctx, delta, error);
156
+ });
157
+ return false;
158
+ }
159
+ // Sync group removed — handle immediately. Clears affected local state
160
+ // and forces re-bootstrap with the updated group list. Same fallback:
161
+ // a failed revocation clear escalates instead of leaving revoked rows.
162
+ if (delta.actionType === 'S') {
163
+ void ctx.handleGroupRemoved(delta).catch((error) => {
164
+ handleGroupHandlerFailure(ctx, delta, error);
165
+ });
166
+ return false;
167
+ }
168
+ // Delete — run the cascade cancel immediately (O(1) through the foreign-key
169
+ // index; it must run before any later update on the same model lands, so
170
+ // pending update transactions for soon-deleted children don't race their
171
+ // parent's delete). The persistence and pool write still goes through the
172
+ // same batched path as updates: flushing each delete on its own produced one
173
+ // store write, one pool mutation, and one `models:changed` event per row, so
174
+ // deleting a parent with many children fanned out into many of each, whereas
175
+ // the batched path collapses them into one per flush. Deduplication in
176
+ // `flushPendingDeltas` handles an update-then-delete on the same model by
177
+ // replaying in arrival order.
178
+ if (delta.actionType === 'D') {
179
+ ctx.cascadeCancelTransactionsForDeletedParent(delta.modelName, delta.modelId);
180
+ }
181
+ // The delta is accepted and queued — the `receive` stage boundary.
182
+ runStage(ctx.stagePlugins ?? [], 'receive', { delta });
183
+ ctx.pendingDeltas.push(delta);
184
+ return true;
185
+ }
186
+ /** Debounce a flush for live single-delta traffic. */
187
+ export function scheduleDeltaFlush(ctx) {
188
+ if (ctx.batchTimer)
189
+ clearTimeout(ctx.batchTimer);
190
+ if (ctx.pendingDeltas.length >= ctx.smartSyncOptions.maxBatchSize) {
191
+ void ctx.flushPendingDeltas().catch(ctx.handleFlushError);
192
+ }
193
+ else {
194
+ ctx.batchTimer = setTimeout(() => {
195
+ void ctx.flushPendingDeltas().catch(ctx.handleFlushError);
196
+ }, ctx.smartSyncOptions.batchingDelay);
197
+ }
198
+ }
199
+ /** Apply an authoritative delta frame as one atomic flush. */
200
+ export function applyDeltaFrame(ctx, deltas) {
201
+ let enqueuedAny = false;
202
+ for (const delta of deltas) {
203
+ if (enqueueDelta(ctx, delta, { authoritative: true }))
204
+ enqueuedAny = true;
205
+ }
206
+ if (!enqueuedAny)
207
+ return;
208
+ if (ctx.batchTimer) {
209
+ clearTimeout(ctx.batchTimer);
210
+ ctx.batchTimer = null;
211
+ }
212
+ void ctx.flushPendingDeltas().catch(ctx.handleFlushError);
213
+ }
214
+ /**
215
+ * Flushes the queued deltas: deduplicates them, applies custom-entity deltas
216
+ * straight to the pool, writes the rest to the local store and then the pool,
217
+ * and advances the acknowledgement cursor once the store write has committed.
218
+ */
219
+ export async function flushPendingDeltas(ctx) {
220
+ const activeFlush = activeFlushes.get(ctx);
221
+ if (activeFlush)
222
+ return activeFlush;
223
+ const flush = drainPendingDeltas(ctx);
224
+ activeFlushes.set(ctx, flush);
225
+ try {
226
+ await flush;
227
+ }
228
+ finally {
229
+ if (activeFlushes.get(ctx) === flush)
230
+ activeFlushes.delete(ctx);
231
+ }
232
+ }
233
+ /**
234
+ * Detaches each batch before its first await, then keeps draining anything
235
+ * that arrived in the meantime. Detaching is the critical ownership transfer:
236
+ * a later frame appends to a fresh queue instead of observing and reprocessing
237
+ * the batch currently being persisted.
238
+ */
239
+ async function drainPendingDeltas(ctx) {
240
+ while (ctx.pendingDeltas.length > 0) {
241
+ if (ctx.batchTimer) {
242
+ clearTimeout(ctx.batchTimer);
243
+ ctx.batchTimer = null;
244
+ }
245
+ const queuedDeltas = ctx.pendingDeltas;
246
+ ctx.pendingDeltas = [];
247
+ try {
248
+ await flushDeltaBatch(ctx, queuedDeltas);
249
+ }
250
+ catch (error) {
251
+ // Preserve the pre-existing retry contract. The failed detached batch
252
+ // goes back ahead of deltas received while it was in flight.
253
+ ctx.pendingDeltas = [...queuedDeltas, ...ctx.pendingDeltas];
254
+ throw error;
255
+ }
256
+ if (ctx.pendingDeltas.length > 0) {
257
+ // A sustained stream can refill the detached queue before every
258
+ // persistence promise settles. Promise-only looping then forms an
259
+ // unbounded microtask chain that starves WebSocket reads, timers and
260
+ // replication keepalives. Give the host one macrotask turn between
261
+ // owned batches; Node has setImmediate, browsers fall back to a timer.
262
+ await yieldToHost();
263
+ }
264
+ }
265
+ if (ctx.batchTimer) {
266
+ clearTimeout(ctx.batchTimer);
267
+ ctx.batchTimer = null;
268
+ }
269
+ }
270
+ function yieldToHost() {
271
+ const immediate = globalThis.setImmediate;
272
+ return new Promise((resolve) => {
273
+ if (immediate)
274
+ immediate(resolve);
275
+ else
276
+ setTimeout(resolve, 0);
277
+ });
278
+ }
279
+ async function flushDeltaBatch(ctx, queuedDeltas) {
280
+ const stagePlugins = ctx.stagePlugins ?? [];
281
+ const deduplicatedDeltas = ctx.deduplicateDeltas(queuedDeltas);
282
+ runStage(stagePlugins, 'dedupe', { deltas: deduplicatedDeltas });
283
+ // Custom entities → apply straight to the pool, skipping the local store.
284
+ const customDeltas = deduplicatedDeltas.filter((d) => ctx.isCustomEntity(d.modelName));
285
+ if (customDeltas.length > 0) {
286
+ runInAction(() => {
287
+ for (const delta of customDeltas) {
288
+ const data = typeof delta.data === 'string'
289
+ ? JSON.parse(delta.data)
290
+ : (delta.data);
291
+ // 'C' (Covering) is treated identically to 'I' here — the client
292
+ // gained permission to see the entity, so we insert it into the
293
+ // pool as if newly created.
294
+ if (delta.actionType === 'I' || delta.actionType === 'U' || delta.actionType === 'C') {
295
+ const existing = ctx.objectPool.get(delta.modelId);
296
+ if (existing) {
297
+ existing.updateFromData(data);
298
+ }
299
+ else {
300
+ const model = ctx.createCustomEntity(delta.modelName, delta.modelId, data);
301
+ if (model) {
302
+ model.markAsPersisted();
303
+ ctx.objectPool.add(model, ModelScope.live);
304
+ }
305
+ }
306
+ }
307
+ else if (delta.actionType === 'D') {
308
+ ctx.objectPool.remove(delta.modelId);
309
+ }
310
+ }
311
+ });
312
+ }
313
+ // Regular deltas → the local store, then the pool.
314
+ // 'G' and 'S' deltas are handled earlier (handleSyncGroupChange /
315
+ // handleGroupRemoved) and never reach here, though the persistence
316
+ // signature accepts them defensively.
317
+ const regularDeltas = deduplicatedDeltas.filter((d) => !ctx.isCustomEntity(d.modelName));
318
+ const batch = await ctx.processDeltaBatch(regularDeltas.map((d) => ({
319
+ syncId: d.id,
320
+ actionType: d.actionType,
321
+ modelName: d.modelName,
322
+ modelId: d.modelId,
323
+ data: typeof d.data === 'string' ? JSON.parse(d.data) : d.data,
324
+ // Thread `transactionId` through so the receive layer can recognize
325
+ // echoes of locally-applied transactions and skip the pool mutation.
326
+ transactionId: d.transactionId,
327
+ })));
328
+ const dbResults = batch.results;
329
+ runStage(stagePlugins, 'persist', { deltas: regularDeltas });
330
+ // Apply the batch results to the in-memory graph. When a plugin has
331
+ // declared the `apply` stage, its handlers ARE the apply — the
332
+ // materialiser attached where it said it would. The direct call is the
333
+ // bridge for stores constructed without plugins (subclasses, tests),
334
+ // whose own apply is the whole pipeline.
335
+ if (pluginsForStage(stagePlugins, 'apply').length > 0) {
336
+ runStage(stagePlugins, 'apply', { changes: dbResults });
337
+ }
338
+ else {
339
+ ctx.applyDeltaBatchToPool(dbResults);
340
+ }
341
+ // Acknowledge and advance the sync cursor, gated on persistence.
342
+ //
343
+ // We must acknowledge `persistedSyncId` — the high-water mark of deltas whose
344
+ // store transaction actually committed — not the input batch's last delta id.
345
+ // Acknowledging the input range would advance the server's view past deltas
346
+ // that never persisted; the next catch-up would then send the advanced cursor,
347
+ // the server would answer "you're up to date", and the unpersisted delta would
348
+ // be lost. The cursor and the persisted state must move together.
349
+ const persistedSyncId = batch.persistedSyncId;
350
+ if (persistedSyncId > ctx.lastAckedId) {
351
+ ctx.acknowledge(persistedSyncId);
352
+ ctx.advancePersisted(persistedSyncId);
353
+ runStage(stagePlugins, 'acknowledge', { syncId: persistedSyncId });
354
+ }
355
+ // Cache invalidation happens automatically via the 'models:changed' event.
356
+ runStage(stagePlugins, 'notify', { changes: dbResults });
357
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Handles the delta types that change which sync groups a session can see. A
3
+ * sync group is a fan-out scope the server uses to decide which entities a
4
+ * client receives. When a session's membership changes, these handlers update
5
+ * the client's subscription list; when access is revoked, they clear cached
6
+ * data and trigger a full re-bootstrap so revoked rows cannot linger on the
7
+ * device.
8
+ *
9
+ * Every handler takes a {@link GroupChangeContext}, the narrow facade through
10
+ * which it reaches the client's local storage and connection lifecycle hooks.
11
+ */
12
+ import type { RuntimeContext } from '../RuntimeContext.js';
13
+ import type { SyncDelta, SyncGroupChangePayload, GroupAddedPayload } from './SyncWebSocket.js';
14
+ /**
15
+ * The collaborators the group-change handlers depend on. It gathers the
16
+ * client's local storage, in-memory pool, and connection lifecycle hooks
17
+ * behind one narrow interface, so the handlers stay decoupled from the larger
18
+ * store that supplies them.
19
+ */
20
+ export interface GroupChangeContext {
21
+ /**
22
+ * Local persistence. Performs the security clear, reads and writes the
23
+ * subscription metadata, and sets the flag that forces a full bootstrap.
24
+ */
25
+ readonly database: {
26
+ clear(): Promise<void>;
27
+ getWorkspaceMetadata(): Promise<{
28
+ subscribedSyncGroups?: string[];
29
+ } | null>;
30
+ updateWorkspaceMetadata(metadata: {
31
+ subscribedSyncGroups: string[];
32
+ }): Promise<void>;
33
+ markRequiresFullBootstrap(): void;
34
+ };
35
+ /** The in-memory object cache, cleared alongside local storage when access is revoked. */
36
+ readonly objectPool: {
37
+ clear(): void;
38
+ };
39
+ /** Returns the sync groups the live connection is currently subscribed to. */
40
+ getSubscribedSyncGroups(): readonly string[];
41
+ /**
42
+ * Returns the session's authoritative sync groups, resolved from the current
43
+ * user context via {@link resolveSyncGroups}; null when no user context has
44
+ * been set yet.
45
+ */
46
+ getCurrentSyncGroups(): readonly string[] | null;
47
+ /**
48
+ * Returns the session's bootstrap mode. A value of 'none' means the
49
+ * participant never pulls a baseline, so it never re-bootstraps.
50
+ */
51
+ getBootstrapMode(): 'full' | 'none' | undefined;
52
+ /** Disconnects the live connection, one step of the forced re-bootstrap cycle. */
53
+ disconnectWebSocket(): void;
54
+ /** Emits a connection lifecycle event to any registered listener; a no-op when none is set. */
55
+ emitConnectionEvent(event: string): void;
56
+ handleGroupAdded(payload: GroupAddedPayload, syncId: number): Promise<void>;
57
+ computeUpdatedSyncGroups(payload: SyncGroupChangePayload): string[];
58
+ forceFullRebootstrap(): void;
59
+ /** The owning client's runtime. Defaults to the module-global bridge. */
60
+ readonly runtime?: RuntimeContext;
61
+ }
62
+ /**
63
+ * Handles a 'G' (group-change) delta. The server sends two shapes of this
64
+ * delta, told apart by the payload:
65
+ *
66
+ * Incremental — `{ group, userId }`: the recipient was added to a single
67
+ * sync group. No re-bootstrap follows; the newly visible entities arrive as
68
+ * ordinary 'C' (covering) deltas through the normal insert path.
69
+ *
70
+ * Full diff — `{ addedGroups, removedGroups }`: one delta carrying the whole
71
+ * membership change. This forces a full re-bootstrap (disconnect, reconnect,
72
+ * and refetch), clearing cached data first if any group was removed.
73
+ */
74
+ export declare function handleSyncGroupChange(ctx: GroupChangeContext, delta: SyncDelta): Promise<void>;
75
+ /**
76
+ * Handles an incremental group-added delta. It records the new sync group in
77
+ * the subscription metadata without forcing a re-bootstrap; the server then
78
+ * sends a 'C' (covering) delta for each newly visible entity, which flows
79
+ * through the normal insert path.
80
+ */
81
+ export declare function handleGroupAdded(ctx: GroupChangeContext, payload: GroupAddedPayload, syncId: number): Promise<void>;
82
+ /**
83
+ * Handles an 'S' (group-removed) delta, which signals the recipient has lost
84
+ * access to a sync group. The client does not track which entities belong to
85
+ * which group, so it cannot purge only the affected rows; instead it clears
86
+ * local state and forces a re-bootstrap with the updated group list.
87
+ */
88
+ export declare function handleGroupRemoved(ctx: GroupChangeContext, delta: SyncDelta): Promise<void>;
89
+ /** Computes the new sync-group set after applying the additions and removals in a diff. */
90
+ export declare function computeUpdatedSyncGroups(ctx: GroupChangeContext, payload: SyncGroupChangePayload): string[];
91
+ /**
92
+ * Forces a full re-bootstrap by marking local storage as needing one,
93
+ * disconnecting, and emitting a connection lifecycle event that the reconnect
94
+ * path acts on. Does nothing for participants whose bootstrap mode is 'none':
95
+ * they never pull a baseline, so after a trigger such as a sync-group shrink or
96
+ * an access revocation they rely on covering deltas to repopulate the data they
97
+ * subscribe to.
98
+ */
99
+ export declare function forceFullRebootstrap(ctx: GroupChangeContext): void;
100
+ /**
101
+ * Resolves the sync-group list this session subscribes to, and is the single
102
+ * place that decision is made. The server-issued `context.syncGroups` is
103
+ * authoritative; when it is absent, the session subscribes to no explicit
104
+ * groups. {@link checkSyncGroupShrinkage} and connection setup both read
105
+ * through here, so the live subscription and the access-revocation check can
106
+ * never disagree.
107
+ */
108
+ export declare function resolveSyncGroups(context: {
109
+ syncGroups?: readonly string[];
110
+ }): readonly string[];
111
+ /**
112
+ * Compares the session's current sync groups against the set stored from the
113
+ * last session. If any group is now missing, access has narrowed, so this
114
+ * clears cached data and forces a full bootstrap before recording the new set.
115
+ */
116
+ export declare function checkSyncGroupShrinkage(ctx: GroupChangeContext): Promise<void>;