@abloatai/ablo 0.25.0 → 0.27.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 (425) hide show
  1. package/AGENTS.md +5 -3
  2. package/CHANGELOG.md +34 -0
  3. package/README.md +104 -88
  4. package/dist/BaseSyncedStore.d.ts +140 -266
  5. package/dist/BaseSyncedStore.js +338 -739
  6. package/dist/Database.d.ts +62 -77
  7. package/dist/Database.js +106 -127
  8. package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
  9. package/dist/{ObjectPool.js → InstanceCache.js} +91 -83
  10. package/dist/LazyReferenceCollection.d.ts +11 -15
  11. package/dist/LazyReferenceCollection.js +16 -15
  12. package/dist/Model.d.ts +37 -52
  13. package/dist/Model.js +52 -69
  14. package/dist/ModelRegistry.d.ts +46 -25
  15. package/dist/ModelRegistry.js +32 -30
  16. package/dist/NetworkMonitor.d.ts +5 -6
  17. package/dist/NetworkMonitor.js +6 -7
  18. package/dist/SyncClient.d.ts +119 -109
  19. package/dist/SyncClient.js +303 -224
  20. package/dist/SyncEngineContext.d.ts +1 -3
  21. package/dist/SyncEngineContext.js +1 -2
  22. package/dist/adapters/alwaysOnline.d.ts +6 -8
  23. package/dist/adapters/alwaysOnline.js +6 -8
  24. package/dist/adapters/inMemoryStorage.d.ts +9 -9
  25. package/dist/adapters/inMemoryStorage.js +9 -9
  26. package/dist/agent/Agent.d.ts +39 -31
  27. package/dist/agent/Agent.js +35 -23
  28. package/dist/agent/index.d.ts +4 -4
  29. package/dist/agent/index.js +5 -5
  30. package/dist/agent/session.d.ts +47 -44
  31. package/dist/agent/session.js +37 -48
  32. package/dist/agent/types.d.ts +26 -31
  33. package/dist/agent/types.js +6 -7
  34. package/dist/ai-sdk/coordinatedTool.d.ts +108 -0
  35. package/dist/ai-sdk/{coordinated-tool.js → coordinatedTool.js} +44 -38
  36. package/dist/ai-sdk/coordinationContext.d.ts +46 -0
  37. package/dist/ai-sdk/{coordination-context.js → coordinationContext.js} +30 -31
  38. package/dist/ai-sdk/index.d.ts +25 -22
  39. package/dist/ai-sdk/index.js +25 -22
  40. package/dist/ai-sdk/wrap.d.ts +7 -8
  41. package/dist/ai-sdk/wrap.js +2 -2
  42. package/dist/auth/credentialPolicy.d.ts +74 -71
  43. package/dist/auth/credentialPolicy.js +51 -56
  44. package/dist/auth/credentialSource.d.ts +7 -18
  45. package/dist/auth/credentialSource.js +10 -18
  46. package/dist/auth/index.d.ts +59 -58
  47. package/dist/auth/index.js +34 -40
  48. package/dist/auth/schemas.d.ts +5 -4
  49. package/dist/auth/schemas.js +5 -4
  50. package/dist/batching/index.d.ts +19 -21
  51. package/dist/batching/index.js +14 -17
  52. package/dist/cli.cjs +483 -369
  53. package/dist/client/Ablo.d.ts +107 -836
  54. package/dist/client/Ablo.js +174 -833
  55. package/dist/client/ApiClient.d.ts +44 -20
  56. package/dist/client/ApiClient.js +193 -44
  57. package/dist/client/auth.d.ts +51 -60
  58. package/dist/client/auth.js +137 -110
  59. package/dist/client/claimHeartbeatLoop.d.ts +50 -0
  60. package/dist/client/claimHeartbeatLoop.js +88 -0
  61. package/dist/client/consoleLogger.d.ts +35 -0
  62. package/dist/client/consoleLogger.js +44 -0
  63. package/dist/client/createInternalComponents.d.ts +14 -17
  64. package/dist/client/createInternalComponents.js +26 -31
  65. package/dist/client/createModelProxy.d.ts +130 -120
  66. package/dist/client/createModelProxy.js +158 -124
  67. package/dist/client/credentialEndpoint.d.ts +61 -0
  68. package/dist/client/credentialEndpoint.js +86 -0
  69. package/dist/client/functionalUpdate.d.ts +29 -27
  70. package/dist/client/functionalUpdate.js +21 -21
  71. package/dist/client/hostedEndpoints.d.ts +21 -0
  72. package/dist/client/hostedEndpoints.js +21 -0
  73. package/dist/client/httpClient.d.ts +58 -54
  74. package/dist/client/httpClient.js +29 -31
  75. package/dist/client/identity.d.ts +15 -20
  76. package/dist/client/identity.js +49 -59
  77. package/dist/client/modelRegistration.d.ts +10 -0
  78. package/dist/client/modelRegistration.js +301 -0
  79. package/dist/client/options.d.ts +373 -0
  80. package/dist/client/options.js +6 -0
  81. package/dist/client/registerDataSource.d.ts +9 -9
  82. package/dist/client/registerDataSource.js +15 -16
  83. package/dist/client/resourceTypes.d.ts +333 -0
  84. package/dist/client/resourceTypes.js +7 -0
  85. package/dist/client/schemaConfig.d.ts +44 -0
  86. package/dist/client/schemaConfig.js +176 -0
  87. package/dist/client/sessionMint.d.ts +17 -13
  88. package/dist/client/sessionMint.js +26 -31
  89. package/dist/client/validateAbloOptions.d.ts +12 -14
  90. package/dist/client/validateAbloOptions.js +9 -10
  91. package/dist/client/writeOptionsSchema.d.ts +18 -16
  92. package/dist/client/writeOptionsSchema.js +23 -20
  93. package/dist/client/wsMutationExecutor.d.ts +28 -0
  94. package/dist/client/wsMutationExecutor.js +71 -0
  95. package/dist/context.d.ts +6 -4
  96. package/dist/context.js +6 -7
  97. package/dist/coordination/index.d.ts +13 -4
  98. package/dist/coordination/index.js +29 -4
  99. package/dist/coordination/schema.d.ts +176 -128
  100. package/dist/coordination/schema.js +197 -133
  101. package/dist/coordination/trace.d.ts +9 -11
  102. package/dist/coordination/trace.js +13 -15
  103. package/dist/core/DatabaseManager.d.ts +5 -8
  104. package/dist/core/DatabaseManager.js +38 -40
  105. package/dist/core/QueryProcessor.d.ts +7 -9
  106. package/dist/core/QueryProcessor.js +27 -34
  107. package/dist/core/QueryView.d.ts +17 -5
  108. package/dist/core/QueryView.js +6 -7
  109. package/dist/core/StoreManager.d.ts +14 -16
  110. package/dist/core/StoreManager.js +26 -25
  111. package/dist/core/ViewRegistry.d.ts +5 -5
  112. package/dist/core/ViewRegistry.js +4 -4
  113. package/dist/core/index.d.ts +18 -13
  114. package/dist/core/index.js +32 -26
  115. package/dist/core/openIDBWithTimeout.d.ts +38 -36
  116. package/dist/core/openIDBWithTimeout.js +57 -54
  117. package/dist/core/queryUtils.d.ts +45 -0
  118. package/dist/core/queryUtils.js +69 -0
  119. package/dist/core/storeContract.d.ts +145 -0
  120. package/dist/core/storeContract.js +12 -0
  121. package/dist/environment.d.ts +28 -0
  122. package/dist/environment.js +21 -0
  123. package/dist/errorCodes.d.ts +118 -101
  124. package/dist/errorCodes.js +277 -260
  125. package/dist/errors.d.ts +170 -165
  126. package/dist/errors.js +161 -151
  127. package/dist/index.d.ts +30 -27
  128. package/dist/index.js +90 -82
  129. package/dist/interfaces/index.d.ts +108 -133
  130. package/dist/interfaces/index.js +5 -4
  131. package/dist/keys/index.d.ts +27 -29
  132. package/dist/keys/index.js +59 -49
  133. package/dist/mutators/RecordingTransaction.d.ts +16 -16
  134. package/dist/mutators/RecordingTransaction.js +31 -37
  135. package/dist/mutators/Transaction.d.ts +18 -26
  136. package/dist/mutators/Transaction.js +14 -20
  137. package/dist/mutators/UndoManager.d.ts +122 -131
  138. package/dist/mutators/UndoManager.js +149 -155
  139. package/dist/mutators/defineMutators.d.ts +24 -37
  140. package/dist/mutators/defineMutators.js +14 -20
  141. package/dist/mutators/inverseOp.d.ts +12 -15
  142. package/dist/mutators/inverseOp.js +12 -15
  143. package/dist/mutators/mutateActions.d.ts +10 -9
  144. package/dist/mutators/mutateActions.js +1 -1
  145. package/dist/mutators/readerActions.d.ts +9 -8
  146. package/dist/mutators/readerActions.js +2 -2
  147. package/dist/mutators/undoApply.d.ts +31 -27
  148. package/dist/mutators/undoApply.js +26 -24
  149. package/dist/policy/index.d.ts +5 -3
  150. package/dist/policy/index.js +5 -3
  151. package/dist/policy/types.d.ts +105 -101
  152. package/dist/policy/types.js +67 -66
  153. package/dist/query/client.d.ts +32 -16
  154. package/dist/query/client.js +103 -72
  155. package/dist/query/types.d.ts +37 -60
  156. package/dist/query/types.js +13 -33
  157. package/dist/react/AbloProvider.d.ts +7 -11
  158. package/dist/react/AbloProvider.js +24 -17
  159. package/dist/react/context.d.ts +27 -146
  160. package/dist/react/context.js +9 -10
  161. package/dist/react/index.d.ts +41 -42
  162. package/dist/react/index.js +37 -38
  163. package/dist/react/internalContext.d.ts +17 -19
  164. package/dist/react/useAblo.d.ts +23 -22
  165. package/dist/react/useAblo.js +17 -15
  166. package/dist/react/useCurrentUserId.d.ts +8 -7
  167. package/dist/react/useCurrentUserId.js +8 -7
  168. package/dist/react/useErrorListener.d.ts +7 -7
  169. package/dist/react/useErrorListener.js +11 -12
  170. package/dist/react/useMutationFailureListener.d.ts +8 -8
  171. package/dist/react/useMutationFailureListener.js +9 -9
  172. package/dist/react/useMutators.d.ts +11 -11
  173. package/dist/react/useMutators.js +10 -4
  174. package/dist/react/useReactive.js +2 -3
  175. package/dist/react/useSyncStatus.d.ts +4 -6
  176. package/dist/react/useUndoScope.d.ts +7 -9
  177. package/dist/react/useUndoScope.js +3 -3
  178. package/dist/schema/coordination.d.ts +21 -25
  179. package/dist/schema/coordination.js +21 -25
  180. package/dist/schema/ddl.d.ts +43 -39
  181. package/dist/schema/ddl.js +75 -68
  182. package/dist/schema/ddlLock.d.ts +35 -0
  183. package/dist/schema/ddlLock.js +46 -0
  184. package/dist/schema/diff.d.ts +99 -61
  185. package/dist/schema/diff.js +43 -34
  186. package/dist/schema/field.d.ts +37 -42
  187. package/dist/schema/field.js +36 -49
  188. package/dist/schema/generate.d.ts +12 -12
  189. package/dist/schema/generate.js +12 -12
  190. package/dist/schema/index.d.ts +5 -4
  191. package/dist/schema/index.js +29 -21
  192. package/dist/schema/model.d.ts +121 -146
  193. package/dist/schema/model.js +24 -35
  194. package/dist/schema/openapi.d.ts +10 -9
  195. package/dist/schema/openapi.js +7 -1
  196. package/dist/schema/queries.d.ts +30 -32
  197. package/dist/schema/queries.js +24 -25
  198. package/dist/schema/relation.d.ts +89 -99
  199. package/dist/schema/relation.js +13 -13
  200. package/dist/schema/residency.d.ts +38 -0
  201. package/dist/schema/residency.js +30 -0
  202. package/dist/schema/roles.d.ts +45 -27
  203. package/dist/schema/roles.js +52 -21
  204. package/dist/schema/schema.d.ts +36 -45
  205. package/dist/schema/schema.js +42 -39
  206. package/dist/schema/select.d.ts +13 -13
  207. package/dist/schema/select.js +13 -13
  208. package/dist/schema/serialize.d.ts +36 -39
  209. package/dist/schema/serialize.js +27 -31
  210. package/dist/schema/sugar.d.ts +17 -32
  211. package/dist/schema/sugar.js +14 -29
  212. package/dist/schema/{sync-delta-row.d.ts → syncDeltaRow.d.ts} +27 -50
  213. package/dist/schema/syncDeltaRow.js +89 -0
  214. package/dist/schema/tenancy.d.ts +44 -46
  215. package/dist/schema/tenancy.js +46 -48
  216. package/dist/server/adapter.d.ts +58 -58
  217. package/dist/server/adapter.js +13 -14
  218. package/dist/server/commit.d.ts +60 -64
  219. package/dist/server/index.d.ts +9 -10
  220. package/dist/server/index.js +1 -1
  221. package/dist/server/readConfig.d.ts +70 -0
  222. package/dist/server/readConfig.js +8 -0
  223. package/dist/server/storageMode.d.ts +23 -0
  224. package/dist/server/storageMode.js +17 -0
  225. package/dist/source/adapter.d.ts +31 -26
  226. package/dist/source/adapter.js +10 -10
  227. package/dist/source/adapters/drizzle.d.ts +28 -23
  228. package/dist/source/adapters/drizzle.js +34 -28
  229. package/dist/source/adapters/kysely.d.ts +27 -25
  230. package/dist/source/adapters/kysely.js +28 -26
  231. package/dist/source/adapters/memory.d.ts +8 -7
  232. package/dist/source/adapters/memory.js +10 -9
  233. package/dist/source/adapters/prisma.d.ts +13 -12
  234. package/dist/source/adapters/prisma.js +27 -29
  235. package/dist/source/conformance.d.ts +18 -11
  236. package/dist/source/conformance.js +27 -19
  237. package/dist/source/connector.d.ts +31 -32
  238. package/dist/source/connector.js +30 -28
  239. package/dist/source/connectorProtocol.d.ts +160 -0
  240. package/dist/source/connectorProtocol.js +162 -0
  241. package/dist/source/contract.d.ts +26 -27
  242. package/dist/source/contract.js +28 -29
  243. package/dist/source/factory.d.ts +94 -0
  244. package/dist/source/factory.js +268 -0
  245. package/dist/source/index.d.ts +10 -462
  246. package/dist/source/index.js +17 -421
  247. package/dist/source/migrations.d.ts +9 -9
  248. package/dist/source/migrations.js +9 -9
  249. package/dist/source/next.d.ts +10 -11
  250. package/dist/source/next.js +7 -8
  251. package/dist/source/pushQueue.d.ts +70 -48
  252. package/dist/source/pushQueue.js +36 -29
  253. package/dist/source/signing.d.ts +88 -0
  254. package/dist/source/signing.js +159 -0
  255. package/dist/source/types.d.ts +351 -0
  256. package/dist/source/types.js +43 -0
  257. package/dist/stores/ObjectStore.d.ts +11 -12
  258. package/dist/stores/ObjectStore.js +34 -35
  259. package/dist/stores/ObjectStoreContract.d.ts +12 -15
  260. package/dist/stores/SyncActionStore.d.ts +8 -12
  261. package/dist/stores/SyncActionStore.js +77 -46
  262. package/dist/surface.d.ts +28 -21
  263. package/dist/surface.js +28 -20
  264. package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +37 -45
  265. package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +101 -80
  266. package/dist/sync/ConnectionManager.d.ts +47 -50
  267. package/dist/sync/ConnectionManager.js +74 -70
  268. package/dist/sync/NetworkProbe.d.ts +27 -31
  269. package/dist/sync/NetworkProbe.js +67 -72
  270. package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +49 -36
  271. package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +79 -54
  272. package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +45 -59
  273. package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +44 -52
  274. package/dist/sync/SyncWebSocket.d.ts +175 -250
  275. package/dist/sync/SyncWebSocket.js +431 -769
  276. package/dist/sync/awaitClaimGrant.d.ts +18 -18
  277. package/dist/sync/awaitClaimGrant.js +38 -30
  278. package/dist/sync/bootstrapApply.d.ts +70 -0
  279. package/dist/sync/bootstrapApply.js +73 -0
  280. package/dist/sync/commitFrames.d.ts +44 -0
  281. package/dist/sync/commitFrames.js +94 -0
  282. package/dist/sync/createClaimStream.d.ts +23 -22
  283. package/dist/sync/createClaimStream.js +108 -25
  284. package/dist/sync/createPresenceStream.d.ts +19 -18
  285. package/dist/sync/createPresenceStream.js +25 -26
  286. package/dist/sync/createSnapshot.d.ts +13 -17
  287. package/dist/sync/createSnapshot.js +20 -26
  288. package/dist/sync/credentialLifecycle.d.ts +175 -0
  289. package/dist/sync/credentialLifecycle.js +322 -0
  290. package/dist/sync/deltaPipeline.d.ts +113 -0
  291. package/dist/sync/deltaPipeline.js +261 -0
  292. package/dist/sync/groupChange.d.ts +113 -0
  293. package/dist/sync/groupChange.js +242 -0
  294. package/dist/sync/heartbeat.d.ts +63 -0
  295. package/dist/sync/heartbeat.js +91 -0
  296. package/dist/sync/participants.d.ts +27 -27
  297. package/dist/sync/schemas.d.ts +3 -2
  298. package/dist/sync/schemas.js +14 -10
  299. package/dist/sync/syncCursor.d.ts +40 -0
  300. package/dist/sync/syncCursor.js +55 -0
  301. package/dist/sync/syncPlan.d.ts +54 -0
  302. package/dist/sync/syncPlan.js +50 -0
  303. package/dist/sync/syncPosition.d.ts +54 -49
  304. package/dist/sync/syncPosition.js +57 -52
  305. package/dist/sync/wsFrameHandlers.d.ts +116 -0
  306. package/dist/sync/wsFrameHandlers.js +374 -0
  307. package/dist/testing/fixtures/bootstrap.d.ts +21 -17
  308. package/dist/testing/fixtures/bootstrap.js +12 -6
  309. package/dist/testing/fixtures/deltas.d.ts +31 -34
  310. package/dist/testing/fixtures/deltas.js +30 -33
  311. package/dist/testing/fixtures/models.d.ts +11 -10
  312. package/dist/testing/fixtures/models.js +12 -10
  313. package/dist/testing/helpers/{react-wrapper.d.ts → reactWrapper.d.ts} +13 -10
  314. package/dist/testing/helpers/{react-wrapper.js → reactWrapper.js} +15 -12
  315. package/dist/testing/helpers/{sync-engine-harness.d.ts → syncEngineHarness.d.ts} +17 -18
  316. package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +14 -11
  317. package/dist/testing/helpers/wait.d.ts +13 -8
  318. package/dist/testing/helpers/wait.js +13 -8
  319. package/dist/testing/index.d.ts +4 -4
  320. package/dist/testing/index.js +3 -3
  321. package/dist/testing/mocks/MockMutationExecutor.d.ts +18 -17
  322. package/dist/testing/mocks/MockMutationExecutor.js +15 -14
  323. package/dist/testing/mocks/MockNetworkMonitor.d.ts +8 -8
  324. package/dist/testing/mocks/MockNetworkMonitor.js +8 -8
  325. package/dist/testing/mocks/MockSyncContext.d.ts +21 -34
  326. package/dist/testing/mocks/MockSyncContext.js +16 -45
  327. package/dist/testing/mocks/MockSyncStore.d.ts +14 -14
  328. package/dist/testing/mocks/MockSyncStore.js +11 -11
  329. package/dist/testing/mocks/MockWebSocket.d.ts +28 -24
  330. package/dist/testing/mocks/MockWebSocket.js +22 -21
  331. package/dist/transactions/TransactionQueue.d.ts +190 -221
  332. package/dist/transactions/TransactionQueue.js +424 -822
  333. package/dist/transactions/TransactionStore.d.ts +20 -0
  334. package/dist/transactions/TransactionStore.js +53 -0
  335. package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
  336. package/dist/transactions/UnconfirmedWrites.js +104 -0
  337. package/dist/transactions/coalesceRules.d.ts +58 -0
  338. package/dist/transactions/coalesceRules.js +140 -0
  339. package/dist/transactions/commitPayload.d.ts +130 -0
  340. package/dist/transactions/commitPayload.js +143 -0
  341. package/dist/transactions/deltaConfirmation.d.ts +58 -0
  342. package/dist/transactions/deltaConfirmation.js +215 -0
  343. package/dist/transactions/optimisticApply.d.ts +49 -0
  344. package/dist/transactions/optimisticApply.js +65 -0
  345. package/dist/transactions/replayValidation.d.ts +99 -0
  346. package/dist/transactions/replayValidation.js +111 -0
  347. package/dist/types/global.d.ts +46 -41
  348. package/dist/types/global.js +20 -19
  349. package/dist/types/index.d.ts +74 -80
  350. package/dist/types/index.js +22 -27
  351. package/dist/types/modelData.d.ts +10 -0
  352. package/dist/types/modelData.js +9 -0
  353. package/dist/types/participant.d.ts +20 -0
  354. package/dist/types/participant.js +10 -0
  355. package/dist/types/streams.d.ts +216 -209
  356. package/dist/types/streams.js +7 -7
  357. package/dist/utils/asyncIterator.d.ts +25 -32
  358. package/dist/utils/asyncIterator.js +25 -32
  359. package/dist/utils/duration.d.ts +12 -15
  360. package/dist/utils/duration.js +12 -15
  361. package/dist/utils/mobxSetup.d.ts +53 -0
  362. package/dist/utils/{mobx-setup.js → mobxSetup.js} +44 -100
  363. package/dist/webhooks/events.d.ts +21 -16
  364. package/dist/webhooks/events.js +10 -8
  365. package/dist/webhooks/index.d.ts +5 -7
  366. package/dist/webhooks/index.js +5 -7
  367. package/dist/{schema/sync-delta-wire.d.ts → wire/delta.d.ts} +58 -41
  368. package/dist/wire/delta.js +114 -0
  369. package/dist/wire/errorEnvelope.d.ts +35 -27
  370. package/dist/wire/errorEnvelope.js +38 -32
  371. package/dist/wire/frames.d.ts +150 -67
  372. package/dist/wire/frames.js +48 -1
  373. package/dist/wire/index.d.ts +18 -13
  374. package/dist/wire/index.js +36 -13
  375. package/dist/wire/listEnvelope.d.ts +16 -23
  376. package/dist/wire/listEnvelope.js +7 -6
  377. package/dist/wire/protocol.d.ts +38 -0
  378. package/dist/wire/protocol.js +38 -0
  379. package/dist/wire/protocolVersion.d.ts +60 -0
  380. package/dist/wire/protocolVersion.js +67 -0
  381. package/docs/api-keys.md +4 -3
  382. package/docs/coordination.md +59 -0
  383. package/docs/examples/existing-python-backend.md +3 -3
  384. package/docs/identity.md +4 -4
  385. package/docs/integration-guide.md +1 -1
  386. package/docs/react.md +1 -1
  387. package/docs/sessions.md +5 -7
  388. package/package.json +24 -21
  389. package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
  390. package/dist/ai-sdk/coordination-context.d.ts +0 -52
  391. package/dist/client/index.d.ts +0 -36
  392. package/dist/client/index.js +0 -33
  393. package/dist/config/index.d.ts +0 -10
  394. package/dist/config/index.js +0 -12
  395. package/dist/core/query-utils.d.ts +0 -34
  396. package/dist/core/query-utils.js +0 -59
  397. package/dist/interfaces/headless.d.ts +0 -95
  398. package/dist/interfaces/headless.js +0 -41
  399. package/dist/query/index.d.ts +0 -6
  400. package/dist/query/index.js +0 -5
  401. package/dist/realtime/index.d.ts +0 -10
  402. package/dist/realtime/index.js +0 -9
  403. package/dist/schema/plane.d.ts +0 -23
  404. package/dist/schema/plane.js +0 -19
  405. package/dist/schema/sync-delta-row.js +0 -103
  406. package/dist/schema/sync-delta-wire.js +0 -102
  407. package/dist/server/next.d.ts +0 -51
  408. package/dist/server/next.js +0 -47
  409. package/dist/server/read-config.d.ts +0 -67
  410. package/dist/server/read-config.js +0 -8
  411. package/dist/server/storage-mode.d.ts +0 -1
  412. package/dist/server/storage-mode.js +0 -18
  413. package/dist/source/connector-protocol.d.ts +0 -159
  414. package/dist/source/connector-protocol.js +0 -161
  415. package/dist/sync/OfflineFlush.d.ts +0 -9
  416. package/dist/sync/OfflineFlush.js +0 -22
  417. package/dist/sync/OfflineTransactionStore.d.ts +0 -37
  418. package/dist/sync/OfflineTransactionStore.js +0 -263
  419. package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
  420. package/dist/transactions/OptimisticEchoTracker.js +0 -104
  421. package/dist/transactions/index.d.ts +0 -16
  422. package/dist/transactions/index.js +0 -7
  423. package/dist/transactions/mutation-error-handler.d.ts +0 -5
  424. package/dist/transactions/mutation-error-handler.js +0 -39
  425. package/dist/utils/mobx-setup.d.ts +0 -42
