@abloatai/ablo 0.26.0 → 0.28.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 (418) hide show
  1. package/CHANGELOG.md +42 -2
  2. package/README.md +102 -86
  3. package/dist/BaseSyncedStore.d.ts +85 -88
  4. package/dist/BaseSyncedStore.js +134 -151
  5. package/dist/Database.d.ts +68 -69
  6. package/dist/Database.js +316 -135
  7. package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
  8. package/dist/{ObjectPool.js → InstanceCache.js} +85 -83
  9. package/dist/LazyReferenceCollection.d.ts +11 -15
  10. package/dist/LazyReferenceCollection.js +12 -16
  11. package/dist/Model.d.ts +54 -52
  12. package/dist/Model.js +78 -62
  13. package/dist/ModelRegistry.d.ts +21 -19
  14. package/dist/ModelRegistry.js +23 -27
  15. package/dist/NetworkMonitor.d.ts +5 -6
  16. package/dist/NetworkMonitor.js +5 -6
  17. package/dist/SyncClient.d.ts +122 -118
  18. package/dist/SyncClient.js +541 -245
  19. package/dist/adapters/alwaysOnline.d.ts +6 -8
  20. package/dist/adapters/alwaysOnline.js +6 -8
  21. package/dist/adapters/inMemoryStorage.d.ts +10 -9
  22. package/dist/adapters/inMemoryStorage.js +21 -9
  23. package/dist/agent/Agent.d.ts +27 -32
  24. package/dist/agent/Agent.js +18 -19
  25. package/dist/agent/index.d.ts +4 -4
  26. package/dist/agent/index.js +5 -5
  27. package/dist/agent/session.d.ts +47 -44
  28. package/dist/agent/session.js +37 -48
  29. package/dist/agent/types.d.ts +26 -31
  30. package/dist/agent/types.js +6 -7
  31. package/dist/ai-sdk/coordinatedTool.d.ts +108 -0
  32. package/dist/ai-sdk/{coordinated-tool.js → coordinatedTool.js} +44 -38
  33. package/dist/ai-sdk/coordinationContext.d.ts +46 -0
  34. package/dist/ai-sdk/{coordination-context.js → coordinationContext.js} +26 -33
  35. package/dist/ai-sdk/index.d.ts +25 -22
  36. package/dist/ai-sdk/index.js +25 -22
  37. package/dist/ai-sdk/wrap.d.ts +6 -7
  38. package/dist/ai-sdk/wrap.js +1 -1
  39. package/dist/auth/credentialPolicy.d.ts +69 -74
  40. package/dist/auth/credentialPolicy.js +51 -56
  41. package/dist/auth/credentialSource.d.ts +6 -5
  42. package/dist/auth/credentialSource.js +9 -10
  43. package/dist/auth/index.d.ts +59 -58
  44. package/dist/auth/index.js +31 -37
  45. package/dist/auth/schemas.d.ts +5 -4
  46. package/dist/auth/schemas.js +5 -4
  47. package/dist/batching/index.d.ts +19 -21
  48. package/dist/batching/index.js +14 -17
  49. package/dist/cli.cjs +173 -121
  50. package/dist/client/Ablo.d.ts +97 -74
  51. package/dist/client/Ablo.js +129 -163
  52. package/dist/client/ApiClient.d.ts +30 -19
  53. package/dist/client/ApiClient.js +442 -81
  54. package/dist/client/auth.d.ts +47 -47
  55. package/dist/client/auth.js +108 -117
  56. package/dist/client/claimHeartbeatLoop.d.ts +50 -0
  57. package/dist/client/claimHeartbeatLoop.js +88 -0
  58. package/dist/client/consoleLogger.d.ts +5 -6
  59. package/dist/client/consoleLogger.js +5 -6
  60. package/dist/client/createInternalComponents.d.ts +16 -17
  61. package/dist/client/createInternalComponents.js +26 -31
  62. package/dist/client/createModelProxy.d.ts +130 -120
  63. package/dist/client/createModelProxy.js +152 -122
  64. package/dist/client/credentialEndpoint.d.ts +40 -42
  65. package/dist/client/credentialEndpoint.js +35 -36
  66. package/dist/client/functionalUpdate.d.ts +29 -27
  67. package/dist/client/functionalUpdate.js +21 -21
  68. package/dist/client/hostedEndpoints.d.ts +9 -12
  69. package/dist/client/hostedEndpoints.js +9 -12
  70. package/dist/client/httpClient.d.ts +59 -53
  71. package/dist/client/httpClient.js +29 -31
  72. package/dist/client/identity.d.ts +15 -20
  73. package/dist/client/identity.js +47 -58
  74. package/dist/client/modelRegistration.d.ts +5 -9
  75. package/dist/client/modelRegistration.js +78 -87
  76. package/dist/client/options.d.ts +157 -157
  77. package/dist/client/options.js +3 -7
  78. package/dist/client/registerDataSource.d.ts +9 -9
  79. package/dist/client/registerDataSource.js +15 -16
  80. package/dist/client/resourceTypes.d.ts +64 -75
  81. package/dist/client/resourceTypes.js +4 -10
  82. package/dist/client/schemaConfig.d.ts +31 -43
  83. package/dist/client/schemaConfig.js +38 -50
  84. package/dist/client/sessionMint.d.ts +16 -12
  85. package/dist/client/sessionMint.js +26 -31
  86. package/dist/client/validateAbloOptions.d.ts +12 -14
  87. package/dist/client/validateAbloOptions.js +8 -9
  88. package/dist/client/writeOptionsSchema.d.ts +18 -16
  89. package/dist/client/writeOptionsSchema.js +23 -20
  90. package/dist/client/wsMutationExecutor.d.ts +16 -20
  91. package/dist/client/wsMutationExecutor.js +18 -23
  92. package/dist/commit/contract.d.ts +493 -0
  93. package/dist/commit/contract.js +187 -0
  94. package/dist/commit/index.d.ts +6 -0
  95. package/dist/commit/index.js +5 -0
  96. package/dist/context.d.ts +6 -4
  97. package/dist/context.js +6 -4
  98. package/dist/coordination/index.d.ts +10 -8
  99. package/dist/coordination/index.js +14 -12
  100. package/dist/coordination/schema.d.ts +176 -128
  101. package/dist/coordination/schema.js +197 -133
  102. package/dist/coordination/trace.d.ts +9 -10
  103. package/dist/coordination/trace.js +13 -14
  104. package/dist/core/DatabaseManager.d.ts +5 -7
  105. package/dist/core/DatabaseManager.js +15 -19
  106. package/dist/core/QueryProcessor.d.ts +7 -9
  107. package/dist/core/QueryProcessor.js +22 -28
  108. package/dist/core/QueryView.d.ts +8 -8
  109. package/dist/core/QueryView.js +2 -2
  110. package/dist/core/StoreManager.d.ts +14 -14
  111. package/dist/core/StoreManager.js +33 -24
  112. package/dist/core/ViewRegistry.d.ts +5 -5
  113. package/dist/core/ViewRegistry.js +4 -4
  114. package/dist/core/index.d.ts +17 -12
  115. package/dist/core/index.js +32 -26
  116. package/dist/core/openIDBWithTimeout.d.ts +38 -36
  117. package/dist/core/openIDBWithTimeout.js +42 -43
  118. package/dist/core/queryUtils.d.ts +45 -0
  119. package/dist/core/queryUtils.js +69 -0
  120. package/dist/core/storeContract.d.ts +63 -61
  121. package/dist/core/storeContract.js +8 -12
  122. package/dist/environment.d.ts +28 -0
  123. package/dist/environment.js +21 -0
  124. package/dist/errorCodes.d.ts +107 -99
  125. package/dist/errorCodes.js +137 -134
  126. package/dist/errors.d.ts +160 -166
  127. package/dist/errors.js +155 -158
  128. package/dist/index.d.ts +36 -27
  129. package/dist/index.js +91 -86
  130. package/dist/interfaces/index.d.ts +102 -113
  131. package/dist/interfaces/index.js +5 -4
  132. package/dist/keys/index.d.ts +27 -29
  133. package/dist/keys/index.js +41 -40
  134. package/dist/mutators/RecordingTransaction.d.ts +16 -16
  135. package/dist/mutators/RecordingTransaction.js +31 -37
  136. package/dist/mutators/Transaction.d.ts +18 -26
  137. package/dist/mutators/Transaction.js +14 -20
  138. package/dist/mutators/UndoManager.d.ts +124 -131
  139. package/dist/mutators/UndoManager.js +177 -156
  140. package/dist/mutators/defineMutators.d.ts +23 -34
  141. package/dist/mutators/defineMutators.js +14 -20
  142. package/dist/mutators/inverseOp.d.ts +12 -15
  143. package/dist/mutators/inverseOp.js +12 -15
  144. package/dist/mutators/mutateActions.d.ts +10 -9
  145. package/dist/mutators/mutateActions.js +1 -1
  146. package/dist/mutators/readerActions.d.ts +9 -8
  147. package/dist/mutators/readerActions.js +2 -2
  148. package/dist/mutators/undoApply.d.ts +31 -27
  149. package/dist/mutators/undoApply.js +26 -24
  150. package/dist/policy/index.d.ts +5 -3
  151. package/dist/policy/index.js +5 -3
  152. package/dist/policy/types.d.ts +104 -100
  153. package/dist/policy/types.js +67 -66
  154. package/dist/query/client.d.ts +28 -23
  155. package/dist/query/client.js +45 -43
  156. package/dist/query/types.d.ts +37 -60
  157. package/dist/query/types.js +13 -33
  158. package/dist/react/AbloProvider.d.ts +1 -1
  159. package/dist/react/AbloProvider.js +2 -2
  160. package/dist/react/context.d.ts +25 -28
  161. package/dist/react/context.js +9 -10
  162. package/dist/react/index.d.ts +41 -42
  163. package/dist/react/index.js +37 -38
  164. package/dist/react/internalContext.d.ts +17 -19
  165. package/dist/react/useAblo.d.ts +28 -25
  166. package/dist/react/useAblo.js +41 -17
  167. package/dist/react/useCurrentUserId.d.ts +8 -7
  168. package/dist/react/useCurrentUserId.js +8 -7
  169. package/dist/react/useErrorListener.d.ts +7 -7
  170. package/dist/react/useErrorListener.js +10 -11
  171. package/dist/react/useMutationFailureListener.d.ts +8 -8
  172. package/dist/react/useMutationFailureListener.js +8 -8
  173. package/dist/react/useMutators.d.ts +11 -11
  174. package/dist/react/useMutators.js +3 -3
  175. package/dist/react/useReactive.js +2 -2
  176. package/dist/react/useSyncStatus.d.ts +4 -6
  177. package/dist/react/useUndoScope.d.ts +7 -9
  178. package/dist/react/useUndoScope.js +1 -1
  179. package/dist/schema/coordination.d.ts +21 -25
  180. package/dist/schema/coordination.js +21 -25
  181. package/dist/schema/ddl.d.ts +43 -39
  182. package/dist/schema/ddl.js +75 -68
  183. package/dist/schema/ddlLock.d.ts +20 -24
  184. package/dist/schema/ddlLock.js +18 -23
  185. package/dist/schema/diff.d.ts +99 -61
  186. package/dist/schema/diff.js +43 -34
  187. package/dist/schema/field.d.ts +37 -42
  188. package/dist/schema/field.js +35 -48
  189. package/dist/schema/generate.d.ts +12 -12
  190. package/dist/schema/generate.js +12 -12
  191. package/dist/schema/index.d.ts +3 -3
  192. package/dist/schema/index.js +21 -23
  193. package/dist/schema/model.d.ts +118 -143
  194. package/dist/schema/model.js +22 -33
  195. package/dist/schema/openapi.d.ts +10 -9
  196. package/dist/schema/openapi.js +5 -3
  197. package/dist/schema/queries.d.ts +29 -31
  198. package/dist/schema/queries.js +23 -25
  199. package/dist/schema/relation.d.ts +89 -99
  200. package/dist/schema/relation.js +13 -13
  201. package/dist/schema/residency.d.ts +16 -13
  202. package/dist/schema/residency.js +16 -13
  203. package/dist/schema/roles.d.ts +36 -43
  204. package/dist/schema/roles.js +31 -37
  205. package/dist/schema/schema.d.ts +64 -43
  206. package/dist/schema/schema.js +31 -32
  207. package/dist/schema/select.d.ts +13 -13
  208. package/dist/schema/select.js +13 -13
  209. package/dist/schema/serialize.d.ts +28 -31
  210. package/dist/schema/serialize.js +27 -31
  211. package/dist/schema/sugar.d.ts +17 -32
  212. package/dist/schema/sugar.js +14 -29
  213. package/dist/schema/{sync-delta-row.d.ts → syncDeltaRow.d.ts} +26 -49
  214. package/dist/schema/syncDeltaRow.js +89 -0
  215. package/dist/schema/tenancy.d.ts +44 -46
  216. package/dist/schema/tenancy.js +46 -48
  217. package/dist/server/adapter.d.ts +58 -58
  218. package/dist/server/adapter.js +13 -14
  219. package/dist/server/commit.d.ts +60 -64
  220. package/dist/server/index.d.ts +9 -10
  221. package/dist/server/index.js +1 -1
  222. package/dist/server/readConfig.d.ts +70 -0
  223. package/dist/server/readConfig.js +8 -0
  224. package/dist/server/storageMode.d.ts +23 -0
  225. package/dist/server/storageMode.js +17 -0
  226. package/dist/source/adapter.d.ts +30 -25
  227. package/dist/source/adapter.js +10 -10
  228. package/dist/source/adapters/drizzle.d.ts +28 -23
  229. package/dist/source/adapters/drizzle.js +30 -25
  230. package/dist/source/adapters/kysely.d.ts +27 -25
  231. package/dist/source/adapters/kysely.js +24 -23
  232. package/dist/source/adapters/memory.d.ts +8 -7
  233. package/dist/source/adapters/memory.js +9 -8
  234. package/dist/source/adapters/prisma.d.ts +13 -12
  235. package/dist/source/adapters/prisma.js +22 -25
  236. package/dist/source/conformance.d.ts +18 -11
  237. package/dist/source/conformance.js +17 -11
  238. package/dist/source/connector.d.ts +31 -32
  239. package/dist/source/connector.js +28 -28
  240. package/dist/source/connectorProtocol.d.ts +160 -0
  241. package/dist/source/connectorProtocol.js +162 -0
  242. package/dist/source/contract.d.ts +26 -27
  243. package/dist/source/contract.js +28 -29
  244. package/dist/source/factory.d.ts +46 -58
  245. package/dist/source/factory.js +22 -27
  246. package/dist/source/index.d.ts +7 -9
  247. package/dist/source/index.js +12 -14
  248. package/dist/source/migrations.d.ts +9 -9
  249. package/dist/source/migrations.js +9 -9
  250. package/dist/source/next.d.ts +9 -10
  251. package/dist/source/next.js +6 -7
  252. package/dist/source/pushQueue.d.ts +69 -47
  253. package/dist/source/pushQueue.js +32 -28
  254. package/dist/source/signing.d.ts +46 -17
  255. package/dist/source/signing.js +28 -11
  256. package/dist/source/types.d.ts +121 -104
  257. package/dist/source/types.js +13 -14
  258. package/dist/stores/ObjectStore.d.ts +24 -12
  259. package/dist/stores/ObjectStore.js +38 -16
  260. package/dist/stores/ObjectStoreContract.d.ts +14 -15
  261. package/dist/stores/SyncActionStore.d.ts +7 -11
  262. package/dist/stores/SyncActionStore.js +13 -17
  263. package/dist/surface.d.ts +28 -21
  264. package/dist/surface.js +29 -20
  265. package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +36 -42
  266. package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +76 -76
  267. package/dist/sync/ConnectionManager.d.ts +39 -50
  268. package/dist/sync/ConnectionManager.js +55 -66
  269. package/dist/sync/NetworkProbe.d.ts +24 -29
  270. package/dist/sync/NetworkProbe.js +63 -69
  271. package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +42 -41
  272. package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +59 -54
  273. package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +43 -57
  274. package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +43 -51
  275. package/dist/sync/SyncWebSocket.d.ts +141 -166
  276. package/dist/sync/SyncWebSocket.js +191 -223
  277. package/dist/sync/awaitClaimGrant.d.ts +18 -18
  278. package/dist/sync/awaitClaimGrant.js +11 -11
  279. package/dist/sync/bootstrapApply.d.ts +34 -24
  280. package/dist/sync/bootstrapApply.js +27 -19
  281. package/dist/sync/commitFrames.d.ts +21 -20
  282. package/dist/sync/commitFrames.js +18 -18
  283. package/dist/sync/createClaimStream.d.ts +23 -22
  284. package/dist/sync/createClaimStream.js +105 -23
  285. package/dist/sync/createPresenceStream.d.ts +19 -18
  286. package/dist/sync/createPresenceStream.js +25 -26
  287. package/dist/sync/createSnapshot.d.ts +12 -14
  288. package/dist/sync/createSnapshot.js +20 -26
  289. package/dist/sync/credentialLifecycle.d.ts +104 -104
  290. package/dist/sync/credentialLifecycle.js +140 -147
  291. package/dist/sync/deltaPipeline.d.ts +36 -34
  292. package/dist/sync/deltaPipeline.js +64 -65
  293. package/dist/sync/groupChange.d.ts +63 -61
  294. package/dist/sync/groupChange.js +74 -78
  295. package/dist/sync/heartbeat.d.ts +34 -33
  296. package/dist/sync/heartbeat.js +31 -31
  297. package/dist/sync/participants.d.ts +19 -19
  298. package/dist/sync/persistedPrefix.d.ts +12 -0
  299. package/dist/sync/persistedPrefix.js +22 -0
  300. package/dist/sync/schemas.d.ts +3 -2
  301. package/dist/sync/schemas.js +14 -10
  302. package/dist/sync/syncCursor.d.ts +17 -21
  303. package/dist/sync/syncCursor.js +17 -21
  304. package/dist/sync/syncPlan.d.ts +28 -36
  305. package/dist/sync/syncPlan.js +18 -19
  306. package/dist/sync/syncPosition.d.ts +54 -49
  307. package/dist/sync/syncPosition.js +57 -52
  308. package/dist/sync/wsFrameHandlers.d.ts +35 -36
  309. package/dist/sync/wsFrameHandlers.js +63 -67
  310. package/dist/testing/fixtures/bootstrap.d.ts +12 -6
  311. package/dist/testing/fixtures/bootstrap.js +12 -6
  312. package/dist/testing/fixtures/deltas.d.ts +30 -33
  313. package/dist/testing/fixtures/deltas.js +30 -33
  314. package/dist/testing/fixtures/models.d.ts +11 -10
  315. package/dist/testing/fixtures/models.js +11 -10
  316. package/dist/testing/helpers/{react-wrapper.d.ts → reactWrapper.d.ts} +13 -10
  317. package/dist/testing/helpers/{react-wrapper.js → reactWrapper.js} +15 -12
  318. package/dist/testing/helpers/{sync-engine-harness.d.ts → syncEngineHarness.d.ts} +17 -15
  319. package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +12 -10
  320. package/dist/testing/helpers/wait.d.ts +13 -8
  321. package/dist/testing/helpers/wait.js +13 -8
  322. package/dist/testing/index.d.ts +5 -3
  323. package/dist/testing/index.js +3 -2
  324. package/dist/testing/mocks/FakeDatabase.d.ts +18 -0
  325. package/dist/testing/mocks/FakeDatabase.js +10 -0
  326. package/dist/testing/mocks/MockMutationExecutor.d.ts +18 -17
  327. package/dist/testing/mocks/MockMutationExecutor.js +15 -14
  328. package/dist/testing/mocks/MockNetworkMonitor.d.ts +8 -8
  329. package/dist/testing/mocks/MockNetworkMonitor.js +8 -8
  330. package/dist/testing/mocks/MockSyncContext.d.ts +20 -17
  331. package/dist/testing/mocks/MockSyncContext.js +15 -13
  332. package/dist/testing/mocks/MockSyncStore.d.ts +10 -10
  333. package/dist/testing/mocks/MockSyncStore.js +11 -11
  334. package/dist/testing/mocks/MockWebSocket.d.ts +28 -23
  335. package/dist/testing/mocks/MockWebSocket.js +22 -21
  336. package/dist/transactions/TransactionQueue.d.ts +244 -181
  337. package/dist/transactions/TransactionQueue.js +929 -423
  338. package/dist/transactions/TransactionStore.d.ts +6 -4
  339. package/dist/transactions/TransactionStore.js +6 -4
  340. package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
  341. package/dist/transactions/UnconfirmedWrites.js +104 -0
  342. package/dist/transactions/coalesceRules.d.ts +41 -17
  343. package/dist/transactions/coalesceRules.js +40 -17
  344. package/dist/transactions/commitEnvelope.d.ts +132 -0
  345. package/dist/transactions/commitEnvelope.js +139 -0
  346. package/dist/transactions/commitOutboxStore.d.ts +32 -0
  347. package/dist/transactions/commitOutboxStore.js +26 -0
  348. package/dist/transactions/commitPayload.d.ts +63 -52
  349. package/dist/transactions/commitPayload.js +54 -57
  350. package/dist/transactions/deltaConfirmation.d.ts +20 -22
  351. package/dist/transactions/deltaConfirmation.js +37 -45
  352. package/dist/transactions/httpCommitEnvelope.d.ts +43 -0
  353. package/dist/transactions/httpCommitEnvelope.js +179 -0
  354. package/dist/transactions/optimisticApply.d.ts +49 -0
  355. package/dist/transactions/optimisticApply.js +65 -0
  356. package/dist/transactions/replayValidation.d.ts +182 -0
  357. package/dist/transactions/replayValidation.js +156 -0
  358. package/dist/types/global.d.ts +46 -41
  359. package/dist/types/global.js +20 -19
  360. package/dist/types/index.d.ts +71 -77
  361. package/dist/types/index.js +22 -22
  362. package/dist/types/modelData.d.ts +6 -8
  363. package/dist/types/modelData.js +5 -7
  364. package/dist/types/participant.d.ts +10 -11
  365. package/dist/types/participant.js +6 -8
  366. package/dist/types/streams.d.ts +208 -195
  367. package/dist/types/streams.js +7 -7
  368. package/dist/utils/asyncIterator.d.ts +25 -32
  369. package/dist/utils/asyncIterator.js +25 -32
  370. package/dist/utils/duration.d.ts +12 -15
  371. package/dist/utils/duration.js +12 -15
  372. package/dist/utils/mobxSetup.d.ts +53 -0
  373. package/dist/utils/{mobx-setup.js → mobxSetup.js} +42 -98
  374. package/dist/webhooks/events.d.ts +21 -16
  375. package/dist/webhooks/events.js +10 -8
  376. package/dist/webhooks/index.d.ts +5 -7
  377. package/dist/webhooks/index.js +5 -7
  378. package/dist/wire/bootstrapReason.d.ts +9 -0
  379. package/dist/wire/bootstrapReason.js +8 -0
  380. package/dist/{schema/sync-delta-wire.d.ts → wire/delta.d.ts} +58 -41
  381. package/dist/wire/delta.js +114 -0
  382. package/dist/wire/errorEnvelope.d.ts +30 -31
  383. package/dist/wire/errorEnvelope.js +34 -40
  384. package/dist/wire/frames.d.ts +315 -86
  385. package/dist/wire/frames.js +47 -33
  386. package/dist/wire/index.d.ts +18 -14
  387. package/dist/wire/index.js +32 -27
  388. package/dist/wire/listEnvelope.d.ts +16 -23
  389. package/dist/wire/listEnvelope.js +7 -6
  390. package/dist/wire/protocol.d.ts +25 -32
  391. package/dist/wire/protocol.js +25 -32
  392. package/dist/wire/protocolVersion.d.ts +44 -40
  393. package/dist/wire/protocolVersion.js +44 -40
  394. package/docs/api.md +10 -10
  395. package/docs/coordination.md +59 -0
  396. package/docs/mcp.md +1 -1
  397. package/package.json +17 -11
  398. package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
  399. package/dist/ai-sdk/coordination-context.d.ts +0 -52
  400. package/dist/core/query-utils.d.ts +0 -34
  401. package/dist/core/query-utils.js +0 -59
  402. package/dist/schema/sync-delta-row.js +0 -103
  403. package/dist/schema/sync-delta-wire.js +0 -102
  404. package/dist/server/read-config.d.ts +0 -67
  405. package/dist/server/read-config.js +0 -8
  406. package/dist/server/storage-mode.d.ts +0 -8
  407. package/dist/server/storage-mode.js +0 -28
  408. package/dist/source/connector-protocol.d.ts +0 -159
  409. package/dist/source/connector-protocol.js +0 -161
  410. package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
  411. package/dist/transactions/OptimisticEchoTracker.js +0 -104
  412. package/dist/transactions/mutation-error-handler.d.ts +0 -5
  413. package/dist/transactions/mutation-error-handler.js +0 -39
  414. package/dist/transactions/optimistic.d.ts +0 -24
  415. package/dist/transactions/optimistic.js +0 -45
  416. package/dist/transactions/persistedReplay.d.ts +0 -93
  417. package/dist/transactions/persistedReplay.js +0 -105
  418. package/dist/utils/mobx-setup.d.ts +0 -42
