@abloatai/ablo 0.34.1 → 0.35.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 (483) hide show
  1. package/AGENTS.md +2 -1
  2. package/CHANGELOG.md +674 -5
  3. package/README.md +39 -22
  4. package/dist/BaseSyncedStore.d.ts +152 -44
  5. package/dist/BaseSyncedStore.js +300 -184
  6. package/dist/Database.d.ts +9 -24
  7. package/dist/Database.js +37 -22
  8. package/dist/InstanceCache.d.ts +25 -4
  9. package/dist/InstanceCache.js +48 -15
  10. package/dist/LazyReferenceCollection.d.ts +3 -3
  11. package/dist/LazyReferenceCollection.js +4 -4
  12. package/dist/Model.d.ts +6 -6
  13. package/dist/Model.js +10 -10
  14. package/dist/ModelRegistry.d.ts +4 -4
  15. package/dist/ModelRegistry.js +3 -3
  16. package/dist/{SyncEngineContext.d.ts → RuntimeContext.d.ts} +20 -13
  17. package/dist/{SyncEngineContext.js → RuntimeContext.js} +11 -12
  18. package/dist/SyncClient.d.ts +42 -32
  19. package/dist/SyncClient.js +166 -110
  20. package/dist/ai-sdk/coordinatedTool.d.ts +2 -2
  21. package/dist/ai-sdk/coordinatedTool.js +1 -1
  22. package/dist/ai-sdk/coordinationContext.d.ts +2 -2
  23. package/dist/ai-sdk/coordinationContext.js +1 -1
  24. package/dist/ai-sdk/wrap.d.ts +3 -3
  25. package/dist/ai-sdk/wrap.js +2 -2
  26. package/dist/auth/index.d.ts +1 -156
  27. package/dist/auth/index.js +8 -301
  28. package/dist/cli.cjs +3344 -1073
  29. package/dist/client/Ablo.d.ts +42 -287
  30. package/dist/client/Ablo.js +118 -963
  31. package/dist/client/abloClient.d.ts +309 -0
  32. package/dist/client/abloClient.js +13 -0
  33. package/dist/client/clientPrelude.d.ts +52 -0
  34. package/dist/client/clientPrelude.js +60 -0
  35. package/dist/client/consoleLogger.d.ts +2 -2
  36. package/dist/client/coreClient.d.ts +60 -0
  37. package/dist/client/coreClient.js +118 -0
  38. package/dist/client/createInternalComponents.d.ts +4 -4
  39. package/dist/client/createInternalComponents.js +9 -8
  40. package/dist/client/createModelProxy.d.ts +78 -373
  41. package/dist/client/createModelProxy.js +114 -86
  42. package/dist/client/humans.d.ts +48 -0
  43. package/dist/client/humans.js +52 -0
  44. package/dist/client/modelRegistration.d.ts +1 -1
  45. package/dist/client/modelRegistration.js +9 -9
  46. package/dist/client/options.d.ts +73 -17
  47. package/dist/client/reactiveEngine.d.ts +48 -0
  48. package/dist/client/reactiveEngine.js +910 -0
  49. package/dist/client/resourceTypes.d.ts +9 -250
  50. package/dist/client/resourceTypes.js +8 -5
  51. package/dist/client/schemaConfig.d.ts +4 -4
  52. package/dist/client/schemaConfig.js +6 -2
  53. package/dist/client/validateAbloOptions.d.ts +3 -2
  54. package/dist/client/validateAbloOptions.js +1 -1
  55. package/dist/client/wsMutationExecutor.d.ts +3 -3
  56. package/dist/client/wsMutationExecutor.js +3 -3
  57. package/dist/context.d.ts +9 -9
  58. package/dist/context.js +10 -9
  59. package/dist/coordination/ClaimLog.d.ts +26 -0
  60. package/dist/coordination/ClaimLog.js +32 -0
  61. package/dist/coordination/index.d.ts +1 -15
  62. package/dist/coordination/index.js +8 -31
  63. package/dist/core/DatabaseManager.js +1 -1
  64. package/dist/core/QueryView.d.ts +1 -1
  65. package/dist/core/QueryView.js +1 -1
  66. package/dist/core/StoreManager.d.ts +4 -23
  67. package/dist/core/StoreManager.js +5 -55
  68. package/dist/core/index.d.ts +2 -2
  69. package/dist/core/index.js +2 -2
  70. package/dist/core/storeContract.d.ts +2 -2
  71. package/dist/docs/catalog.d.ts +72 -0
  72. package/dist/docs/catalog.js +227 -0
  73. package/dist/docs/index.d.ts +10 -0
  74. package/dist/docs/index.js +10 -0
  75. package/dist/environment.d.ts +1 -40
  76. package/dist/environment.js +8 -37
  77. package/dist/index.d.ts +40 -34
  78. package/dist/index.js +26 -20
  79. package/dist/interfaces/index.d.ts +44 -134
  80. package/dist/keys/index.d.ts +1 -77
  81. package/dist/keys/index.js +8 -190
  82. package/dist/mutators/{RecordingTransaction.d.ts → RecordingMutation.d.ts} +4 -4
  83. package/dist/mutators/{RecordingTransaction.js → RecordingMutation.js} +2 -2
  84. package/dist/mutators/Transaction.d.ts +1 -1
  85. package/dist/mutators/Transaction.js +1 -1
  86. package/dist/mutators/UndoManager.d.ts +6 -6
  87. package/dist/mutators/UndoManager.js +5 -5
  88. package/dist/mutators/defineMutators.d.ts +3 -3
  89. package/dist/mutators/defineMutators.js +1 -1
  90. package/dist/mutators/inverseOp.js +2 -2
  91. package/dist/mutators/mutateActions.d.ts +3 -3
  92. package/dist/mutators/mutateActions.js +1 -1
  93. package/dist/mutators/readerActions.d.ts +1 -1
  94. package/dist/mutators/undoApply.d.ts +1 -1
  95. package/dist/mutators/undoApply.js +1 -1
  96. package/dist/policy/index.d.ts +2 -2
  97. package/dist/policy/index.js +1 -1
  98. package/dist/query/client.d.ts +2 -2
  99. package/dist/query/client.js +4 -4
  100. package/dist/query/types.d.ts +6 -41
  101. package/dist/query/types.js +2 -2
  102. package/dist/react/AbloProvider.d.ts +6 -8
  103. package/dist/react/AbloProvider.js +5 -7
  104. package/dist/react/context.d.ts +1 -1
  105. package/dist/react/context.js +1 -1
  106. package/dist/react/index.d.ts +5 -5
  107. package/dist/react/index.js +3 -3
  108. package/dist/react/internalContext.d.ts +1 -1
  109. package/dist/react/useAblo.d.ts +3 -3
  110. package/dist/react/useAblo.js +1 -1
  111. package/dist/react/useCurrentUserId.js +1 -1
  112. package/dist/react/useErrorListener.js +1 -1
  113. package/dist/react/useMutationFailureListener.d.ts +2 -2
  114. package/dist/react/useMutationFailureListener.js +1 -1
  115. package/dist/react/useMutators.d.ts +3 -3
  116. package/dist/react/useMutators.js +3 -3
  117. package/dist/react/useUndoScope.d.ts +5 -5
  118. package/dist/react/useUndoScope.js +1 -1
  119. package/dist/schema/coordination.d.ts +69 -10
  120. package/dist/schema/coordination.js +86 -9
  121. package/dist/schema/ddl.js +2 -2
  122. package/dist/schema/diff.d.ts +1 -1
  123. package/dist/schema/generate.js +1 -1
  124. package/dist/schema/index.d.ts +10 -10
  125. package/dist/schema/index.js +18 -18
  126. package/dist/schema/queries.d.ts +27 -27
  127. package/dist/schema/queries.js +23 -23
  128. package/dist/schema/select.d.ts +3 -3
  129. package/dist/schema/select.js +3 -3
  130. package/dist/schema/serialize.d.ts +15 -6
  131. package/dist/schema/serialize.js +17 -3
  132. package/dist/schema/sugar.d.ts +6 -7
  133. package/dist/schema/sugar.js +9 -12
  134. package/dist/schema/syncDeltaRow.d.ts +4 -152
  135. package/dist/schema/syncDeltaRow.js +4 -105
  136. package/dist/server/adapter.d.ts +18 -1
  137. package/dist/server/commit.d.ts +10 -16
  138. package/dist/server/index.d.ts +1 -1
  139. package/dist/server/index.js +1 -1
  140. package/dist/server/readConfig.d.ts +1 -1
  141. package/dist/source/adapters/drizzle.d.ts +1 -1
  142. package/dist/source/adapters/drizzle.js +2 -2
  143. package/dist/source/adapters/kysely.d.ts +1 -1
  144. package/dist/source/adapters/kysely.js +1 -1
  145. package/dist/source/adapters/kyselyMutationCore.d.ts +1 -1
  146. package/dist/source/adapters/kyselyMutationCore.js +2 -2
  147. package/dist/source/adapters/memory.js +1 -1
  148. package/dist/source/adapters/prisma.d.ts +8 -3
  149. package/dist/source/adapters/prisma.js +1 -1
  150. package/dist/source/connector.js +1 -1
  151. package/dist/source/connectorProtocol.d.ts +2 -8
  152. package/dist/source/connectorProtocol.js +3 -2
  153. package/dist/source/contract.d.ts +29 -17
  154. package/dist/source/contract.js +27 -22
  155. package/dist/source/factory.d.ts +1 -1
  156. package/dist/source/footprint.d.ts +111 -0
  157. package/dist/source/footprint.js +0 -0
  158. package/dist/source/idempotency.js +2 -2
  159. package/dist/source/index.d.ts +1 -0
  160. package/dist/source/index.js +3 -0
  161. package/dist/source/next.d.ts +1 -1
  162. package/dist/source/signing.d.ts +9 -2
  163. package/dist/source/signing.js +4 -1
  164. package/dist/source/types.d.ts +6 -4
  165. package/dist/source/types.js +1 -1
  166. package/dist/stores/ObjectStore.d.ts +1 -1
  167. package/dist/stores/SyncActionStore.d.ts +1 -1
  168. package/dist/stores/SyncActionStore.js +2 -10
  169. package/dist/stores/syncAction.d.ts +26 -0
  170. package/dist/stores/syncAction.js +16 -0
  171. package/dist/surface.d.ts +3 -3
  172. package/dist/surface.js +6 -4
  173. package/dist/sync/BootstrapFetcher.d.ts +123 -6
  174. package/dist/sync/BootstrapFetcher.js +492 -66
  175. package/dist/sync/ConnectionManager.d.ts +6 -198
  176. package/dist/sync/ConnectionManager.js +6 -677
  177. package/dist/sync/OnDemandLoader.d.ts +2 -2
  178. package/dist/sync/OnDemandLoader.js +60 -21
  179. package/dist/sync/SubscriptionManager.d.ts +13 -2
  180. package/dist/sync/SubscriptionManager.js +23 -5
  181. package/dist/sync/SyncWebSocket.d.ts +27 -510
  182. package/dist/sync/SyncWebSocket.js +76 -954
  183. package/dist/sync/awaitClaimGrant.d.ts +4 -44
  184. package/dist/sync/awaitClaimGrant.js +4 -109
  185. package/dist/sync/commitFrames.d.ts +6 -40
  186. package/dist/sync/commitFrames.js +6 -97
  187. package/dist/sync/contextPorts.d.ts +18 -0
  188. package/dist/sync/contextPorts.js +31 -0
  189. package/dist/sync/createClaimStream.d.ts +5 -49
  190. package/dist/sync/createClaimStream.js +5 -469
  191. package/dist/sync/createPresenceStream.d.ts +26 -4
  192. package/dist/sync/createPresenceStream.js +28 -20
  193. package/dist/sync/createSnapshot.d.ts +2 -2
  194. package/dist/sync/createSnapshot.js +1 -1
  195. package/dist/sync/credentialLifecycle.d.ts +5 -173
  196. package/dist/sync/credentialLifecycle.js +5 -320
  197. package/dist/sync/deltaPipeline.d.ts +1 -1
  198. package/dist/sync/participants.d.ts +5 -4
  199. package/dist/sync/participants.js +29 -22
  200. package/dist/sync/schemaDrift.d.ts +55 -0
  201. package/dist/sync/schemaDrift.js +53 -0
  202. package/dist/sync/schemas.d.ts +21 -32
  203. package/dist/sync/schemas.js +26 -17
  204. package/dist/sync/syncPlan.d.ts +3 -3
  205. package/dist/sync/wsFrameHandlers.d.ts +6 -114
  206. package/dist/sync/wsFrameHandlers.js +6 -392
  207. package/dist/testing/fixtures/bootstrap.d.ts +1 -1
  208. package/dist/testing/fixtures/deltas.d.ts +1 -1
  209. package/dist/testing/fixtures/httpResponses.d.ts +70 -0
  210. package/dist/testing/fixtures/httpResponses.js +90 -0
  211. package/dist/testing/fixtures/models.js +1 -1
  212. package/dist/testing/helpers/wait.js +1 -1
  213. package/dist/testing/mocks/MockMutationExecutor.d.ts +2 -2
  214. package/dist/testing/mocks/MockMutationExecutor.js +8 -14
  215. package/dist/testing/mocks/MockSyncContext.d.ts +11 -11
  216. package/dist/testing/mocks/MockSyncContext.js +10 -9
  217. package/dist/testing/mocks/MockSyncStore.js +1 -1
  218. package/dist/testing/mocks/MockWebSocket.d.ts +2 -2
  219. package/dist/transaction/ablo.d.ts +88 -0
  220. package/dist/transaction/ablo.js +33 -0
  221. package/dist/{client/auth.d.ts → transaction/auth/apiKey.d.ts} +43 -8
  222. package/dist/{client/auth.js → transaction/auth/apiKey.js} +19 -0
  223. package/dist/transaction/auth/bootstrapScope.d.ts +15 -0
  224. package/dist/transaction/auth/bootstrapScope.js +1 -0
  225. package/dist/transaction/auth/capability.d.ts +177 -0
  226. package/dist/transaction/auth/capability.js +199 -0
  227. package/dist/{auth → transaction/auth}/credentialSource.d.ts +8 -1
  228. package/dist/{client → transaction/auth}/identity.d.ts +8 -7
  229. package/dist/{client → transaction/auth}/identity.js +1 -1
  230. package/dist/transaction/auth/index.d.ts +162 -0
  231. package/dist/transaction/auth/index.js +304 -0
  232. package/dist/{auth → transaction/auth}/schemas.d.ts +1 -1
  233. package/dist/{auth → transaction/auth}/schemas.js +13 -13
  234. package/dist/{client → transaction/auth}/sessionMint.d.ts +8 -8
  235. package/dist/{client → transaction/auth}/sessionMint.js +4 -7
  236. package/dist/transaction/coordination/awaitClaimGrant.d.ts +49 -0
  237. package/dist/transaction/coordination/awaitClaimGrant.js +112 -0
  238. package/dist/transaction/coordination/claimMeta.d.ts +49 -0
  239. package/dist/transaction/coordination/claimMeta.js +52 -0
  240. package/dist/transaction/coordination/createClaimStream.d.ts +64 -0
  241. package/dist/transaction/coordination/createClaimStream.js +475 -0
  242. package/dist/transaction/coordination/events.d.ts +74 -0
  243. package/dist/transaction/coordination/events.js +7 -0
  244. package/dist/transaction/coordination/index.d.ts +19 -0
  245. package/dist/transaction/coordination/index.js +44 -0
  246. package/dist/transaction/coordination/locator.d.ts +83 -0
  247. package/dist/transaction/coordination/locator.js +82 -0
  248. package/dist/transaction/coordination/schema.d.ts +1473 -0
  249. package/dist/{coordination → transaction/coordination}/schema.js +490 -55
  250. package/dist/transaction/coordination/targetConflict.d.ts +2 -0
  251. package/dist/transaction/coordination/targetConflict.js +103 -0
  252. package/dist/{coordination → transaction/coordination}/trace.d.ts +7 -18
  253. package/dist/{coordination → transaction/coordination}/trace.js +18 -25
  254. package/dist/transaction/durableWrites.d.ts +62 -0
  255. package/dist/{client → transaction}/durableWrites.js +28 -3
  256. package/dist/transaction/environment.d.ts +105 -0
  257. package/dist/transaction/environment.js +108 -0
  258. package/dist/{errorCodes.d.ts → transaction/errorCodes.d.ts} +11 -11
  259. package/dist/{errorCodes.js → transaction/errorCodes.js} +35 -12
  260. package/dist/{errors.d.ts → transaction/errors.d.ts} +37 -13
  261. package/dist/{errors.js → transaction/errors.js} +85 -16
  262. package/dist/transaction/index.d.ts +20 -0
  263. package/dist/transaction/index.js +20 -0
  264. package/dist/transaction/keys/index.d.ts +87 -0
  265. package/dist/transaction/keys/index.js +207 -0
  266. package/dist/transaction/log/syncDeltaRow.d.ts +158 -0
  267. package/dist/transaction/log/syncDeltaRow.js +95 -0
  268. package/dist/{sync/syncPosition.d.ts → transaction/logPosition.d.ts} +22 -8
  269. package/dist/{sync/syncPosition.js → transaction/logPosition.js} +15 -6
  270. package/dist/transaction/logger.d.ts +16 -0
  271. package/dist/transaction/logger.js +7 -0
  272. package/dist/transaction/observability.d.ts +53 -0
  273. package/dist/transaction/observability.js +19 -0
  274. package/dist/transaction/plugin.d.ts +192 -0
  275. package/dist/transaction/plugin.js +87 -0
  276. package/dist/{policy → transaction/policy}/types.d.ts +3 -3
  277. package/dist/{policy → transaction/policy}/types.js +2 -0
  278. package/dist/transaction/resources/httpResources.d.ts +266 -0
  279. package/dist/transaction/resources/httpResources.js +7 -0
  280. package/dist/transaction/resources/modelOperations.d.ts +319 -0
  281. package/dist/transaction/resources/modelOperations.js +12 -0
  282. package/dist/transaction/resources/mutationOptions.d.ts +66 -0
  283. package/dist/transaction/resources/mutationOptions.js +9 -0
  284. package/dist/transaction/resources/where.d.ts +85 -0
  285. package/dist/transaction/resources/where.js +70 -0
  286. package/dist/{client → transaction/resources}/writeOptionsSchema.d.ts +2 -6
  287. package/dist/{client → transaction/resources}/writeOptionsSchema.js +9 -5
  288. package/dist/{schema → transaction/schema}/field.d.ts +5 -5
  289. package/dist/{schema → transaction/schema}/field.js +5 -5
  290. package/dist/transaction/schema/loadStrategy.d.ts +45 -0
  291. package/dist/transaction/schema/loadStrategy.js +46 -0
  292. package/dist/{schema → transaction/schema}/model.d.ts +50 -35
  293. package/dist/{schema → transaction/schema}/model.js +30 -20
  294. package/dist/transaction/schema/openapi.d.ts +57 -0
  295. package/dist/transaction/schema/openapi.js +340 -0
  296. package/dist/{schema → transaction/schema}/relation.d.ts +14 -14
  297. package/dist/{schema → transaction/schema}/relation.js +7 -7
  298. package/dist/{schema → transaction/schema}/residency.d.ts +0 -9
  299. package/dist/{schema → transaction/schema}/residency.js +0 -5
  300. package/dist/{schema → transaction/schema}/roles.d.ts +5 -5
  301. package/dist/{schema → transaction/schema}/roles.js +5 -5
  302. package/dist/{schema → transaction/schema}/schema.d.ts +12 -10
  303. package/dist/{schema → transaction/schema}/schema.js +4 -3
  304. package/dist/{schema → transaction/schema}/tenancy.d.ts +1 -1
  305. package/dist/{schema → transaction/schema}/tenancy.js +7 -4
  306. package/dist/transaction/transactionLayer.d.ts +82 -0
  307. package/dist/transaction/transactionLayer.js +24 -0
  308. package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.d.ts +4 -5
  309. package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.js +9 -13
  310. package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.d.ts +9 -2
  311. package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.js +5 -9
  312. package/dist/{transactions/durableWriteStore.d.ts → transaction/transactions/settlement/pendingWrite.d.ts} +10 -36
  313. package/dist/transaction/transactions/settlement/pendingWrite.js +20 -0
  314. package/dist/transaction/transport/commitFrames.d.ts +90 -0
  315. package/dist/transaction/transport/commitFrames.js +134 -0
  316. package/dist/transaction/transport/connectionManager.d.ts +215 -0
  317. package/dist/transaction/transport/connectionManager.js +673 -0
  318. package/dist/transaction/transport/credentialLifecycle.d.ts +177 -0
  319. package/dist/transaction/transport/credentialLifecycle.js +324 -0
  320. package/dist/{sync → transaction/transport}/heartbeat.d.ts +3 -1
  321. package/dist/{sync → transaction/transport}/heartbeat.js +6 -4
  322. package/dist/{client → transaction/transport}/httpClient.d.ts +59 -16
  323. package/dist/{client → transaction/transport}/httpClient.js +5 -5
  324. package/dist/transaction/transport/httpOptions.d.ts +33 -0
  325. package/dist/transaction/transport/httpOptions.js +12 -0
  326. package/dist/{client → transaction/transport}/httpTransport.js +171 -85
  327. package/dist/{sync/NetworkProbe.d.ts → transaction/transport/networkProbe.d.ts} +7 -4
  328. package/dist/{sync/NetworkProbe.js → transaction/transport/networkProbe.js} +14 -13
  329. package/dist/transaction/transport/wsFrameHandlers.d.ts +128 -0
  330. package/dist/transaction/transport/wsFrameHandlers.js +429 -0
  331. package/dist/transaction/transport/wsTransport.d.ts +576 -0
  332. package/dist/transaction/transport/wsTransport.js +1017 -0
  333. package/dist/transaction/types/assertExact.d.ts +17 -0
  334. package/dist/transaction/types/assertExact.js +1 -0
  335. package/dist/{types → transaction/types}/global.d.ts +17 -2
  336. package/dist/{types → transaction/types}/global.js +2 -1
  337. package/dist/{types → transaction/types}/index.d.ts +14 -46
  338. package/dist/{types → transaction/types}/index.js +7 -16
  339. package/dist/{types → transaction/types}/streams.d.ts +63 -45
  340. package/dist/{utils → transaction/utils}/json.d.ts +18 -0
  341. package/dist/transaction/utils/json.js +276 -0
  342. package/dist/transaction/wire/accountResponses.d.ts +351 -0
  343. package/dist/transaction/wire/accountResponses.js +255 -0
  344. package/dist/transaction/wire/auth.d.ts +49 -0
  345. package/dist/transaction/wire/auth.js +57 -0
  346. package/dist/transaction/wire/claimEvent.d.ts +76 -0
  347. package/dist/transaction/wire/claimEvent.js +73 -0
  348. package/dist/transaction/wire/claims.d.ts +463 -0
  349. package/dist/transaction/wire/claims.js +229 -0
  350. package/dist/{wire → transaction/wire}/commit.d.ts +125 -193
  351. package/dist/{wire → transaction/wire}/commit.js +68 -47
  352. package/dist/{wire → transaction/wire}/delta.d.ts +66 -17
  353. package/dist/{wire → transaction/wire}/delta.js +37 -13
  354. package/dist/transaction/wire/errorEnvelope.d.ts +72 -0
  355. package/dist/{wire → transaction/wire}/errorEnvelope.js +36 -5
  356. package/dist/transaction/wire/feedCursor.d.ts +60 -0
  357. package/dist/transaction/wire/feedCursor.js +82 -0
  358. package/dist/transaction/wire/feedEvent.d.ts +177 -0
  359. package/dist/transaction/wire/feedEvent.js +39 -0
  360. package/dist/transaction/wire/frames.d.ts +194 -0
  361. package/dist/transaction/wire/frames.js +50 -0
  362. package/dist/transaction/wire/inboundFrames.d.ts +552 -0
  363. package/dist/transaction/wire/inboundFrames.js +116 -0
  364. package/dist/transaction/wire/index.d.ts +50 -0
  365. package/dist/transaction/wire/index.js +74 -0
  366. package/dist/{wire → transaction/wire}/listEnvelope.d.ts +13 -14
  367. package/dist/transaction/wire/listEnvelope.js +42 -0
  368. package/dist/transaction/wire/modelResponses.d.ts +85 -0
  369. package/dist/transaction/wire/modelResponses.js +43 -0
  370. package/dist/transactions/{TransactionQueue.d.ts → mutations/MutationQueue.d.ts} +79 -38
  371. package/dist/transactions/{TransactionQueue.js → mutations/MutationQueue.js} +110 -59
  372. package/dist/transactions/{TransactionStore.d.ts → mutations/MutationStore.d.ts} +8 -8
  373. package/dist/transactions/{TransactionStore.js → mutations/MutationStore.js} +2 -2
  374. package/dist/transactions/{coalesceRules.d.ts → mutations/coalesceRules.d.ts} +10 -10
  375. package/dist/transactions/{coalesceRules.js → mutations/coalesceRules.js} +1 -1
  376. package/dist/transactions/mutations/commitLatency.d.ts +52 -0
  377. package/dist/transactions/mutations/commitLatency.js +130 -0
  378. package/dist/transactions/{commitOutboxStore.d.ts → mutations/commitOutboxStore.d.ts} +1 -5
  379. package/dist/transactions/{commitOutboxStore.js → mutations/commitOutboxStore.js} +1 -1
  380. package/dist/transactions/{commitPayload.d.ts → mutations/commitPayload.d.ts} +16 -15
  381. package/dist/transactions/{commitPayload.js → mutations/commitPayload.js} +12 -12
  382. package/dist/transactions/{deltaConfirmation.d.ts → mutations/deltaConfirmation.d.ts} +11 -11
  383. package/dist/transactions/{deltaConfirmation.js → mutations/deltaConfirmation.js} +7 -7
  384. package/dist/transactions/mutations/durableWriteStore.d.ts +14 -0
  385. package/dist/transactions/mutations/durableWriteStore.js +12 -0
  386. package/dist/transactions/{optimisticApply.d.ts → mutations/optimisticApply.d.ts} +7 -7
  387. package/dist/transactions/{replayValidation.d.ts → mutations/replayValidation.d.ts} +3 -3
  388. package/dist/transactions/{replayValidation.js → mutations/replayValidation.js} +4 -3
  389. package/dist/utils/mobxSetup.d.ts +1 -1
  390. package/dist/utils/mobxSetup.js +5 -2
  391. package/dist/webhooks/events.d.ts +2 -2
  392. package/dist/wire/index.d.ts +1 -34
  393. package/dist/wire/index.js +8 -49
  394. package/docs/agent-messaging.md +3 -3
  395. package/docs/agents.md +19 -12
  396. package/docs/api-keys.md +8 -4
  397. package/docs/api.md +22 -18
  398. package/docs/audit.md +2 -0
  399. package/docs/cli.md +31 -3
  400. package/docs/client-behavior.md +8 -6
  401. package/docs/concurrency-convention.md +30 -24
  402. package/docs/coordination.md +48 -38
  403. package/docs/data-sources.md +3 -1
  404. package/docs/debugging.md +5 -3
  405. package/docs/deployment.md +267 -0
  406. package/docs/examples/agent-human.md +49 -42
  407. package/docs/examples/ai-sdk-tool.md +69 -44
  408. package/docs/examples/existing-python-backend.md +8 -6
  409. package/docs/examples/nextjs.md +129 -47
  410. package/docs/examples/scoped-agent.md +45 -44
  411. package/docs/examples/server-agent.md +46 -26
  412. package/docs/groups.md +32 -29
  413. package/docs/guarantees.md +4 -2
  414. package/docs/how-it-works.md +9 -7
  415. package/docs/idempotency.md +126 -0
  416. package/docs/identity.md +58 -54
  417. package/docs/index.md +172 -86
  418. package/docs/integration-guide.md +17 -16
  419. package/docs/interaction-model.md +6 -4
  420. package/docs/mcp.md +41 -16
  421. package/docs/migration.md +63 -5
  422. package/docs/operating-on-your-database.md +3 -1
  423. package/docs/projects.md +2 -0
  424. package/docs/quickstart.md +22 -5
  425. package/docs/react.md +12 -10
  426. package/docs/schema-contract.md +5 -3
  427. package/docs/session-settings.md +108 -0
  428. package/docs/sessions.md +3 -1
  429. package/docs/webhooks.md +3 -1
  430. package/llms.txt +47 -17
  431. package/package.json +10 -8
  432. package/dist/agent/Agent.d.ts +0 -366
  433. package/dist/agent/Agent.js +0 -514
  434. package/dist/agent/index.d.ts +0 -115
  435. package/dist/agent/index.js +0 -128
  436. package/dist/agent/session.d.ts +0 -93
  437. package/dist/agent/session.js +0 -149
  438. package/dist/agent/types.d.ts +0 -68
  439. package/dist/agent/types.js +0 -9
  440. package/dist/client/durableWrites.d.ts +0 -21
  441. package/dist/coordination/schema.d.ts +0 -722
  442. package/dist/schema/openapi.d.ts +0 -29
  443. package/dist/schema/openapi.js +0 -124
  444. package/dist/transactions/durableWriteStore.js +0 -30
  445. package/dist/utils/json.js +0 -88
  446. package/dist/wire/errorEnvelope.d.ts +0 -55
  447. package/dist/wire/frames.d.ts +0 -197
  448. package/dist/wire/frames.js +0 -49
  449. package/dist/wire/listEnvelope.js +0 -18
  450. /package/dist/{client → transaction/auth}/credentialEndpoint.d.ts +0 -0
  451. /package/dist/{client → transaction/auth}/credentialEndpoint.js +0 -0
  452. /package/dist/{auth → transaction/auth}/credentialPolicy.d.ts +0 -0
  453. /package/dist/{auth → transaction/auth}/credentialPolicy.js +0 -0
  454. /package/dist/{auth → transaction/auth}/credentialSource.js +0 -0
  455. /package/dist/{client → transaction/auth}/hostedEndpoints.d.ts +0 -0
  456. /package/dist/{client → transaction/auth}/hostedEndpoints.js +0 -0
  457. /package/dist/{client → transaction/coordination}/claimHeartbeatLoop.d.ts +0 -0
  458. /package/dist/{client → transaction/coordination}/claimHeartbeatLoop.js +0 -0
  459. /package/dist/{client → transaction}/persistence.d.ts +0 -0
  460. /package/dist/{client → transaction}/persistence.js +0 -0
  461. /package/dist/{client → transaction/resources}/functionalUpdate.d.ts +0 -0
  462. /package/dist/{client → transaction/resources}/functionalUpdate.js +0 -0
  463. /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.d.ts +0 -0
  464. /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.js +0 -0
  465. /package/dist/{client → transaction/transport}/httpTransport.d.ts +0 -0
  466. /package/dist/{types → transaction/types}/modelData.d.ts +0 -0
  467. /package/dist/{types → transaction/types}/modelData.js +0 -0
  468. /package/dist/{types → transaction/types}/participant.d.ts +0 -0
  469. /package/dist/{types → transaction/types}/participant.js +0 -0
  470. /package/dist/{types → transaction/types}/streams.js +0 -0
  471. /package/dist/{utils → transaction/utils}/asyncIterator.d.ts +0 -0
  472. /package/dist/{utils → transaction/utils}/asyncIterator.js +0 -0
  473. /package/dist/{utils → transaction/utils}/duration.d.ts +0 -0
  474. /package/dist/{utils → transaction/utils}/duration.js +0 -0
  475. /package/dist/{wire → transaction/wire}/bootstrapReason.d.ts +0 -0
  476. /package/dist/{wire → transaction/wire}/bootstrapReason.js +0 -0
  477. /package/dist/{wire → transaction/wire}/protocol.d.ts +0 -0
  478. /package/dist/{wire → transaction/wire}/protocol.js +0 -0
  479. /package/dist/{wire → transaction/wire}/protocolVersion.d.ts +0 -0
  480. /package/dist/{wire → transaction/wire}/protocolVersion.js +0 -0
  481. /package/dist/transactions/{UnconfirmedWrites.d.ts → mutations/UnconfirmedWrites.d.ts} +0 -0
  482. /package/dist/transactions/{UnconfirmedWrites.js → mutations/UnconfirmedWrites.js} +0 -0
  483. /package/dist/transactions/{optimisticApply.js → mutations/optimisticApply.js} +0 -0
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TransactionQueue manages the lifecycle of local writes on their way to the
2
+ * MutationQueue manages the lifecycle of local writes on their way to the
3
3
  * server: it applies each change optimistically, batches the writes made in one