@@ -1,29 +1,53 @@
1
1
  /**
2
- * Stateless API client for `Ablo({ apiKey })`.
3
- *
4
- * This is the hosted-API product surface: no schema, no object pool, no
5
- * IndexedDB, no WebSocket. It maps the public Model / Claim / Commit
6
- * nouns directly to HTTP routes on sync-server.
2
+ * The stateless API client behind `Ablo({ apiKey })`. It carries no schema,
3
+ * object pool, local database, or WebSocket, and maps the public Model, Claim,
4
+ * and Commit nouns directly to HTTP routes on the server. This is the transport
5
+ * used for server-side agents, workers, and serverless code.
7
6
  */
8
- import type { AbloOptions, CommitResource, ClaimCreateOptions, ClaimWaitOptions, ModelClient, ModelClaim, ModelTarget, CreateSessionParams, AbloSession } from './Ablo.js';
7
+ import type { AbloOptions } from './options.js';
8
+ import type { CommitResource, ClaimCreateOptions, ClaimWaitOptions, ModelClient, ModelClaim, ModelTarget, CreateSessionParams, AbloSession } from './resourceTypes.js';
9
9
  import type { SchemaRecord } from '../schema/schema.js';
10
10
  import type { Duration } from '../utils/duration.js';
11
- import type { Claim } from '../types/streams.js';
11
+ import type { Claim, ClaimHeartbeat } from '../types/streams.js';
12
12
  import type { SyncObservabilityProvider } from '../interfaces/index.js';