@@ -1,25 +1,22 @@
1
1
  /**
2
- * Delta-confirmation tracking the queue's "did my write's echo arrive?"
3
- * machinery, lifted out of `TransactionQueue.ts` as a stateful leaf.
4
- *
5
- * Owns the ack watermark (via the shared `SyncPosition`), the per-transaction
6
- * confirmation timeout map, and the retry-with-backoff/reconciliation policy
7
- * for `awaiting_delta` transactions. Talks back to the queue through the
8
- * minimal `DeltaConfirmationContext` interface (never the host class type),
9
- * so the leaf stays cycle-free and testable in isolation.
2
+ * Tracks whether the confirming delta for a write has arrived. It holds the
3
+ * acknowledgement watermark (through the shared {@link SyncPosition}), the
4
+ * per-transaction confirmation timeouts, and the retry-with-backoff and
5
+ * reconciliation policy for transactions in the `awaiting_delta` status. It
6
+ * reaches back to {@link TransactionQueue} only through the small
7
+ * {@link DeltaConfirmationContext} interface, not the queue class itself, so it
8
+ * has no cyclic dependency and can be tested on its own.
10
9
  */
11
10
  import { getContext } from '../context.js';
12
11
  export class DeltaConfirmationTracker {
13
12
  ctx;
14
- // Delta confirmation retry config (Replicache-style exponential backoff)
15
- // Max retries before requesting full reconciliation
13
+ // Retry configuration for delta confirmation, using exponential backoff.
14
+ // Maximum retries before requesting a full reconciliation.
16
15
  static DELTA_MAX_RETRIES = 5;
17
- // Initial timeout (first attempt)
18
- static DELTA_INITIAL_TIMEOUT_MS = 30_000;
19
- // Max timeout cap (like Replicache's maxDelayMs of 60s)
16
+ // Upper bound on the backoff timeout.
20
17
  static DELTA_MAX_TIMEOUT_MS = 120_000;
21
- // LINEAR PATTERN: Track delta confirmation timeouts for awaiting_delta transactions
22
- // Following Replicache/PowerSync pattern: retry with backoff instead of rolling back
18
+ // Pending confirmation timeouts for transactions awaiting their delta. On
19
+ // timeout the tracker retries with backoff rather than rolling back.
23
20
  deltaConfirmationTimeouts = new Map();
24
21
  // Track retry attempts per transaction for exponential backoff
25
22
  deltaConfirmationRetries = new Map();
@@ -34,12 +31,12 @@ export class DeltaConfirmationTracker {
34
31
  this.ctx.position.noteAck(lastSyncId);
35
32
  }
36
33
  /**
37
- * LINEAR PATTERN: Confirm all awaiting transactions when delta with syncId >= threshold arrives.
38
- * This replaces clientMutationId echoing - transactions are confirmed by sync ID threshold.
39
- * @param syncId - The sync ID of the received delta
34
+ * Confirms every awaiting transaction whose sync-id threshold this delta
35
+ * meets or exceeds.
36
+ * @param syncId - The sync id of the received delta.
40
37
  */
41
38
  onDeltaReceived(syncId) {
42
- // Cursor advancing happens where the delta is APPLIED (the store calls
39
+ // The cursor advances where the delta is applied (the store calls
43
40
  // position.advanceApplied / advancePersisted); this hook only resolves
44
41
  // confirmation thresholds against the incoming id.
45
42
  const awaitingTxs = this.ctx.store.getByStatus('awaiting_delta');
@@ -82,7 +79,7 @@ export class DeltaConfirmationTracker {
82
79
  }
83
80
  // Log batch summary only if we confirmed something
84
81
  if (confirmedCount > 0) {
85
- // Use warn for staging visibility when transactions confirm
82
+ // Leave a breadcrumb when transactions confirm.
86
83
  getContext().observability.breadcrumb('Transactions confirmed via delta', 'sync.transaction', 'info', {
87
84
  count: confirmedCount,
88
85
  syncId,
@@ -90,15 +87,16 @@ export class DeltaConfirmationTracker {
90
87
  });
91
88
  }
92
89
  }
93
- // REPLICACHE/POWERSYNC PATTERN: Schedule delta confirmation with retry + reconciliation
94
- // Instead of rolling back on timeout (which destroys confirmed server state),
95
- // retry with exponential backoff and request reconciliation to catch up on missed deltas.
96
- // Only rollback on explicit server rejection, never on timeout.
90
+ // Schedule the confirmation wait for a transaction. On timeout the tracker
91
+ // retries with exponential backoff and requests reconciliation to catch up on
92
+ // missed deltas, rather than rolling back, which would discard state the
93
+ // server has already confirmed. A rollback happens only on an explicit server
94
+ // rejection, never on a timeout.
97
95
  scheduleDeltaConfirmationTimeout(tx, timeoutMs) {
98
96
  // Cancel any existing timeout for this transaction
99
97
  this.cancelDeltaConfirmationTimeout(tx.id);
100
- // NB: deliberately NOT an async callback the body is fully synchronous,
101
- // and `setTimeout(async …)` turns any throw into an unhandled promise
98
+ // Deliberately not an async callback: the body is fully synchronous, and
99
+ // `setTimeout(async …)` would turn any throw into an unhandled promise
102
100
  // rejection instead of a catchable synchronous error.
103
101
  const timeoutHandle = setTimeout(() => {
104
102
  const currentTx = this.ctx.store.get(tx.id);
@@ -119,12 +117,6 @@ export class DeltaConfirmationTracker {
119
117
  return;
120
118
  }
121
119
  const retryCount = this.deltaConfirmationRetries.get(tx.id) ?? 0;
122
- const diagnosis = this.lastSeenSyncId === 0
123
- ? 'No deltas received - delta pipeline may be broken'
124
- : currentTx.syncIdNeededForCompletion &&
125
- this.lastSeenSyncId < currentTx.syncIdNeededForCompletion
126
- ? 'Delta not yet received - may be lost or delayed'
127
- : 'Delta should have confirmed - possible race condition';
128
120
  getContext().observability.captureReconciliation({
129
121
  reason: 'delta_timeout',
130
122
  model: tx.modelName,
@@ -135,14 +127,15 @@ export class DeltaConfirmationTracker {
135
127
  connectionState: this.ctx.isConnected() ? 'connected' : 'disconnected',
136
128
  });
137
129
  if (retryCount < DeltaConfirmationTracker.DELTA_MAX_RETRIES) {
138
- // RETRY: Request reconciliation and re-schedule with exponential backoff
139
- // The server already committed this mutation we just need the delta to arrive
130
+ // Retry: request reconciliation and re-schedule with exponential
131
+ // backoff. The server has already committed the mutation; only the
132
+ // delta is outstanding.
140
133
  this.deltaConfirmationRetries.set(tx.id, retryCount + 1);
141
134
  this.deltaConfirmationTimeouts.delete(tx.id);
142
135
  // Exponential backoff: 30s → 60s → 120s → 120s → 120s (capped)
143
136
  const nextTimeout = Math.min(timeoutMs * 2, DeltaConfirmationTracker.DELTA_MAX_TIMEOUT_MS);
144
- // Emit reconciliation request so SyncedStore can cycle the WebSocket
145
- // to trigger delta catch-up from the server
137
+ // Request reconciliation so the client can cycle the connection and
138
+ // catch up on missed deltas from the server.
146
139
  this.ctx.emit('reconciliation:needed', {
147
140
  reason: 'delta_confirmation_timeout',
148
141
  txId: tx.id,
@@ -163,10 +156,10 @@ export class DeltaConfirmationTracker {
163
156
  this.scheduleDeltaConfirmationTimeout(tx, nextTimeout);
164
157
  }
165
158
  else {
166
- // LINEAR PATTERN: Retries exhausted persist to IndexedDB instead of rolling back.
167
- // The transaction succeeded on the server (HTTP 200), so the data exists server-side.
168
- // Persist the awaiting state so it survives tab close. On next session, the WebSocket
169
- // reconnect + delta catch-up will naturally confirm it (like Linear's IndexedDB caching).
159
+ // Retries exhausted: persist the awaiting state instead of rolling back.
160
+ // The commit succeeded on the server, so the data exists there. Saving
161
+ // the awaiting state lets it survive the page closing; on the next
162
+ // session, reconnecting and catching up on deltas will confirm it.
170
163
  this.deltaConfirmationRetries.delete(tx.id);
171
164
  this.deltaConfirmationTimeouts.delete(tx.id);
172
165
  getContext().observability.captureDeltaRetryExhausted({
@@ -176,7 +169,7 @@ export class DeltaConfirmationTracker {
176
169
  retryCount: DeltaConfirmationTracker.DELTA_MAX_RETRIES,
177
170
  syncIdNeeded: currentTx.syncIdNeededForCompletion,
178
171
  });
179
- // Emit persist event SyncClient handles the IDB write
172
+ // Emit the persist event; the client performs the write to local storage.
180
173
  this.ctx.emit('transaction:persist_awaiting', {
181
174
  txId: tx.id,
182
175
  model: tx.modelName,
@@ -208,10 +201,9 @@ export class DeltaConfirmationTracker {
208
201
  this.deltaConfirmationRetries.delete(id);
209
202
  }
210
203
  /**
211
- * Tear down every armed confirmation timer (30–120s each, one per
212
- * in-flight transaction). Called from `TransactionQueue.dispose()`
213
- * without it a disposed queue kept the Node process alive and fired
214
- * callbacks against an already-cleared store (T1.19).
204
+ * Clears every armed confirmation timer, one per in-flight transaction.
205
+ * {@link TransactionQueue.dispose} calls this; without it a disposed queue
206
+ * would keep the process alive and fire callbacks against a cleared store.
215
207
  */
216
208
  dispose() {
217
209
  for (const timeoutHandle of this.deltaConfirmationTimeouts.values()) {
@@ -0,0 +1,43 @@
1
+ /** Crash-durable exact HTTP request used by the stateless agent client. */
2
+ import { z } from 'zod';
3
+ export declare const HTTP_COMMIT_ENVELOPE_VERSION: 1;
4
+ export declare const HTTP_COMMIT_ENVELOPE_PREFIX = "http-commit-envelope:";
5
+ /** Stay one hour inside the server's 24-hour idempotency retention window. */
6
+ export declare const HTTP_COMMIT_REPLAY_WINDOW_MS: number;
7
+ /** Apply normal JSON semantics once, then make object-key order canonical. */
8
+ export declare function canonicalHttpCommitBody(value: unknown): string;
9
+ export declare const durableHttpCommitEnvelopeSchema: z.ZodObject<{
10
+ id: z.ZodString;
11
+ type: z.ZodLiteral<"http_commit_envelope">;
12
+ storageVersion: z.ZodLiteral<1>;
13
+ idempotencyKey: z.core.$ZodBranded<z.ZodString, "IdempotencyKey", "out">;
14
+ request: z.ZodObject<{
15
+ method: z.ZodEnum<{
16
+ DELETE: "DELETE";
17
+ POST: "POST";
18
+ PATCH: "PATCH";
19
+ }>;
20
+ path: z.ZodString;
21
+ body: z.ZodString;
22
+ }, z.core.$strict>;
23
+ scopeNamespace: z.ZodString;
24
+ createdAt: z.ZodNumber;
25
+ sealedAt: z.ZodNumber;
26
+ sequence: z.ZodOptional<z.ZodNumber>;
27
+ timestamp: z.ZodNumber;
28
+ }, z.core.$strict>;
29
+ export type DurableHttpCommitEnvelope = z.infer<typeof durableHttpCommitEnvelopeSchema>;
30
+ export declare function httpCommitEnvelopeRecordId(idempotencyKey: string, scopeNamespace?: string): string;
31
+ export declare function createDurableHttpCommitEnvelope(input: {
32
+ idempotencyKey: string;
33
+ request: {
34
+ method: 'POST' | 'PATCH' | 'DELETE';
35
+ path: string;
36
+ body: unknown;
37
+ };
38
+ scopeNamespace: string;
39
+ createdAt?: number;
40
+ sealedAt?: number;
41
+ sequence?: number;
42
+ }): DurableHttpCommitEnvelope;
43
+ export declare function isHttpCommitReplayExpired(envelope: Pick<DurableHttpCommitEnvelope, 'sealedAt'>, now?: number): boolean;
@@ -0,0 +1,179 @@
1
+ /** Crash-durable exact HTTP request used by the stateless agent client. */
2
+ import { z } from 'zod';
3
+ import { v5 as uuidv5 } from 'uuid';
4
+ import { idempotencyKeySchema } from '../commit/contract.js';
5
+ import { stableStringify } from '../utils/json.js';
6
+ export const HTTP_COMMIT_ENVELOPE_VERSION = 1;
7
+ export const HTTP_COMMIT_ENVELOPE_PREFIX = 'http-commit-envelope:';
8
+ /** Stay one hour inside the server's 24-hour idempotency retention window. */
9
+ export const HTTP_COMMIT_REPLAY_WINDOW_MS = 23 * 60 * 60 * 1000;
10
+ const HTTP_COMMIT_SCOPE_ID_NAMESPACE = '043e8f73-86fc-5f62-af46-935d68fca729';
11
+ const commitPathSchema = z.literal('/v1/commits');
12
+ const modelCollectionPathSchema = z.string().regex(/^\/v1\/models\/[^/]+$/);
13
+ const modelEntityPathSchema = z.string().regex(/^\/v1\/models\/[^/]+\/[^/]+$/);
14
+ function hasSafeModelPathSegments(path) {
15
+ if (!path.startsWith('/v1/models/'))
16
+ return true;
17
+ try {
18
+ return path
19
+ .slice('/v1/models/'.length)
20
+ .split('/')
21
+ .every((segment) => {
22
+ const decoded = decodeURIComponent(segment);
23
+ return (decoded.length > 0 &&
24
+ decoded !== '.' &&
25
+ decoded !== '..' &&
26
+ !decoded.includes('/') &&
27
+ !decoded.includes('\\'));
28
+ });
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ /** Apply normal JSON semantics once, then make object-key order canonical. */
35
+ export function canonicalHttpCommitBody(value) {
36
+ const serialized = JSON.stringify(value);
37
+ if (serialized === undefined) {
38
+ throw new TypeError('HTTP commit body is not JSON serializable');
39
+ }
40
+ return stableStringify(JSON.parse(serialized));
41
+ }
42
+ export const durableHttpCommitEnvelopeSchema = z
43
+ .strictObject({
44
+ id: z.string().startsWith(HTTP_COMMIT_ENVELOPE_PREFIX),
45
+ type: z.literal('http_commit_envelope'),
46
+ storageVersion: z.literal(HTTP_COMMIT_ENVELOPE_VERSION),
47
+ idempotencyKey: idempotencyKeySchema,
48
+ request: z.strictObject({
49
+ method: z.enum(['POST', 'PATCH', 'DELETE']),
50
+ path: z.string().startsWith('/'),
51
+ body: z.string().refine((body) => {
52
+ try {
53
+ JSON.parse(body);
54
+ return true;
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ }, 'HTTP commit body must be valid JSON'),
60
+ }),
61
+ scopeNamespace: z.string().min(1),
62
+ createdAt: z.number().int().nonnegative(),
63
+ sealedAt: z.number().int().nonnegative(),
64
+ /** Monotonic within one client; disambiguates writes sealed in the same ms. */
65
+ sequence: z.number().int().nonnegative().optional(),
66
+ timestamp: z.number().int().nonnegative(),
67
+ })
68
+ .superRefine((envelope, context) => {
69
+ const legacyId = httpCommitEnvelopeRecordId(envelope.idempotencyKey);
70
+ const scopedId = httpCommitEnvelopeRecordId(envelope.idempotencyKey, envelope.scopeNamespace);
71
+ if (envelope.id !== legacyId && envelope.id !== scopedId) {
72
+ context.addIssue({
73
+ code: 'custom',
74
+ path: ['id'],
75
+ message: 'HTTP envelope id must be derived from its idempotency key',
76
+ });
77
+ }
78
+ if (envelope.sealedAt < envelope.createdAt) {
79
+ context.addIssue({
80
+ code: 'custom',
81
+ path: ['sealedAt'],
82
+ message: 'HTTP envelope cannot be sealed before it is created',
83
+ });
84
+ }
85
+ const { method, path } = envelope.request;
86
+ const allowedPath = method === 'POST'
87
+ ? commitPathSchema.safeParse(path).success ||
88
+ modelCollectionPathSchema.safeParse(path).success
89
+ : modelEntityPathSchema.safeParse(path).success;
90
+ if (!allowedPath || !hasSafeModelPathSegments(path)) {
91
+ context.addIssue({
92
+ code: 'custom',
93
+ path: ['request', 'path'],
94
+ message: 'HTTP outbox records may target only commit or model-mutation routes',
95
+ });
96
+ return;
97
+ }
98
+ let body;
99
+ try {
100
+ body = JSON.parse(envelope.request.body);
101
+ }
102
+ catch {
103
+ return; // The field-level JSON refinement reports this.
104
+ }
105
+ if (typeof body !== 'object' || body === null || Array.isArray(body)) {
106
+ context.addIssue({
107
+ code: 'custom',
108
+ path: ['request', 'body'],
109
+ message: 'HTTP commit body must be a JSON object',
110
+ });
111
+ return;
112
+ }
113
+ const record = body;
114
+ if (record.idempotencyKey !== envelope.idempotencyKey) {
115
+ context.addIssue({
116
+ code: 'custom',
117
+ path: ['request', 'body', 'idempotencyKey'],
118
+ message: 'HTTP body idempotency key must match its envelope',
119
+ });
120
+ }
121
+ if (path === '/v1/commits') {
122
+ if (record.clientTxId !== envelope.idempotencyKey ||
123
+ !Array.isArray(record.operations) ||
124
+ record.operations.length === 0) {
125
+ context.addIssue({
126
+ code: 'custom',
127
+ path: ['request', 'body'],
128
+ message: 'Commit-route body must carry the same clientTxId and operations',
129
+ });
130
+ }
131
+ }
132
+ else if (method === 'POST' && typeof record.id !== 'string') {
133
+ context.addIssue({
134
+ code: 'custom',
135
+ path: ['request', 'body', 'id'],
136
+ message: 'Model-create body must carry its entity id',
137
+ });
138
+ }
139
+ else if (method === 'PATCH' &&
140
+ (typeof record.data !== 'object' || record.data === null || Array.isArray(record.data))) {
141
+ context.addIssue({
142
+ code: 'custom',
143
+ path: ['request', 'body', 'data'],
144
+ message: 'Model-update body must carry a JSON object patch',
145
+ });
146
+ }
147
+ });
148
+ export function httpCommitEnvelopeRecordId(idempotencyKey, scopeNamespace) {
149
+ if (!scopeNamespace) {
150
+ return `${HTTP_COMMIT_ENVELOPE_PREFIX}${idempotencyKey}`;
151
+ }
152
+ const scopeId = uuidv5(scopeNamespace, HTTP_COMMIT_SCOPE_ID_NAMESPACE);
153
+ return `${HTTP_COMMIT_ENVELOPE_PREFIX}${scopeId}:${idempotencyKey}`;
154
+ }
155
+ export function createDurableHttpCommitEnvelope(input) {
156
+ const now = Date.now();
157
+ const createdAt = input.createdAt ?? now;
158
+ const sealedAt = input.sealedAt ?? now;
159
+ const body = canonicalHttpCommitBody(input.request.body);
160
+ return durableHttpCommitEnvelopeSchema.parse({
161
+ id: httpCommitEnvelopeRecordId(input.idempotencyKey, input.scopeNamespace),
162
+ type: 'http_commit_envelope',
163
+ storageVersion: HTTP_COMMIT_ENVELOPE_VERSION,
164
+ idempotencyKey: input.idempotencyKey,
165
+ request: {
166
+ method: input.request.method,
167
+ path: input.request.path,
168
+ body,
169
+ },
170
+ scopeNamespace: input.scopeNamespace,
171
+ createdAt,
172
+ sealedAt,
173
+ ...(input.sequence !== undefined ? { sequence: input.sequence } : {}),
174
+ timestamp: sealedAt,
175
+ });
176
+ }
177
+ export function isHttpCommitReplayExpired(envelope, now = Date.now()) {
178
+ return now - envelope.sealedAt >= HTTP_COMMIT_REPLAY_WINDOW_MS;
179
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Local-first apply and rollback bookkeeping for pending mutations.
3
+ *
4
+ * When a change is written before the server confirms it, these functions
5
+ * record it in a ledger and emit an `optimistic:*` event; separate listeners
6
+ * apply the change to the in-memory data and, on rollback, restore the value
7
+ * it replaced. Each function takes the ledger and a small event emitter rather
8
+ * than any larger object, so the rules have no dependencies of their own and
9
+ * can be tested in isolation.
10
+ */
11
+ import type { Model } from '../Model.js';
12
+ import type { MutationInput, Transaction } from './commitPayload.js';
13
+ /**
14
+ * One tracked optimistic mutation: the live model plus the value it held
15
+ * before the change, kept so a rollback can restore it.
16
+ */
17
+ export interface OptimisticUpdateEntry {
18
+ model: Model;
19
+ previousState: MutationInput | null | undefined;
20
+ transaction: Transaction;
21
+ }
22
+ /**
23
+ * The event emitter these functions use to announce each optimistic change and
24
+ * its rollback. You supply the implementation.
25
+ */
26
+ export interface OptimisticEmitter {
27
+ emit(event: string, payload: unknown): void;
28
+ }
29
+ /**
30
+ * Record an optimistic create and announce it. There is no prior value to
31
+ * restore, so the rollback pre-image is `null`.
32
+ */
33
+ export declare function applyOptimisticCreate(optimisticUpdates: Map<string, OptimisticUpdateEntry>, emitter: OptimisticEmitter, model: Model, transaction: Transaction): void;
34
+ /**
35
+ * Record an optimistic update and announce it, keeping the row's prior value so
36
+ * a rollback can put it back.
37
+ */
38
+ export declare function applyOptimisticUpdate(optimisticUpdates: Map<string, OptimisticUpdateEntry>, emitter: OptimisticEmitter, model: Model, transaction: Transaction): void;
39
+ /**
40
+ * Record an optimistic delete and announce it, keeping the deleted row so a
41
+ * rollback can restore it.
42
+ */
43
+ export declare function applyOptimisticDelete(optimisticUpdates: Map<string, OptimisticUpdateEntry>, emitter: OptimisticEmitter, model: Model, transaction: Transaction): void;
44
+ /**
45
+ * Undo an optimistic mutation by its transaction id: emit `optimistic:rollback`
46
+ * with the saved pre-image so listeners can restore the prior value, then drop
47
+ * the ledger entry. Does nothing if the transaction was never tracked.
48
+ */
49
+ export declare function rollbackOptimistic(optimisticUpdates: Map<string, OptimisticUpdateEntry>, emitter: OptimisticEmitter, transaction: Transaction, reason?: string, error?: Error): Promise<void>;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Local-first apply and rollback bookkeeping for pending mutations.
3
+ *
4
+ * When a change is written before the server confirms it, these functions
5
+ * record it in a ledger and emit an `optimistic:*` event; separate listeners
6
+ * apply the change to the in-memory data and, on rollback, restore the value
7
+ * it replaced. Each function takes the ledger and a small event emitter rather
8
+ * than any larger object, so the rules have no dependencies of their own and
9
+ * can be tested in isolation.
10
+ */
11
+ /**
12
+ * Record an optimistic create and announce it. There is no prior value to
13
+ * restore, so the rollback pre-image is `null`.
14
+ */
15
+ export function applyOptimisticCreate(optimisticUpdates, emitter, model, transaction) {
16
+ optimisticUpdates.set(transaction.id, {
17
+ model,
18
+ previousState: null,
19
+ transaction,
20
+ });
21
+ emitter.emit('optimistic:create', { model, transaction });
22
+ }
23
+ /**
24
+ * Record an optimistic update and announce it, keeping the row's prior value so
25
+ * a rollback can put it back.
26
+ */
27
+ export function applyOptimisticUpdate(optimisticUpdates, emitter, model, transaction) {
28
+ optimisticUpdates.set(transaction.id, {
29
+ model,
30
+ previousState: transaction.previousData,
31
+ transaction,
32
+ });
33
+ emitter.emit('optimistic:update', { model, transaction });
34
+ }
35
+ /**
36
+ * Record an optimistic delete and announce it, keeping the deleted row so a
37
+ * rollback can restore it.
38
+ */
39
+ export function applyOptimisticDelete(optimisticUpdates, emitter, model, transaction) {
40
+ optimisticUpdates.set(transaction.id, {
41
+ model,
42
+ previousState: transaction.previousData,
43
+ transaction,
44
+ });
45
+ emitter.emit('optimistic:delete', { model, transaction });
46
+ }
47
+ /**
48
+ * Undo an optimistic mutation by its transaction id: emit `optimistic:rollback`
49
+ * with the saved pre-image so listeners can restore the prior value, then drop
50
+ * the ledger entry. Does nothing if the transaction was never tracked.
51
+ */
52
+ export function rollbackOptimistic(optimisticUpdates, emitter, transaction, reason, error) {
53
+ const optimistic = optimisticUpdates.get(transaction.id);
54
+ if (!optimistic)
55
+ return Promise.resolve();
56
+ emitter.emit('optimistic:rollback', {
57
+ model: optimistic.model,
58
+ previousState: optimistic.previousState,
59
+ transaction,
60
+ reason: reason ?? 'unknown',
61
+ error,
62
+ });
63
+ optimisticUpdates.delete(transaction.id);
64
+ return Promise.resolve();
65
+ }