4
4
  * event-loop tick into a single commit, retries transient failures, and rolls
5
5
  * back on permanent rejection.
@@ -13,25 +13,25 @@
13
13
  */
14
14
  import { EventEmitter } from 'events';
15
15
  import { v4 as uuid } from 'uuid';
16
- import { Model } from '../Model.js';
17
- import { getContext } from '../context.js';
18
- import { AbloError, AbloConnectionError, AbloIdempotencyError, AbloNotFoundError, AbloValidationError, errorCodeSpec, } from '../errors.js';
19
- import { SyncPosition } from '../sync/syncPosition.js';
20
- import { mutationCommitResultSchema, } from '../wire/commit.js';
16
+ import { Model } from '../../Model.js';
17
+ import { getContext } from '../../context.js';
18
+ import { AbloError, AbloConnectionError, AbloIdempotencyError, AbloNotFoundError, AbloValidationError, errorCodeSpec, } from '../../transaction/errors.js';
19
+ import { LogPosition } from '../../transaction/logPosition.js';
20
+ import { mutationCommitResultSchema, } from '../../transaction/wire/commit.js';
21
21
  import { projectCommitPayload, computePriorityScore, normalizeModelKey,
22
22
  // Includes stale guards as well as request identity/audit barriers.
23
23
  hasCommitCoalescingBarrier, applyWriteOptions, asTransportError, extractStatusCode, TX_TYPE_TO_MUTATION_OP, } from './commitPayload.js';