13
13
  export type AbloApiClientOptions = Omit<AbloOptions, 'schema'> & {
14
14
  readonly schema?: null | undefined;
15
15
  readonly bootstrapBaseUrl?: string | undefined;
16
16
  /**
17
- * Observability provider forwarded from `Ablo({ observability })`. The HTTP
18
- * transport emits the same claim/conflict seams as the WS transport so a
19
- * `ClaimLog` works identically for headless (server-agent) evals.
17
+ * The observability provider forwarded from `Ablo({ observability })`. The HTTP
18
+ * transport emits the same claim and conflict events as the WebSocket transport,
19
+ * so a `ClaimLog` works identically for headless server-agent evaluations.
20
20
  */
21
21
  readonly observability?: SyncObservabilityProvider;
22
+ /**
23
+ * Per-request deadline in milliseconds for the stateless HTTP transport.
24
+ * Every request this client issues is aborted after this long and surfaces
25
+ * as a retryable connection error — without it a black-holed server hangs
26
+ * a headless agent forever (browsers never time fetch out on their own).
27
+ * Pass `0` to disable the deadline.
28
+ *
29
+ * @default 30_000
30
+ */
31
+ readonly timeoutMs?: number;
22
32
  };
33
+ /** Default per-request deadline for the stateless HTTP transport. */
34
+ export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
23
35
  export interface AbloApiClaims {
24
36
  create(options: ClaimCreateOptions): Promise<Claim>;
25
37
  list(target?: Partial<ModelTarget>): Promise<readonly ModelClaim[]>;
26
38
  waitFor(target: Partial<ModelTarget>, options?: ClaimWaitOptions): Promise<void>;
39
+ /**
40
+ * The batched beat — extend every lease this credential holds in one
41
+ * request (`POST /v1/claims/heartbeat`), the stateless twin of the
42
+ * WebSocket keepalive. One round trip per cadence for a worker holding
43
+ * many rows. Returns one {@link ClaimHeartbeat} per extended lease,
44
+ * tagged with its claim id — no separate result type to learn.
45
+ */
46
+ heartbeatAll(options?: {
47
+ ttl?: Duration;
48
+ }): Promise<readonly (ClaimHeartbeat & {
49
+ readonly claimId: string;
50
+ })[]>;
27
51
  }
28
52
  export type CapabilityParticipantKind = 'agent' | 'system';
29
53
  export interface CapabilityCreateBaseOptions {
@@ -84,14 +108,14 @@ export interface CapabilityRevocation {
84
108
  }
85
109
  export interface CapabilityRotateOptions {
86
110
  /**
87
- * Overlap window — the OLD token keeps authenticating for this long after
88
- * rotation, so you can deploy the replacement with zero downtime. Default
89
- * 24h server-side.
111
+ * The overlap window — the old token keeps authenticating for this long after
112
+ * rotation, so you can deploy the replacement with zero downtime. Defaults to
113
+ * 24h on the server.
90
114
  */
91
115
  readonly grace?: Duration;
92
116
  readonly graceSeconds?: number;
93
117
  /**
94
- * Lifetime of the REPLACEMENT capability. Omit to inherit the original's
118
+ * The lifetime of the replacement capability. Omit to inherit the original's
95
119
  * lifetime.
96
120
  */
97
121
  readonly lease?: Duration;
@@ -113,9 +137,9 @@ export interface CapabilityResource {
113
137
  retrieve(id: string): Promise<CapabilityRecord>;
114
138
  revoke(id: string): Promise<CapabilityRevocation>;
115
139
  /**
116
- * Rotate with overlap (Stripe's "roll" model): mint a fresh capability
117
- * carrying the SAME scope, and keep the old token working for a grace
118
- * window so you can roll out the replacement without downtime.
140
+ * Rotate with overlap: mint a fresh capability that carries the same scope, and
141
+ * keep the old token working for a grace window so you can roll out the
142
+ * replacement without downtime.
119
143
  */
120
144
  rotate(id: string, options?: CapabilityRotateOptions): Promise<RotatedCapability>;
121
145
  /**
@@ -142,9 +166,9 @@ export interface AbloApi {
142
166
  */
143
167
  getAuthToken(): Promise<string | null>;
144
168
  /**
145
- * Mint a short-lived scoped session the Stripe `ephemeralKeys.create` shape.
146
- * Minting is a control-plane HTTP call (no socket), so it lives on this stateless
147
- * client too, not only the realtime one. `{ user }` `ek_`, `{ agent, can }` `rk_`.
169
+ * Mint a short-lived scoped session. Minting is a control-plane HTTP call (no
170
+ * socket), so it lives on this stateless client too, not only the realtime one.
171
+ * `{ user }` mints an `ek_`; `{ agent, can }` mints an `rk_`.
148
172
  */
149
173
  readonly sessions: {
150
174
  create(params: CreateSessionParams<SchemaRecord>): Promise<AbloSession>;
@@ -1,17 +1,36 @@
1
1
  /**
2
- * Stateless API client for `Ablo({ apiKey })`.
3
- *
4
- * This is the hosted-API product surface: no schema, no object pool, no
5
- * IndexedDB, no WebSocket. It maps the public Model / Claim / Commit
6
- * nouns directly to HTTP routes on sync-server.
2
+ * The stateless API client behind `Ablo({ apiKey })`. It carries no schema,
3
+ * object pool, local database, or WebSocket, and maps the public Model, Claim,
4
+ * and Commit nouns directly to HTTP routes on the server. This is the transport
5
+ * used for server-side agents, workers, and serverless code.
7
6
  */
8
7
  import { AbloClaimedError, AbloAuthenticationError, AbloConnectionError, AbloValidationError, AbloNotFoundError, claimedError, translateHttpError, } from '../errors.js';
9
8
  import { reconcileFunctionalUpdate, } from './functionalUpdate.js';
10
9
  import { assertBrowserSafety, readProcessEnv, resolveApiKey, resolveApiKeyValue, resolveAuthToken, resolveBaseURL, resolveBootstrapBaseUrl, resolveDatabaseUrl, warnIfCliKeyMismatch, warnIfDatabaseUrlEnvIgnored, warnIfDatabaseUrlDeprecated, } from './auth.js';
11
10
  import { registerDataSource } from './registerDataSource.js';
12
- import { toSeconds } from '../utils/duration.js';
11
+ import { PROTOCOL_VERSION, PROTOCOL_VERSION_HEADER } from '../wire/protocolVersion.js';
12
+ import { toMs, toSeconds } from '../utils/duration.js';
13
+ import { heartbeatCadenceMs, resolveHeartbeatOptions, startClaimHeartbeatLoop, } from './claimHeartbeatLoop.js';
13
14
  import { mintSession } from './sessionMint.js';
15
+ /**
16
+ * Interpret a heartbeat reply for a lease this handle HOLDS: anything other
17
+ * than `held` means the lease is no longer ours (a holder cannot be `queued`;
18
+ * `lost` rides a 409 that the wire error mapping already surfaces as
19
+ * AbloClaimedError before reaching here). The thrown loss is the definitive
20
+ * signal that stops the auto-heartbeat loop.
21
+ */
22
+ function heldHeartbeatReply(reply, label) {
23
+ if (reply.status === 'held' && typeof reply.expiresAt === 'number') {
24
+ return {
25
+ expiresAt: reply.expiresAt,
26
+ ...(reply.queueDepth !== undefined ? { queueDepth: reply.queueDepth } : {}),
27
+ };
28
+ }
29
+ throw new AbloClaimedError(`The lease behind ${label} is no longer held — it expired or was granted onward. Re-acquire the claim and retry; a write attempted under the old lease is rejected by its \`readAt\` guard.`, { code: 'claim_lost' });
30
+ }
14
31
  import { assertWriteOptions } from './writeOptionsSchema.js';
32
+ /** Default per-request deadline for the stateless HTTP transport. */
33
+ export const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
15
34
  const DEFAULT_AGENT_LEASE = '10m';
16
35
  export function createProtocolClient(options) {
17
36
  const env = readProcessEnv();
@@ -29,11 +48,12 @@ export function createProtocolClient(options) {
29
48
  databaseUrl: configuredDatabaseUrl,
30
49
  dangerouslyAllowBrowser: options.dangerouslyAllowBrowser,
31
50
  });
32
- // Observability seam for the STATELESS HTTP transport. The WS transport emits
33
- // claim/conflict events from SyncWebSocket; the HTTP path (server-side agents,
51
+ // Observability hook for the stateless HTTP transport. The WebSocket transport
52
+ // emits claim and conflict events; the HTTP path (server-side agents,
34
53
  // `transport: 'http'`) emitted nothing, so a `ClaimLog` handed to a headless
35
- // agent eval stayed empty. Mirror the two WS seams here: claim acquired +
36
- // coordination-conflict rejection. No-op when no provider is configured.
54
+ // agent evaluation stayed empty. This mirrors the two WebSocket events here:
55
+ // claim acquired and coordination-conflict rejection. A no-op when no provider
56
+ // is configured.
37
57
  const observability = options.observability;
38
58
  // Shared by the two HTTP write doors (`commits.create` + per-model
39
59
  // `mutateModel`): a rejected write whose code is a coordination conflict is
@@ -101,6 +121,9 @@ export function createProtocolClient(options) {
101
121
  const headers = {
102
122
  'Content-Type': 'application/json',
103
123
  Authorization: `Bearer ${token}`,
124
+ // Protocol handshake for the HTTP transport (wire/protocolVersion.ts):
125
+ // the server answers an out-of-range version with a typed 426.
126
+ [PROTOCOL_VERSION_HEADER]: String(PROTOCOL_VERSION),
104
127
  };
105
128
  for (const [key, value] of Object.entries(options.defaultHeaders ?? {})) {
106
129
  if (value == null) {
@@ -120,20 +143,66 @@ export function createProtocolClient(options) {
120
143
  }
121
144
  return target.toString();
122
145
  }
146
+ const requestTimeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
123
147
  async function requestJson(path, init) {
124
148
  await ready();
125
149
  const { idempotencyKey, ...requestInit } = init;
126
150
  const headers = await authHeaders();
127
151
  if (idempotencyKey)
128
152
  headers['Idempotency-Key'] = idempotencyKey;
129
- const res = await fetchImpl(endpoint(path), {
130
- ...requestInit,
131
- headers: {
132
- ...headers,
133
- ...requestInit.headers,
134
- },
135
- });
136
- const bodyText = await res.text();
153
+ // Deadline: abort the request after `timeoutMs` so a black-holed server
154
+ // can't hang the caller forever (fetch has NO default timeout in browsers,
155
+ // and only undici's generous defaults in Node). A caller-supplied signal
156
+ // is combined with the deadline via a shared controller — the portable
157
+ // equivalent of `AbortSignal.any([caller, AbortSignal.timeout(t)])`,
158
+ // which older runtimes (and the jsdom test env) don't implement. The
159
+ // same pattern already guards `query/client.ts` and `BootstrapFetcher`.
160
+ const callerSignal = requestInit.signal ?? undefined;
161
+ const controller = new AbortController();
162
+ const onCallerAbort = () => { controller.abort(callerSignal?.reason); };
163
+ if (callerSignal) {
164
+ if (callerSignal.aborted)
165
+ onCallerAbort();
166
+ else
167
+ callerSignal.addEventListener('abort', onCallerAbort, { once: true });
168
+ }
169
+ let timedOut = false;
170
+ const deadline = requestTimeoutMs > 0
171
+ ? setTimeout(() => {
172
+ timedOut = true;
173
+ controller.abort();
174
+ }, requestTimeoutMs)
175
+ : null;
176
+ let res;
177
+ let bodyText;
178
+ try {
179
+ res = await fetchImpl(endpoint(path), {
180
+ ...requestInit,
181
+ signal: controller.signal,
182
+ headers: {
183
+ ...headers,
184
+ ...requestInit.headers,
185
+ },
186
+ });
187
+ // Keep the deadline armed while the body streams — a server that sends
188
+ // headers then stalls the body is the same hang with better manners.
189
+ bodyText = await res.text();
190
+ }
191
+ catch (error) {
192
+ if (timedOut) {
193
+ // Retryable by contract: `wait_for_timeout` is a registered transient
194
+ // transport code, so `isRetryableCode` steers callers to retry.
195
+ throw new AbloConnectionError(`The Ablo API did not respond within ${requestTimeoutMs}ms ` +
196
+ `(${requestInit.method ?? 'GET'} ${path}). The request was aborted; ` +
197
+ 'it is safe to retry.', { code: 'wait_for_timeout', cause: error });
198
+ }
199
+ throw error;
200
+ }
201
+ finally {
202
+ if (deadline)
203
+ clearTimeout(deadline);
204
+ callerSignal?.removeEventListener('abort', onCallerAbort);
205
+ }
137
206
  const body = parseBody(bodyText);
138
207
  if (!res.ok) {
139
208
  throw translateHttpError(res.status, body ?? `Ablo API request failed: ${res.status} ${res.statusText}`, res.headers.get('x-request-id') ?? undefined);
@@ -430,11 +499,11 @@ export function createProtocolClient(options) {
430
499
  queue: claimOptions.queue,
431
500
  }),
432
501
  });
433
- // The fair-queue grant is PUSHED over a WebSocket (`claim_granted`),
434
- // which this stateless HTTP client doesn't hold. Returning a handle here
435
- // would be a phantom holder — a lease we can't confirm is ours. So a
436
- // queued response is surfaced as a typed claimed signal; callers that need
437
- // to *wait* in line use the realtime (WS-backed) `ablo.<model>.claim`.
502
+ // The fair-queue grant is pushed over a WebSocket (`claim_granted`), which
503
+ // this stateless HTTP client doesn't hold. Returning a handle here would be
504
+ // a phantom holder — a lease we can't confirm is ours. So a queued response
505
+ // is surfaced as a typed claimed signal; callers that need to wait in line
506
+ // use the realtime (WebSocket-backed) `ablo.<model>.claim`.
438
507
  if (body.status === 'queued') {
439
508
  throw new AbloClaimedError(`Target ${claimOptions.target.model}/${claimOptions.target.id} is held; ` +
440
509
  `queued at position ${body.position ?? 0}. The HTTP client can't await ` +
@@ -448,9 +517,24 @@ export function createProtocolClient(options) {
448
517
  released = true;
449
518
  await requestJson(`/v1/claims/${encodeURIComponent(id)}`, { method: 'DELETE' });
450
519
  };
520
+ // The by-id twin of the model-scoped heartbeat — same reply contract.
521
+ const heartbeat = async (beatOptions) => {
522
+ const resolved = resolveHeartbeatOptions(beatOptions);
523
+ const reply = await requestJson(`/v1/claims/${encodeURIComponent(id)}/heartbeat`, {
524
+ method: 'POST',
525
+ body: JSON.stringify({
526
+ ...(resolved.ttl !== undefined ? { ttl: resolved.ttl } : {}),
527
+ ...(resolved.details !== undefined
528
+ ? { details: resolved.details }
529
+ : {}),
530
+ }),
531
+ });
532
+ return heldHeartbeatReply(reply, `claim ${id}`);
533
+ };
451
534
  return {
452
535
  object: 'claim',
453
536
  id,
537
+ heartbeat,
454
538
  reason: claimOptions.reason,
455
539
  target: {
456
540
  type: claimOptions.target.model,
@@ -471,6 +555,23 @@ export function createProtocolClient(options) {
471
555
  waitFor(target, options) {
472
556
  return waitForNoClaims(target, options);
473
557
  },
558
+ async heartbeatAll(options) {
559
+ const reply = await requestJson('/v1/claims/heartbeat', {
560
+ method: 'POST',
561
+ body: JSON.stringify(options?.ttl !== undefined ? { ttl: options.ttl } : {}),
562
+ });
563
+ return (reply.results ?? []).flatMap((entry) => typeof entry.claimId === 'string' && typeof entry.expiresAt === 'number'
564
+ ? [
565
+ {
566
+ claimId: entry.claimId,
567
+ expiresAt: entry.expiresAt,
568
+ ...(entry.queueDepth !== undefined
569
+ ? { queueDepth: entry.queueDepth }
570
+ : {}),
571
+ },
572
+ ]
573
+ : []);
574
+ },
474
575
  };
475
576
  async function listModel(modelName, options) {
476
577
  const params = new URLSearchParams();
@@ -502,11 +603,11 @@ export function createProtocolClient(options) {
502
603
  const query = await requestJson(`/v1/models/${encodeURIComponent(modelName)}/${encodeURIComponent(params.id)}`, {
503
604
  method: 'GET',
504
605
  });
505
- // Miss = `data: undefined`, NOT a thrown error. The WebSocket client's
606
+ // A miss is `data: undefined`, not a thrown error. The WebSocket client's
506
607
  // `retrieve` returns `T | undefined` for a missing row; throwing only here
507
608
  // made the obvious read ("does this row exist?") a hard edge that an agent
508
- // had to wrap in try/catch. Both transports now agree: absent row absent
509
- // data. Callers branch on `.data` (already the documented `.data?.x` usage).
609
+ // had to wrap in try/catch. Both transports agree: an absent row means absent
610
+ // data. Callers branch on `.data` (the documented `.data?.x` usage).
510
611
  // Normalize a miss to `undefined` (the server may send `null` or omit it).
511
612
  const data = (query.data ?? undefined);
512
613
  return {
@@ -516,16 +617,16 @@ export function createProtocolClient(options) {
516
617
  };
517
618
  }
518
619
  /**
519
- * Single-op mutation over the model-scoped routes — the canonical surface
520
- * that mirrors `ablo.<model>.create/update/delete`:
620
+ * A single-operation mutation over the model-scoped routes — the canonical
621
+ * surface that mirrors `ablo.<model>.create/update/delete`:
521
622
  *
522
623
  * POST /v1/models/:model create
523
624
  * PATCH /v1/models/:model/:id update
524
625
  * DELETE /v1/models/:model/:id delete
525
626
  *
526
- * This replaces the previous indirection through `POST /v1/commits`. The raw
527
- * `commits.create(...)` resource is still the path for ATOMIC MULTI-OP
528
- * envelopes — this helper is the one-op, one-record path only.
627
+ * The `commits.create(...)` resource remains the path for atomic
628
+ * multi-operation envelopes; this helper handles the one-operation,
629
+ * one-record case.
529
630
  */
530
631
  async function mutateModel(action, modelName, id, data, options) {
531
632
  assertWriteOptions(options && {
@@ -569,9 +670,8 @@ export function createProtocolClient(options) {
569
670
  });
570
671
  }
571
672
  catch (error) {
572
- // Per-model write door (`ablo.<model>.update/create/delete`) — the path
573
- // the demo editor + server agents actually take. Capture coordination
574
- // collisions here too; this single row IS the fallback target.
673
+ // The per-model write door (`ablo.<model>.update/create/delete`). Capture
674
+ // coordination collisions here too; this single row is the fallback target.
575
675
  recordCoordinationConflict(error, clientTxId, [{ model: modelName, id }]);
576
676
  throw error;
577
677
  }
@@ -617,9 +717,25 @@ export function createProtocolClient(options) {
617
717
  throw new AbloClaimedError(`Target ${name}/${params.id} is held; queued at position ${body.position ?? 0}. ` +
618
718
  `The HTTP client cannot await the grant without a WebSocket.`, { code: 'claim_queued' });
619
719
  }
620
- return body.claim?.id ?? body.id ?? body.id ?? createClaimId();
720
+ // `claimId` is the field name the queued response uses; check it alongside
721
+ // the other id shapes the response may carry.
722
+ return body.claim?.id ?? body.id ?? body.claimId ?? createClaimId();
621
723
  };
622
724
  const releaseClaim = (params) => requestJson(claimPath(isClaimHandle(params) ? params.target.id : params.id), { method: 'DELETE' }).then(() => undefined);
725
+ // One beat on the held lease. A lapsed lease answers `claim_lost`
726
+ // (409), which the wire error mapping surfaces as AbloClaimedError —
727
+ // the definitive signal that stops the auto-heartbeat loop.
728
+ const heartbeatClaim = async (id, claimId, options) => {
729
+ const reply = await requestJson(`${claimPath(id)}/heartbeat`, {
730
+ method: 'POST',
731
+ body: JSON.stringify({
732
+ claimId,
733
+ ...(options.ttl !== undefined ? { ttl: options.ttl } : {}),
734
+ ...(options.details !== undefined ? { details: options.details } : {}),
735
+ }),
736
+ });
737
+ return heldHeartbeatReply(reply, `claim ${claimId} on ${name}/${id}`);
738
+ };
623
739
  async function claimImpl(params) {
624
740
  const claimId = await acquireClaim(params);
625
741
  observability?.captureClaim({
@@ -637,7 +753,33 @@ export function createProtocolClient(options) {
637
753
  if (data === undefined) {
638
754
  throw new AbloNotFoundError(`Cannot claim ${name}/${params.id}: it does not exist (or is outside this credential's scope).`, [params.id]);
639
755
  }
640
- const release = () => releaseClaim(params);
756
+ const heartbeat = async (beatOptions) => {
757
+ const resolved = resolveHeartbeatOptions(beatOptions);
758
+ const beat = await heartbeatClaim(params.id, claimId, {
759
+ ttl: resolved.ttl ?? params.ttl,
760
+ ...(resolved.details !== undefined
761
+ ? { details: resolved.details }
762
+ : {}),
763
+ });
764
+ params.onHeartbeat?.(beat);
765
+ return beat;
766
+ };
767
+ // Opt-in auto-heartbeat — the background-worker cadence. The stateless
768
+ // HTTP claim defaults to the server's 60s acquire window when no TTL
769
+ // was requested, so the default cadence lands at 20s beats.
770
+ const stopHeartbeatLoop = params.heartbeat
771
+ ? startClaimHeartbeatLoop({
772
+ beat: () => heartbeat(),
773
+ intervalMs: heartbeatCadenceMs(params.ttl !== undefined ? toMs(params.ttl) : 60_000, params.heartbeat),
774
+ ...(params.onHeartbeatLost
775
+ ? { onLost: params.onHeartbeatLost }
776
+ : {}),
777
+ })
778
+ : undefined;
779
+ const release = () => {
780
+ stopHeartbeatLoop?.();
781
+ return releaseClaim(params);
782
+ };
641
783
  return {
642
784
  object: 'claim',
643
785
  id: claimId,
@@ -657,6 +799,7 @@ export function createProtocolClient(options) {
657
799
  revoke: () => {
658
800
  void release().catch(() => { });
659
801
  },
802
+ heartbeat,
660
803
  [Symbol.asyncDispose]: release,
661
804
  };
662
805
  }
@@ -675,8 +818,8 @@ export function createProtocolClient(options) {
675
818
  reorder: async (params) => {
676
819
  await requestJson(`${claimPath(params.id)}/reorder`, {
677
820
  method: 'POST',
678
- // The reorder route's payload is `{ heldBy, claimId }[]` — Claim's id
679
- // IS the claimId.
821
+ // The reorder route's payload is `{ heldBy, claimId }[]` — a Claim's id
822
+ // is the claimId.
680
823
  body: JSON.stringify({ order: params.order.map((i) => ({ heldBy: i.heldBy, claimId: i.id })) }),
681
824
  });
682
825
  },
@@ -688,7 +831,13 @@ export function createProtocolClient(options) {
688
831
  if (isClaimHandle(claimInput)) {
689
832
  return run({ ...input, claimRef: { id: claimInput.id }, claim: undefined });
690
833
  }
691
- const claimId = await acquireClaim({ id, ...claimInput });
834
+ // `isClaimHandle` ruled out the handle form above; the generic mismatch
835
+ // (the union carries `Claim`, the guard narrows `Claim<T>`) keeps the
836
+ // compiler from subtracting it, so narrow to the inline-options form.
837
+ const claimId = await acquireClaim({
838
+ id,
839
+ ...claimInput,
840
+ });
692
841
  try {
693
842
  return await run({ ...input, claimRef: { id: claimId }, claim: undefined });
694
843
  }
@@ -742,8 +891,8 @@ export function createProtocolClient(options) {
742
891
  // Confirm the write, then return the row — the obvious expectation of
743
892
  // "create" (the WebSocket client already returns the row). The read-
744
893
  // back is the authoritative server row, so it carries the framework
745
- // defaults (createdAt/createdBy/…) AND, for an idempotent re-create of
746
- // an existing id, the EXISTING row rather than the caller's input.
894
+ // defaults (createdAt, createdBy, …) and, for an idempotent re-create of
895
+ // an existing id, the existing row rather than the caller's input.
747
896
  await mutateModel('create', name, id, params.data, {
748
897
  ...options,
749
898
  wait: options?.wait ?? 'confirmed',
@@ -775,9 +924,9 @@ export function createProtocolClient(options) {
775
924
  model,
776
925
  sessions: {
777
926
  async create(params) {
778
- // Stateless mint: the configured key IS the control-plane credential here
779
- // (no startup `rk_` exchange runs on this client). Reuse the resolved base
780
- // URL + fetch; the shared `mintSession` owns the two server doors.
927
+ // Stateless mint: the configured key is the control-plane credential here
928
+ // (no startup `rk_` exchange runs on this client). It reuses the resolved
929
+ // base URL and fetch; the shared `mintSession` handles the two server routes.
781
930
  const apiKey = await resolveApiKeyValue(configuredApiKey);
782
931
  if (!apiKey) {
783
932
  throw new AbloAuthenticationError('sessions.create requires a secret (sk_) API key — call it from your backend, not the browser.', { code: 'apikey_missing' });