24
- import { TransactionStore } from './TransactionStore.js';
24
+ import { MutationStore } from './MutationStore.js';
25
25
  import { entityKey, mergeUpdateData, takeUnsentCreateForModel, findCreateBarrierForDelete, deferDeleteUntilCreateSettles, releaseDeferredDeletesForCreate, } from './coalesceRules.js';
26
26
  import { DeltaConfirmationTracker } from './deltaConfirmation.js';
27
27
  import { deserializePersistedTransaction, isNonReplayablePersistedRow, pendingMutationRecordId, } from './replayValidation.js';
28
- import { createCommitEnvelopeMember, createDurableCommitEnvelope, commitEnvelopeRecordId, durableCommitEnvelopeSchema, } from './commitEnvelope.js';
29
- import { stableStringify } from '../utils/json.js';
28
+ import { createCommitEnvelopeMember, createDurableCommitEnvelope, commitEnvelopeRecordId, durableCommitEnvelopeSchema, } from '../../transaction/transactions/settlement/commitEnvelope.js';
29
+ import { stableStringify } from '../../transaction/utils/json.js';
30
30
  import { applyOptimisticCreate, applyOptimisticUpdate, applyOptimisticDelete, rollbackOptimistic, } from './optimisticApply.js';
31
- export class TransactionQueue extends EventEmitter {
31
+ export class MutationQueue extends EventEmitter {
32
32
  // Keep one hour of clock/network margin inside the server's 24-hour ledger.
33
33
  static DURABLE_REPLAY_WINDOW_MS = 23 * 60 * 60 * 1000;
34
- store = new TransactionStore();
34
+ store = new MutationStore();
35
35
  // Signature of the last permanent-error we logged at `warn`. A `create`
36
36
  // whose id already exists (`unique_violation`) is a permanent rejection
37
37
  // that the offline queue re-drives on every reconnect/bootstrap — without
@@ -100,7 +100,7 @@ export class TransactionQueue extends EventEmitter {
100
100
  this.emit(event, payload);
101
101
  }
102
102
  catch (error) {
103
- getContext().observability.captureTransactionFailure({
103
+ getContext().observability.captureMutationFailure({
104
104
  context: `commit-lifecycle-listener:${event}`,
105
105
  error: error instanceof Error ? error : String(error),
106
106
  });
@@ -114,8 +114,14 @@ export class TransactionQueue extends EventEmitter {
114
114
  this.assertDurableReplayOpen();
115
115
  if (envelope.acceptedAt === undefined &&
116
116
  Date.now() - envelope.sealedAt >=
117
- TransactionQueue.DURABLE_REPLAY_WINDOW_MS) {
117
+ MutationQueue.DURABLE_REPLAY_WINDOW_MS) {
118
118
  this.durableReplayBlock = new AbloIdempotencyError('A pending commit is older than the server idempotency window; newer writes are blocked until it is reviewed.', { code: 'idempotency_conflict' });
119
+ // This gate stops EVERY subsequent write on this client, and each of
120
+ // those rejections is captured to observability rather than surfaced to
121
+ // the caller — without this line the session degrades into "nothing
122
+ // saves and nothing errors". One loud line at the moment the block
123
+ // engages is the only visible trace.
124
+ getContext().logger.warn('sync paused: a saved write from an earlier session is older than the server replay window, so newer writes are held until it is reviewed', { sealedAt: envelope.sealedAt });
119
125
  throw this.durableReplayBlock;
120
126
  }
121
127
  }
@@ -256,6 +262,7 @@ export class TransactionQueue extends EventEmitter {
256
262
  deltaConfirmationTimeout: 30000,
257
263
  retryBackoff: { baseMs: 200, capMs: 1500 },
258
264
  commitOfflineGraceMs: 30_000,
265
+ commitDispatchTimeoutMs: 30_000,
259
266
  };
260
267
  // Track executing transactions for backpressure
261
268
  executingCount = 0;
@@ -264,7 +271,7 @@ export class TransactionQueue extends EventEmitter {
264
271
  // completion paths, `getStats`, and `dispose` all read it.
265
272
  optimisticUpdates = new Map();
266
273
  // Stale-context notifications, keyed by transaction id. When the server
267
- // accepts a commit but reports that an operation's read premise had moved,
274
+ // accepts a commit but reports that an operation's premise had moved,
268
275
  // the notification lands here from the commit acknowledgement and is drained
269
276
  // by `waitForCommitReceipt`, so the receipt can carry it back to the caller.
270
277
  commitNotifications = new Map();
@@ -287,7 +294,7 @@ export class TransactionQueue extends EventEmitter {
287
294
  * shared: the client injects one, and a standalone queue creates its own. The
288
295
  * queue advances the `acked` cursor as commit responses arrive, the store
289
296
  * advances `applied` and `persisted`, and snapshots and claims read
290
- * `readFloor`. See `../sync/syncPosition.js` for the full contract.
297
+ * `readFloor`. See `../logPosition.js` for the full contract.
291
298
  */
292
299
  position;
293
300
  /** Applied-cursor alias, kept so the many internal read sites stay legible. */
@@ -390,9 +397,6 @@ export class TransactionQueue extends EventEmitter {
390
397
  operations: [...input.operations],
391
398
  sourceMutationIds,
392
399
  commitOptions: {
393
- ...(input.commitOptions?.causedByTaskId !== undefined
394
- ? { causedByTaskId: input.commitOptions.causedByTaskId }
395
- : {}),
396
400
  ...(input.commitOptions?.reads !== undefined
397
401
  ? {
398
402
  reads: input.commitOptions.reads === null
@@ -440,7 +444,7 @@ export class TransactionQueue extends EventEmitter {
440
444
  await this.commitOutbox.remove(commitEnvelopeRecordId(idempotencyKey));
441
445
  }
442
446
  catch (error) {
443
- getContext().logger.debug('[TransactionQueue] Durable-write cleanup deferred', {
447
+ getContext().logger.debug('[MutationQueue] Durable-write cleanup deferred', {
444
448
  idempotencyKey,
445
449
  error: error instanceof Error ? error.message : String(error),
446
450
  });
@@ -489,6 +493,35 @@ export class TransactionQueue extends EventEmitter {
489
493
  return parsed.data;
490
494
  throw new AbloConnectionError('The mutation transport returned an invalid commit receipt; its outcome remains pending and is safe to retry.', { code: 'commit_no_result', cause: parsed.error });
491
495
  }
496
+ /**
497
+ * Dispatch a sealed envelope to the transport, bounded by
498
+ * `commitDispatchTimeoutMs`. Every executor call site routes through here:
499
+ * a transport that never answers must become a retryable no-receipt failure
500
+ * (the same class as a malformed receipt) instead of an eternally in-flight
501
+ * commit, because an unanswered commit holds the staged-batch lock and
502
+ * silently stalls every later write in the session. The abandoned commit's
503
+ * eventual result, if it ever arrives, is discarded; the retry re-sends the
504
+ * identical idempotency key, so the server deduplicates an already-applied
505
+ * write.
506
+ */
507
+ dispatchCommitBounded(...args) {
508
+ const timeoutMs = this.config.commitDispatchTimeoutMs;
509
+ const dispatched = this.mutationExecutor.commit(...args);
510
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
511
+ return dispatched;
512
+ return new Promise((resolve, reject) => {
513
+ const timer = setTimeout(() => {
514
+ reject(new AbloConnectionError('The mutation transport did not acknowledge the commit in time; its outcome remains pending and is safe to retry.', { code: 'commit_no_result' }));
515
+ }, timeoutMs);
516
+ dispatched.then((value) => {
517
+ clearTimeout(timer);
518
+ resolve(value);
519
+ }, (error) => {
520
+ clearTimeout(timer);
521
+ reject(error instanceof Error ? error : new Error(String(error)));
522
+ });
523
+ });
524
+ }
492
525
  clearReplicationLagState(transactionId) {
493
526
  const timeout = this.replicationLagTimeouts.get(transactionId);
494
527
  if (timeout)
@@ -620,13 +653,13 @@ export class TransactionQueue extends EventEmitter {
620
653
  * Resolvers for per-transaction `confirmation` promises. Populated in
621
654
  * `attachConfirmation` at staging time, consumed by the constructor-time
622
655
  * listeners on `transaction:completed` / `transaction:failed`. Kept off
623
- * the Transaction row so the store's iteration order stays plain-data
656
+ * the QueuedMutation row so the store's iteration order stays plain-data
624
657
  * and serialization-friendly.
625
658
  */
626
659
  confirmationResolvers = new Map();
627
660
  constructor(config) {
628
661
  super();
629
- this.position = config?.position ?? new SyncPosition();
662
+ this.position = config?.position ?? new LogPosition();
630
663
  // Bind the confirmation tracker to this queue's store/ledger/events.
631
664
  // `isConnected` closes over `isConnectedFn` so `setConnectionChecker`
632
665
  // swaps stay visible to in-flight timeouts.
@@ -683,7 +716,7 @@ export class TransactionQueue extends EventEmitter {
683
716
  * be rolled back — `pending`, `executing`, and `awaiting_delta` — and ignores
684
717
  * `completed` (already settled) and `failed`/`rolled_back` (already
685
718
  * rejected). This complements the `confirmation` promise carried on a known
686
- * {@link Transaction}: use this method at call sites that hold a model
719
+ * {@link QueuedMutation}: use this method at call sites that hold a model
687
720
  * returned by `ablo.<model>.create()` but never see the underlying
688
721
  * transaction.
689
722
  */
@@ -771,7 +804,7 @@ export class TransactionQueue extends EventEmitter {
771
804
  return;
772
805
  // Each failed commit reaches the consumer through its own rejection path,
773
806
  // so this aggregate line is forensic and logged at debug rather than warn.
774
- getContext().logger.debug(`[TransactionQueue] WS disconnected > ${graceMs}ms; failing ${inFlight.length} in-flight commit(s) with AbloConnectionError`, { inFlightIds: inFlight.map((id) => id.slice(0, 8)) });
807
+ getContext().logger.debug(`[MutationQueue] WS disconnected > ${graceMs}ms; failing ${inFlight.length} in-flight commit(s) with AbloConnectionError`, { inFlightIds: inFlight.map((id) => id.slice(0, 8)) });
775
808
  for (const id of inFlight) {
776
809
  const tx = this.commitStore.get(id);
777
810
  if (!tx)
@@ -844,7 +877,7 @@ export class TransactionQueue extends EventEmitter {
844
877
  this.batchIndex++;
845
878
  const currentBatchIndex = this.batchIndex;
846
879
  // Log batch commit for performance monitoring
847
- getContext().logger.debug('[TransactionQueue] commitCreatedTransactions', {
880
+ getContext().logger.debug('[MutationQueue] commitCreatedTransactions', {
848
881
  count: this.createdTransactions.length,
849
882
  batchIndex: currentBatchIndex,
850
883
  types: this.createdTransactions.map((t) => `${t.type}:${t.modelName}`),
@@ -907,7 +940,7 @@ export class TransactionQueue extends EventEmitter {
907
940
  sequence: batch[0]?.commitEnvelope?.sequence,
908
941
  });
909
942
  this.assertEnvelopeInsideReplayWindow(durableEnvelope);
910
- const result = this.parseMutationCommitResult(await this.mutationExecutor.commit(durableEnvelope.operations, {
943
+ const result = this.parseMutationCommitResult(await this.dispatchCommitBounded(durableEnvelope.operations, {
911
944
  idempotencyKey,
912
945
  }));
913
946
  await this.persistDurableCommitAcceptance(durableEnvelope, result);
@@ -976,7 +1009,7 @@ export class TransactionQueue extends EventEmitter {
976
1009
  }
977
1010
  /**
978
1011
  * Records a create and applies it optimistically, then stages it for the next
979
- * batched commit. Returns the {@link Transaction}, whose `confirmation`
1012
+ * batched commit. Returns the {@link QueuedMutation}, whose `confirmation`
980
1013
  * promise settles once the server confirms the write.
981
1014
  */
982
1015
  async create(model, context, writeOptions, sourceMutationId) {
@@ -1080,7 +1113,7 @@ export class TransactionQueue extends EventEmitter {
1080
1113
  const actualModelName = model.getModelName();
1081
1114
  // Skip Activity delete transactions - activities are permanent audit records
1082
1115
  if (actualModelName === 'Activity') {
1083
- getContext().logger.debug('TransactionQueue.delete() skipping Activity deletion - permanent audit records', { modelId: model.id });
1116
+ getContext().logger.debug('MutationQueue.delete() skipping Activity deletion - permanent audit records', { modelId: model.id });
1084
1117
  const modelKey = normalizeModelKey(actualModelName);
1085
1118
  const priorityScore = this.computePriorityScore('delete', actualModelName);
1086
1119
  const mockTransaction = {
@@ -1319,7 +1352,7 @@ export class TransactionQueue extends EventEmitter {
1319
1352
  // are already executing, so the server is not flooded with concurrent
1320
1353
  // requests.
1321
1354
  if (this.executingCount >= this.config.maxExecutingTransactions) {
1322
- getContext().logger.debug('[TransactionQueue] Backpressure: delaying batch, too many executing', {
1355
+ getContext().logger.debug('[MutationQueue] Backpressure: delaying batch, too many executing', {
1323
1356
  executingCount: this.executingCount,
1324
1357
  max: this.config.maxExecutingTransactions,
1325
1358
  });
@@ -1432,7 +1465,7 @@ export class TransactionQueue extends EventEmitter {
1432
1465
  // the exact key assigned before the first transport attempt.
1433
1466
  this.assertEnvelopeInsideReplayWindow(durableEnvelope);
1434
1467
  dispatchStarted = true;
1435
- const result = this.parseMutationCommitResult(await this.mutationExecutor.commit(operations, {
1468
+ const result = this.parseMutationCommitResult(await this.dispatchCommitBounded(operations, {
1436
1469
  idempotencyKey: commitIdempotencyKey,
1437
1470
  }));
1438
1471
  await this.persistDurableCommitAcceptance(durableEnvelope, result);
@@ -1603,7 +1636,7 @@ export class TransactionQueue extends EventEmitter {
1603
1636
  // authoritative `warn` with the same typed cause) — passes
1604
1637
  // through here. Logging it at `warn` made one rejected write
1605
1638
  // surface three identical dumps; keep it at `debug`.
1606
- getContext().logger.debug('[TransactionQueue] Batch commit rejected', {
1639
+ getContext().logger.debug('[MutationQueue] Batch commit rejected', {
1607
1640
  batchSize: batchOps.length,
1608
1641
  models: batchOps.map(({ op }) => `${op.type}:${op.model}`),
1609
1642
  errorType: abloErr?.type ?? error?.name,
@@ -1621,7 +1654,7 @@ export class TransactionQueue extends EventEmitter {
1621
1654
  if (dispatchStarted) {
1622
1655
  await this.removeDurableCommit(commitIdempotencyKey);
1623
1656
  }
1624
- getContext().logger.info('[TransactionQueue] Graceful handling: entity already deleted', {
1657
+ getContext().logger.info('[MutationQueue] Graceful handling: entity already deleted', {
1625
1658
  batchSize: batchOps.length,
1626
1659
  });
1627
1660
  for (const { tx, op } of batchOps) {
@@ -1629,7 +1662,7 @@ export class TransactionQueue extends EventEmitter {
1629
1662
  // Row already gone: the intended state holds, mark completed.
1630
1663
  this.store.updateStatus(tx.id, 'completed');
1631
1664
  this.emit('transaction:completed', tx);
1632
- getContext().logger.debug('[TransactionQueue] Orphaned transaction treated as success', {
1665
+ getContext().logger.debug('[MutationQueue] Orphaned transaction treated as success', {
1633
1666
  txId: tx.id.slice(0, 12),
1634
1667
  model: tx.modelName,
1635
1668
  type: op.type,
@@ -1834,13 +1867,11 @@ export class TransactionQueue extends EventEmitter {
1834
1867
  await existing.sealPromise;
1835
1868
  const existingIntent = stableStringify({
1836
1869
  operations: existing.operations,
1837
- causedByTaskId: existing.causedByTaskId ?? null,
1838
1870
  reads: existing.reads ?? null,
1839
1871
  track: existing.track ?? null,
1840
1872
  });
1841
1873
  const incomingIntent = stableStringify({
1842
1874
  operations,
1843
- causedByTaskId: options.causedByTaskId ?? null,
1844
1875
  reads: options.reads ?? null,
1845
1876
  track: options.track ?? null,
1846
1877
  });
@@ -1871,7 +1902,6 @@ export class TransactionQueue extends EventEmitter {
1871
1902
  id: clientTxId,
1872
1903
  kind: 'commit',
1873
1904
  operations: [...operations],
1874
- causedByTaskId: options.causedByTaskId ?? null,
1875
1905
  ...(options.reads ? { reads: options.reads } : {}),
1876
1906
  ...(options.track ? { track: options.track } : {}),
1877
1907
  status: 'pending',
@@ -1886,7 +1916,6 @@ export class TransactionQueue extends EventEmitter {
1886
1916
  origin: 'atomic_commit',
1887
1917
  operations: tx.operations,
1888
1918
  commitOptions: {
1889
- causedByTaskId: tx.causedByTaskId ?? null,
1890
1919
  ...(tx.reads ? { reads: tx.reads } : {}),
1891
1920
  ...(tx.track ? { track: tx.track } : {}),
1892
1921
  },
@@ -1950,7 +1979,6 @@ export class TransactionQueue extends EventEmitter {
1950
1979
  operations: tx.operations,
1951
1980
  sourceMutationIds: tx.sourceMutationIds,
1952
1981
  commitOptions: {
1953
- causedByTaskId: tx.causedByTaskId ?? null,
1954
1982
  ...(tx.reads ? { reads: tx.reads } : {}),
1955
1983
  ...(tx.track ? { track: tx.track } : {}),
1956
1984
  },
@@ -1961,9 +1989,8 @@ export class TransactionQueue extends EventEmitter {
1961
1989
  tx.durableEnvelope = durableEnvelope;
1962
1990
  this.assertEnvelopeInsideReplayWindow(durableEnvelope);
1963
1991
  dispatchStarted = true;
1964
- const result = this.parseMutationCommitResult(await this.mutationExecutor.commit(durableEnvelope.operations, {
1992
+ const result = this.parseMutationCommitResult(await this.dispatchCommitBounded(durableEnvelope.operations, {
1965
1993
  idempotencyKey: tx.id,
1966
- causedByTaskId: durableEnvelope.commitOptions.causedByTaskId ?? undefined,
1967
1994
  ...(durableEnvelope.commitOptions.reads
1968
1995
  ? { reads: durableEnvelope.commitOptions.reads }
1969
1996
  : {}),
@@ -1991,7 +2018,7 @@ export class TransactionQueue extends EventEmitter {
1991
2018
  }
1992
2019
  else {
1993
2020
  this.scheduleReplicationLagTimeout(tx.id, tx.id, result.correlationId);
1994
- getContext().logger.debug('[TransactionQueue] commit lane awaiting source echo', {
2021
+ getContext().logger.debug('[MutationQueue] commit lane awaiting source echo', {
1995
2022
  txId: tx.id.slice(0, 12),
1996
2023
  });
1997
2024
  }
@@ -2012,14 +2039,29 @@ export class TransactionQueue extends EventEmitter {
2012
2039
  if (dispatchStarted && this.isDefinitiveRejection(error)) {
2013
2040
  await this.removeDurableCommit(tx.id);
2014
2041
  }
2015
- if (!this.isPermanentError(error)) {
2042
+ // A transport that is DOWN is not a failing write: the envelope is
2043
+ // meant to wait for reconnect, which is what makes a commit survive a
2044
+ // dropped connection. A transient error the SERVER keeps returning is
2045
+ // a different thing, and this lane has no attempt bound of its own —
2046
+ // so a 5xx carrying no wire code reads as transient on every kick,
2047
+ // sits at the head of the lane, and `waitForCommitReceipt` never
2048
+ // settles. The caller sees a write that neither lands nor fails.
2049
+ //
2050
+ // Counting only the non-connection failures keeps offline waiting
2051
+ // unbounded while giving a repeating server rejection an end.
2052
+ if (!(error instanceof AbloConnectionError)) {
2053
+ tx.transientAttempts = (tx.transientAttempts ?? 0) + 1;
2054
+ }
2055
+ const exhausted = (tx.transientAttempts ?? 0) > this.config.maxRetries;
2056
+ if (!this.isPermanentError(error) && !exhausted) {
2016
2057
  // Transient: leave it at the head and retry on the next kick
2017
2058
  // (reconnect or the next enqueueCommit) rather than tight-looping
2018
2059
  // while the connection is down.
2019
2060
  tx.status = 'pending';
2020
- getContext().logger.debug('[TransactionQueue] commit lane transient', {
2061
+ getContext().logger.debug('[MutationQueue] commit lane transient', {
2021
2062
  txId: tx.id.slice(0, 12),
2022
2063
  attempts: tx.attempts,
2064
+ transientAttempts: tx.transientAttempts ?? 0,
2023
2065
  message: error.message,
2024
2066
  });
2025
2067
  break;
@@ -2030,7 +2072,7 @@ export class TransactionQueue extends EventEmitter {
2030
2072
  // Internal bookkeeping; the consumer-facing rejection is emitted on
2031
2073
  // 'transaction:failed' and surfaced by the permanent-error headline,
2032
2074
  // so this line stays at debug.
2033
- getContext().logger.debug('[TransactionQueue] commit lane permanent error', {
2075
+ getContext().logger.debug('[MutationQueue] commit lane permanent error', {
2034
2076
  txId: tx.id.slice(0, 12),
2035
2077
  attempts: tx.attempts,
2036
2078
  message: error.message,
@@ -2256,7 +2298,17 @@ export class TransactionQueue extends EventEmitter {
2256
2298
  : '';
2257
2299
  const reason = abloErr?.message ? ` — ${abloErr.message}` : '';
2258
2300
  const code = abloErr?.code ? ` (code: ${abloErr.code})` : '';
2259
- const headline = `Your ${transaction.type} to "${transaction.modelName}" was not saved${reason}${code}.${revertNote}`;
2301
+ // An optimistic write resolves before the server answers, so a later
2302
+ // rejection has no caller left to return to and this log is the only
2303
+ // place it appears. That reads to an application developer as their own
2304
+ // save silently failing — the write showed, then vanished — and sends
2305
+ // them into their editor instead of here. Name the subscription that
2306
+ // hands them the same typed error, so the application can say what
2307
+ // happened rather than only the console.
2308
+ const channelNote = this.config.enableOptimistic
2309
+ ? ' To surface this in your app, subscribe with `ablo.onMutationFailure(…)`.'
2310
+ : '';
2311
+ const headline = `Your ${transaction.type} to "${transaction.modelName}" was not saved${reason}${code}.${revertNote}${channelNote}`;
2260
2312
  if (isRepeat) {
2261
2313
  // Same write rejected for the same reason on each reconnect replay —
2262
2314
  // log the forensics once, stay quiet after.
@@ -2388,7 +2440,7 @@ export class TransactionQueue extends EventEmitter {
2388
2440
  }
2389
2441
  }
2390
2442
  catch (error) {
2391
- getContext().observability.captureTransactionFailure({
2443
+ getContext().observability.captureMutationFailure({
2392
2444
  context: 'load-persisted-transactions',
2393
2445
  error: error instanceof Error ? error : String(error),
2394
2446
  });
@@ -2419,7 +2471,7 @@ export class TransactionQueue extends EventEmitter {
2419
2471
  }
2420
2472
  else {
2421
2473
  getContext().logger.warn('A saved local write is unreadable and was held for review.');
2422
- getContext().observability.captureTransactionFailure({
2474
+ getContext().observability.captureMutationFailure({
2423
2475
  context: 'restore-commit-envelope',
2424
2476
  error: parsed.error,
2425
2477
  });
@@ -2435,9 +2487,9 @@ export class TransactionQueue extends EventEmitter {
2435
2487
  }
2436
2488
  if (envelope.acceptedAt === undefined &&
2437
2489
  Date.now() - envelope.sealedAt >=
2438
- TransactionQueue.DURABLE_REPLAY_WINDOW_MS) {
2490
+ MutationQueue.DURABLE_REPLAY_WINDOW_MS) {
2439
2491
  getContext().logger.warn('A saved local write is too old to retry safely and was held for review.');
2440
- getContext().observability.captureTransactionFailure({
2492
+ getContext().observability.captureMutationFailure({
2441
2493
  context: 'quarantine-expired-commit-envelope',
2442
2494
  error: `Envelope ${envelope.idempotencyKey} is too old to replay safely`,
2443
2495
  });
@@ -2457,7 +2509,6 @@ export class TransactionQueue extends EventEmitter {
2457
2509
  id: envelope.idempotencyKey,
2458
2510
  kind: 'commit',
2459
2511
  operations: envelope.operations.map((operation) => ({ ...operation })),
2460
- causedByTaskId: envelope.commitOptions.causedByTaskId ?? null,
2461
2512
  ...(envelope.commitOptions.reads
2462
2513
  ? { reads: [...envelope.commitOptions.reads] }
2463
2514
  : {}),
@@ -2482,10 +2533,10 @@ export class TransactionQueue extends EventEmitter {
2482
2533
  void this.processCommitLane();
2483
2534
  }
2484
2535
  catch (error) {
2485
- getContext().logger.debug('[TransactionQueue] Failed to restore durable writes', {
2536
+ getContext().logger.debug('[MutationQueue] Failed to restore durable writes', {
2486
2537
  error: error instanceof Error ? error.message : String(error),
2487
2538
  });
2488
- getContext().observability.captureTransactionFailure({
2539
+ getContext().observability.captureMutationFailure({
2489
2540
  context: 'restore-commit-envelopes',
2490
2541
  error: error instanceof Error ? error : String(error),
2491
2542
  });
@@ -2507,10 +2558,10 @@ export class TransactionQueue extends EventEmitter {
2507
2558
  const rowId = typeof data === 'object' && data !== null && typeof data.id === 'string'
2508
2559
  ? data.id
2509
2560
  : undefined;
2510
- getContext().logger.debug('[TransactionQueue] Dropping malformed persisted transaction', {
2561
+ getContext().logger.debug('[MutationQueue] Dropping malformed persisted transaction', {
2511
2562
  rowId,
2512
2563
  });
2513
- getContext().observability.captureTransactionFailure({
2564
+ getContext().observability.captureMutationFailure({
2514
2565
  context: 'deserialize-persisted-transaction',
2515
2566
  error: `Persisted transaction failed schema validation${rowId ? ` (id: ${rowId})` : ''}`,
2516
2567
  });
@@ -2538,7 +2589,7 @@ export class TransactionQueue extends EventEmitter {
2538
2589
  // listener) must surface, not vanish — the status flip above is
2539
2590
  // already committed either way.
2540
2591
  void this.rollbackOptimistic(transaction, 'model_cancelled').catch((error) => {
2541
- getContext().observability.captureTransactionFailure({
2592
+ getContext().observability.captureMutationFailure({
2542
2593
  context: 'rollback-model-cancelled',
2543
2594
  error: error instanceof Error ? error : String(error),
2544
2595
  });
@@ -2553,8 +2604,8 @@ export class TransactionQueue extends EventEmitter {
2553
2604
  * used to cascade a parent deletion. The caller supplies the foreign-key
2554
2605
  * relationship; this method performs the cancellation.
2555
2606
  *
2556
- * @param childModelName - The child model type (for example 'SlideLayer').
2557
- * @param foreignKey - The foreign-key property name (for example 'slideId').
2607
+ * @param childModelName - The child model type (for example 'Block').
2608
+ * @param foreignKey - The foreign-key property name (for example 'sectionId').
2558
2609
  * @param parentId - The deleted parent's id.
2559
2610
  * @returns The number of transactions cancelled.
2560
2611
  */
@@ -2572,13 +2623,13 @@ export class TransactionQueue extends EventEmitter {
2572
2623
  if (fkValue === parentId) {
2573
2624
  this.store.updateStatus(transaction.id, 'rolled_back');
2574
2625
  void this.rollbackOptimistic(transaction, 'cascade_parent_deleted').catch((error) => {
2575
- getContext().observability.captureTransactionFailure({
2626
+ getContext().observability.captureMutationFailure({
2576
2627
  context: 'rollback-cascade-parent-deleted',
2577
2628
  error: error instanceof Error ? error : String(error),
2578
2629
  });
2579
2630
  });
2580
2631
  cancelled++;
2581
- getContext().logger.debug('[TransactionQueue] Cascade cancelled orphaned transaction', {
2632
+ getContext().logger.debug('[MutationQueue] Cascade cancelled orphaned transaction', {
2582
2633
  txId: transaction.id.slice(0, 12),
2583
2634
  model: childModelName,
2584
2635
  foreignKey,
@@ -2624,7 +2675,7 @@ export class TransactionQueue extends EventEmitter {
2624
2675
  // inverse rather than inventing one. With no `updateInput` (a full extract)
2625
2676
  // it falls back to every tracked field. `Model.capturePreviousValues` is the
2626
2677
  // single before-image source, shared with
2627
- // `RecordingTransaction.snapshotFields`.
2678
+ // `RecordingMutation.snapshotFields`.
2628
2679
  const keys = updateInput
2629
2680
  ? Object.keys(updateInput)
2630
2681
  : [...(model.modifiedProperties instanceof Map ? model.modifiedProperties.keys() : [])];
@@ -3,18 +3,18 @@
3
3
  * The status index keeps the queue's hot paths — such as `getByStatus('pending')`
4
4
  * on every batch and coalesce decision — proportional to the number of
5
5
  * transactions in that status rather than the total across all statuses.
6
- * {@link TransactionQueue} owns an instance and routes every status change
6
+ * {@link MutationQueue} owns an instance and routes every status change
7
7
  * through {@link updateStatus}, which keeps the two indexes consistent.
8
8
  */
9
- import type { Transaction } from './commitPayload.js';
10
- export declare class TransactionStore {
9
+ import type { QueuedMutation } from './commitPayload.js';
10
+ export declare class MutationStore {
11
11
  private transactions;
12
12
  private byStatus;
13
- add(transaction: Transaction): void;
14
- get(id: string): Transaction | undefined;
15
- updateStatus(id: string, newStatus: Transaction['status']): void;
16
- getByStatus(status: Transaction['status']): Transaction[];
13
+ add(transaction: QueuedMutation): void;
14
+ get(id: string): QueuedMutation | undefined;
15
+ updateStatus(id: string, newStatus: QueuedMutation['status']): void;
16
+ getByStatus(status: QueuedMutation['status']): QueuedMutation[];
17
17
  remove(id: string): void;
18
18
  clear(): void;
19
- getAll(): Transaction[];
19
+ getAll(): QueuedMutation[];
20
20
  }
@@ -3,10 +3,10 @@
3
3
  * The status index keeps the queue's hot paths — such as `getByStatus('pending')`
4
4
  * on every batch and coalesce decision — proportional to the number of
5
5
  * transactions in that status rather than the total across all statuses.
6
- * {@link TransactionQueue} owns an instance and routes every status change
6
+ * {@link MutationQueue} owns an instance and routes every status change
7
7
  * through {@link updateStatus}, which keeps the two indexes consistent.
8
8
  */
9
- export class TransactionStore {
9
+ export class MutationStore {
10
10
  transactions = new Map();
11
11
  byStatus = new Map();
12
12
  add(transaction) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The queue's rules for coalescing operations that touch the same row, so their
3
- * causal order is preserved. {@link TransactionQueue} calls into these through a
3
+ * causal order is preserved. {@link MutationQueue} calls into these through a
4
4
  * small store-shaped interface:
5
5
  *
6
6
  * - Create-then-delete cancellation ({@link takeUnsentCreateForModel}):
@@ -16,11 +16,11 @@
16
16
  * The queue keeps the `enqueue` and `delete` methods that orchestrate optimistic
17
17
  * state and events; these functions hold only the coalescing rules.
18
18
  */
19
- import type { MutationInput, Transaction } from './commitPayload.js';
20
- /** The subset of {@link TransactionStore} that the coalescing rules read. */
21
- export interface TransactionStoreLike {
22
- get(id: string): Transaction | undefined;
23
- getByStatus(status: Transaction['status']): Transaction[];
19
+ import type { MutationInput, QueuedMutation } from './commitPayload.js';
20
+ /** The subset of {@link MutationStore} that the coalescing rules read. */
21
+ export interface MutationStoreLike {
22
+ get(id: string): QueuedMutation | undefined;
23
+ getByStatus(status: QueuedMutation['status']): QueuedMutation[];
24
24
  }
25
25
  export declare const entityKey: (modelName: string, modelId: string) => string;
26
26
  /**
@@ -30,25 +30,25 @@ export declare const entityKey: (modelName: string, modelId: string) => string;
30
30
  * held it, so the caller can cancel it rather than send a create followed by a
31
31
  * delete.
32
32
  */
33
- export declare function takeUnsentCreateForModel(staged: Transaction[], queued: Transaction[], store: Pick<TransactionStoreLike, 'getByStatus'>, modelName: string, modelId: string): Transaction | undefined;
33
+ export declare function takeUnsentCreateForModel(staged: QueuedMutation[], queued: QueuedMutation[], store: Pick<MutationStoreLike, 'getByStatus'>, modelName: string, modelId: string): QueuedMutation | undefined;
34
34
  /**
35
35
  * Returns the most recent in-flight create for the given model and id that a
36
36
  * delete must wait behind, or undefined if there is none. A pending create that
37
37
  * has never been attempted is not a barrier, because it can be cancelled
38
38
  * instead; once a create has been sent, even a retry-pending one is a barrier.
39
39
  */
40
- export declare function findCreateBarrierForDelete(store: Pick<TransactionStoreLike, 'getByStatus'>, modelName: string, modelId: string): Transaction | undefined;
40
+ export declare function findCreateBarrierForDelete(store: Pick<MutationStoreLike, 'getByStatus'>, modelName: string, modelId: string): QueuedMutation | undefined;
41
41
  /**
42
42
  * Parks a delete until the create for the same row settles, keyed by the
43
43
  * create's model and id. {@link releaseDeferredDeletesForCreate} re-enqueues
44
44
  * the parked deletes once that create completes.
45
45
  */
46
- export declare function deferDeleteUntilCreateSettles(deferredDeletesByCreate: Map<string, Transaction[]>, createTransaction: Transaction, deleteTransaction: Transaction): void;
46
+ export declare function deferDeleteUntilCreateSettles(deferredDeletesByCreate: Map<string, QueuedMutation[]>, createTransaction: QueuedMutation, deleteTransaction: QueuedMutation): void;
47
47
  /**
48
48
  * Re-enqueues the deletes parked behind a create once that create settles,
49
49
  * skipping any whose status is no longer pending.
50
50
  */
51
- export declare function releaseDeferredDeletesForCreate(deferredDeletesByCreate: Map<string, Transaction[]>, store: Pick<TransactionStoreLike, 'get'>, enqueue: (transaction: Transaction) => void, createTransaction: Transaction): void;
51
+ export declare function releaseDeferredDeletesForCreate(deferredDeletesByCreate: Map<string, QueuedMutation[]>, store: Pick<MutationStoreLike, 'get'>, enqueue: (transaction: QueuedMutation) => void, createTransaction: QueuedMutation): void;
52
52
  /**
53
53
  * Merges two update payloads for the same row into one. Later values win, with
54
54
  * one exception: a `metadata` field is deep-merged as an object — parsing it
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The queue's rules for coalescing operations that touch the same row, so their
3
- * causal order is preserved. {@link TransactionQueue} calls into these through a
3
+ * causal order is preserved. {@link MutationQueue} calls into these through a
4
4
  * small store-shaped interface:
5
5
  *
6
6
  * - Create-then-delete cancellation ({@link takeUnsentCreateForModel}):
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Commit latency — how long a user's edit actually takes to land.
3
+ *
4
+ * The engine has never measured this. HUDs and dashboards reach for
5
+ * `window.fetch` timings, which the sync engine's WebSocket never touches, so
6
+ * the latency a user sees reported while editing has had nothing to do with
7
+ * the writes they are making. This module closes that gap without adding a
8
+ * single timestamp to the hot path: `MutationQueue` already emits the commit
9
+ * lifecycle, and the events already carry the correlation key.
10
+ *
11
+ * Three events, two intervals:
12
+ *
13
+ * commit:staging ──sealMs──▶ commit:created ──ackMs──▶ transaction:completed
14
+ *
15
+ * - `sealMs` is **local** — writing the durable envelope before the commit is
16
+ * allowed onto the wire. Slow here means storage (IndexedDB), not network.
17
+ * - `ackMs` is **remote** — dispatch, round-trip, and server work. For a
18
+ * commit routed at a connected source this also spans the wait for the
19
+ * correlated echo that promotes `queued` to `confirmed`, so it answers
20
+ * "when did my edit become real" rather than raw socket round-trip. Read a
21
+ * large `ackMs` against a small `sealMs` as a network or replication cost.
22
+ *
23
+ * Correlation is by `clientTxId`: `MutationQueue` uses it as the transaction
24
+ * id verbatim, so the staging event and the completion event share one key.
25
+ */
26
+ /**
27
+ * The slice of an event emitter this module needs. Declared structurally so a
28
+ * plain object can stand in under test — `MutationQueue` satisfies it by
29
+ * extending `EventEmitter`.
30
+ */
31
+ export interface CommitEventSource {
32
+ on(event: string, listener: (payload: unknown) => void): unknown;
33
+ off(event: string, listener: (payload: unknown) => void): unknown;
34
+ }
35
+ /** One completed commit, broken into its local and remote halves. */
36
+ export interface CommitLatencySample {
37
+ /** The commit's `clientTxId`, identical to its transaction id. */
38
+ clientTxId: string;
39
+ /** Milliseconds sealing the durable envelope locally. */
40
+ sealMs: number;
41
+ /** Milliseconds from sealed envelope to acknowledgement. */
42
+ ackMs: number;
43
+ /** Milliseconds from staging to acknowledgement — `sealMs + ackMs`. */
44
+ totalMs: number;
45
+ }
46
+ /**
47
+ * Pair commit lifecycle events into latency samples. Returns an unsubscribe
48
+ * function that also drops any still-pending timings.
49
+ *
50
+ * `onSample` fires once per commit that completes, in completion order.
51
+ */
52
+ export declare function observeCommitLatency(source: CommitEventSource, onSample: (sample: CommitLatencySample) => void): () => void;