@abloatai/ablo 0.34.1 → 0.36.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 (536) hide show
  1. package/AGENTS.md +2 -1
  2. package/CHANGELOG.md +758 -5
  3. package/README.md +56 -502
  4. package/bin/ablo.cjs +39 -0
  5. package/dist/BaseSyncedStore.d.ts +176 -48
  6. package/dist/BaseSyncedStore.js +346 -214
  7. package/dist/Database.d.ts +17 -44
  8. package/dist/Database.js +96 -79
  9. package/dist/InstanceCache.d.ts +31 -6
  10. package/dist/InstanceCache.js +65 -30
  11. package/dist/LazyReferenceCollection.d.ts +3 -3
  12. package/dist/LazyReferenceCollection.js +4 -4
  13. package/dist/Model.d.ts +23 -13
  14. package/dist/Model.js +27 -17
  15. package/dist/ModelRegistry.d.ts +8 -4
  16. package/dist/ModelRegistry.js +20 -18
  17. package/dist/NetworkMonitor.d.ts +3 -1
  18. package/dist/NetworkMonitor.js +7 -5
  19. package/dist/{SyncEngineContext.d.ts → RuntimeContext.d.ts} +20 -13
  20. package/dist/{SyncEngineContext.js → RuntimeContext.js} +11 -12
  21. package/dist/SyncClient.d.ts +47 -47
  22. package/dist/SyncClient.js +215 -156
  23. package/dist/ai-sdk/coordinatedTool.d.ts +2 -2
  24. package/dist/ai-sdk/coordinatedTool.js +1 -1
  25. package/dist/ai-sdk/coordinationContext.d.ts +2 -2
  26. package/dist/ai-sdk/coordinationContext.js +1 -1
  27. package/dist/ai-sdk/wrap.d.ts +3 -3
  28. package/dist/ai-sdk/wrap.js +2 -2
  29. package/dist/auth/index.d.ts +1 -156
  30. package/dist/auth/index.js +8 -301
  31. package/dist/client/Ablo.d.ts +42 -287
  32. package/dist/client/Ablo.js +129 -963
  33. package/dist/client/abloClient.d.ts +309 -0
  34. package/dist/client/abloClient.js +13 -0
  35. package/dist/client/clientPrelude.d.ts +52 -0
  36. package/dist/client/clientPrelude.js +60 -0
  37. package/dist/client/consoleLogger.d.ts +2 -2
  38. package/dist/client/coreClient.d.ts +60 -0
  39. package/dist/client/coreClient.js +118 -0
  40. package/dist/client/createInternalComponents.d.ts +8 -4
  41. package/dist/client/createInternalComponents.js +17 -10
  42. package/dist/client/createModelProxy.d.ts +98 -373
  43. package/dist/client/createModelProxy.js +233 -139
  44. package/dist/client/humans.d.ts +69 -0
  45. package/dist/client/humans.js +78 -0
  46. package/dist/client/modelRegistration.d.ts +1 -1
  47. package/dist/client/modelRegistration.js +9 -9
  48. package/dist/client/options.d.ts +73 -17
  49. package/dist/client/reactiveEngine.d.ts +53 -0
  50. package/dist/client/reactiveEngine.js +688 -0
  51. package/dist/client/resourceTypes.d.ts +9 -250
  52. package/dist/client/resourceTypes.js +8 -5
  53. package/dist/client/schemaConfig.d.ts +4 -4
  54. package/dist/client/schemaConfig.js +6 -2
  55. package/dist/client/storeCluster.d.ts +47 -0
  56. package/dist/client/storeCluster.js +118 -0
  57. package/dist/client/storeLifecycle.d.ts +61 -0
  58. package/dist/client/storeLifecycle.js +231 -0
  59. package/dist/client/validateAbloOptions.d.ts +3 -2
  60. package/dist/client/validateAbloOptions.js +1 -1
  61. package/dist/client/wsMutationExecutor.d.ts +3 -3
  62. package/dist/client/wsMutationExecutor.js +3 -3
  63. package/dist/context.d.ts +22 -9
  64. package/dist/context.js +33 -9
  65. package/dist/coordination/ClaimLog.d.ts +26 -0
  66. package/dist/coordination/ClaimLog.js +32 -0
  67. package/dist/coordination/index.d.ts +1 -15
  68. package/dist/coordination/index.js +8 -31
  69. package/dist/core/index.d.ts +3 -3
  70. package/dist/core/index.js +2 -2
  71. package/dist/docs/catalog.d.ts +72 -0
  72. package/dist/docs/catalog.js +230 -0
  73. package/dist/docs/index.d.ts +10 -0
  74. package/dist/docs/index.js +10 -0
  75. package/dist/environment.d.ts +1 -40
  76. package/dist/environment.js +8 -37
  77. package/dist/index.d.ts +44 -36
  78. package/dist/index.js +30 -22
  79. package/dist/interfaces/index.d.ts +44 -134
  80. package/dist/keys/index.d.ts +1 -77
  81. package/dist/keys/index.js +8 -190
  82. package/dist/mutators/{RecordingTransaction.d.ts → RecordingMutation.d.ts} +4 -4
  83. package/dist/mutators/{RecordingTransaction.js → RecordingMutation.js} +2 -2
  84. package/dist/mutators/Transaction.d.ts +1 -1
  85. package/dist/mutators/Transaction.js +1 -1
  86. package/dist/mutators/UndoManager.d.ts +6 -6
  87. package/dist/mutators/UndoManager.js +5 -5
  88. package/dist/mutators/defineMutators.d.ts +3 -3
  89. package/dist/mutators/defineMutators.js +1 -1
  90. package/dist/mutators/inverseOp.js +2 -2
  91. package/dist/mutators/mutateActions.d.ts +3 -3
  92. package/dist/mutators/mutateActions.js +1 -1
  93. package/dist/mutators/readerActions.d.ts +1 -1
  94. package/dist/mutators/undoApply.d.ts +1 -1
  95. package/dist/mutators/undoApply.js +1 -1
  96. package/dist/policy/index.d.ts +2 -2
  97. package/dist/policy/index.js +1 -1
  98. package/dist/query/client.d.ts +5 -2
  99. package/dist/query/client.js +10 -9
  100. package/dist/query/types.d.ts +6 -41
  101. package/dist/query/types.js +2 -2
  102. package/dist/react/AbloProvider.d.ts +18 -8
  103. package/dist/react/AbloProvider.js +10 -9
  104. package/dist/react/context.d.ts +3 -3
  105. package/dist/react/context.js +1 -1
  106. package/dist/react/createAbloReact.d.ts +56 -0
  107. package/dist/react/createAbloReact.js +51 -0
  108. package/dist/react/index.d.ts +6 -5
  109. package/dist/react/index.js +6 -3
  110. package/dist/react/internalContext.d.ts +1 -1
  111. package/dist/react/useAblo.d.ts +12 -5
  112. package/dist/react/useAblo.js +26 -8
  113. package/dist/react/useCurrentUserId.js +1 -1
  114. package/dist/react/useErrorListener.js +1 -1
  115. package/dist/react/useMutationFailureListener.d.ts +2 -2
  116. package/dist/react/useMutationFailureListener.js +1 -1
  117. package/dist/react/useMutators.d.ts +3 -3
  118. package/dist/react/useMutators.js +3 -3
  119. package/dist/react/useUndoScope.d.ts +5 -5
  120. package/dist/react/useUndoScope.js +1 -1
  121. package/dist/schema/coordination.d.ts +69 -10
  122. package/dist/schema/coordination.js +90 -9
  123. package/dist/schema/ddl.js +2 -2
  124. package/dist/schema/diff.d.ts +1 -1
  125. package/dist/schema/generate.js +1 -1
  126. package/dist/schema/index.d.ts +11 -10
  127. package/dist/schema/index.js +22 -18
  128. package/dist/schema/queries.d.ts +27 -27
  129. package/dist/schema/queries.js +23 -23
  130. package/dist/schema/select.d.ts +3 -3
  131. package/dist/schema/select.js +6 -3
  132. package/dist/schema/serialize.d.ts +15 -6
  133. package/dist/schema/serialize.js +20 -3
  134. package/dist/schema/sugar.d.ts +6 -7
  135. package/dist/schema/sugar.js +9 -12
  136. package/dist/schema/syncDeltaRow.d.ts +4 -152
  137. package/dist/schema/syncDeltaRow.js +4 -105
  138. package/dist/server/adapter.d.ts +18 -1
  139. package/dist/server/commit.d.ts +10 -16
  140. package/dist/server/index.d.ts +1 -1
  141. package/dist/server/index.js +1 -1
  142. package/dist/server/readConfig.d.ts +1 -1
  143. package/dist/source/adapter.d.ts +7 -5
  144. package/dist/source/adapter.js +7 -5
  145. package/dist/source/adapters/drizzle.d.ts +1 -1
  146. package/dist/source/adapters/drizzle.js +2 -2
  147. package/dist/source/adapters/kysely.d.ts +1 -1
  148. package/dist/source/adapters/kysely.js +1 -1
  149. package/dist/source/adapters/kyselyMutationCore.d.ts +1 -1
  150. package/dist/source/adapters/kyselyMutationCore.js +2 -2
  151. package/dist/source/adapters/memory.js +1 -1
  152. package/dist/source/adapters/prisma.d.ts +8 -3
  153. package/dist/source/adapters/prisma.js +1 -1
  154. package/dist/source/connector.js +1 -1
  155. package/dist/source/connectorProtocol.d.ts +2 -8
  156. package/dist/source/connectorProtocol.js +3 -2
  157. package/dist/source/contract.d.ts +29 -17
  158. package/dist/source/contract.js +27 -22
  159. package/dist/source/factory.d.ts +1 -1
  160. package/dist/source/idempotency.js +2 -2
  161. package/dist/source/index.d.ts +1 -0
  162. package/dist/source/index.js +3 -0
  163. package/dist/source/next.d.ts +1 -1
  164. package/dist/source/signing.d.ts +9 -2
  165. package/dist/source/signing.js +4 -1
  166. package/dist/source/types.d.ts +6 -4
  167. package/dist/source/types.js +1 -1
  168. package/dist/{core/storeContract.d.ts → storeContract.d.ts} +6 -6
  169. package/dist/{core → stores}/DatabaseManager.d.ts +3 -1
  170. package/dist/{core → stores}/DatabaseManager.js +14 -13
  171. package/dist/stores/ObjectStore.d.ts +1 -1
  172. package/dist/{core → stores}/StoreManager.d.ts +9 -26
  173. package/dist/{core → stores}/StoreManager.js +29 -77
  174. package/dist/stores/SyncActionStore.d.ts +4 -2
  175. package/dist/stores/SyncActionStore.js +11 -17
  176. package/dist/stores/syncAction.d.ts +26 -0
  177. package/dist/stores/syncAction.js +16 -0
  178. package/dist/surface.d.ts +3 -3
  179. package/dist/surface.js +6 -4
  180. package/dist/sync/BootstrapFetcher.d.ts +127 -6
  181. package/dist/sync/BootstrapFetcher.js +511 -83
  182. package/dist/sync/ConnectionManager.d.ts +6 -198
  183. package/dist/sync/ConnectionManager.js +6 -677
  184. package/dist/sync/OnDemandLoader.d.ts +5 -2
  185. package/dist/sync/OnDemandLoader.js +61 -21
  186. package/dist/sync/SubscriptionManager.d.ts +13 -2
  187. package/dist/sync/SubscriptionManager.js +23 -5
  188. package/dist/sync/SyncWebSocket.d.ts +27 -510
  189. package/dist/sync/SyncWebSocket.js +76 -954
  190. package/dist/sync/awaitClaimGrant.d.ts +4 -44
  191. package/dist/sync/awaitClaimGrant.js +4 -109
  192. package/dist/sync/bootstrapApply.d.ts +3 -0
  193. package/dist/sync/bootstrapApply.js +2 -2
  194. package/dist/sync/commitFrames.d.ts +6 -40
  195. package/dist/sync/commitFrames.js +6 -97
  196. package/dist/sync/contextPorts.d.ts +18 -0
  197. package/dist/sync/contextPorts.js +31 -0
  198. package/dist/sync/createClaimStream.d.ts +5 -49
  199. package/dist/sync/createClaimStream.js +5 -469
  200. package/dist/sync/createPresenceStream.d.ts +26 -4
  201. package/dist/sync/createPresenceStream.js +28 -20
  202. package/dist/sync/createSnapshot.d.ts +2 -2
  203. package/dist/sync/createSnapshot.js +1 -1
  204. package/dist/sync/credentialLifecycle.d.ts +5 -173
  205. package/dist/sync/credentialLifecycle.js +5 -320
  206. package/dist/sync/deltaPipeline.d.ts +13 -12
  207. package/dist/sync/deltaPipeline.js +21 -4
  208. package/dist/sync/groupChange.d.ts +3 -0
  209. package/dist/sync/groupChange.js +16 -14
  210. package/dist/sync/participants.d.ts +24 -6
  211. package/dist/sync/participants.js +32 -23
  212. package/dist/sync/schemaDrift.d.ts +55 -0
  213. package/dist/sync/schemaDrift.js +53 -0
  214. package/dist/sync/schemas.d.ts +23 -33
  215. package/dist/sync/schemas.js +29 -20
  216. package/dist/sync/syncPlan.d.ts +3 -3
  217. package/dist/sync/wsFrameHandlers.d.ts +6 -114
  218. package/dist/sync/wsFrameHandlers.js +6 -392
  219. package/dist/syncLog/contract.d.ts +20 -0
  220. package/dist/syncLog/contract.js +19 -0
  221. package/dist/syncLog/index.d.ts +1 -0
  222. package/dist/syncLog/index.js +1 -0
  223. package/dist/transaction/ablo.d.ts +88 -0
  224. package/dist/transaction/ablo.js +33 -0
  225. package/dist/{client/auth.d.ts → transaction/auth/apiKey.d.ts} +43 -8
  226. package/dist/{client/auth.js → transaction/auth/apiKey.js} +19 -0
  227. package/dist/transaction/auth/bootstrapScope.d.ts +15 -0
  228. package/dist/transaction/auth/bootstrapScope.js +1 -0
  229. package/dist/transaction/auth/capability.d.ts +212 -0
  230. package/dist/transaction/auth/capability.js +224 -0
  231. package/dist/{auth → transaction/auth}/credentialSource.d.ts +8 -1
  232. package/dist/{client → transaction/auth}/identity.d.ts +8 -7
  233. package/dist/{client → transaction/auth}/identity.js +1 -1
  234. package/dist/transaction/auth/index.d.ts +162 -0
  235. package/dist/transaction/auth/index.js +304 -0
  236. package/dist/{auth → transaction/auth}/schemas.d.ts +1 -1
  237. package/dist/{auth → transaction/auth}/schemas.js +13 -13
  238. package/dist/{client → transaction/auth}/sessionMint.d.ts +8 -8
  239. package/dist/{client → transaction/auth}/sessionMint.js +4 -7
  240. package/dist/transaction/coordination/awaitClaimGrant.d.ts +56 -0
  241. package/dist/transaction/coordination/awaitClaimGrant.js +124 -0
  242. package/dist/{client → transaction/coordination}/claimHeartbeatLoop.d.ts +34 -0
  243. package/dist/{client → transaction/coordination}/claimHeartbeatLoop.js +20 -0
  244. package/dist/transaction/coordination/claimMeta.d.ts +49 -0
  245. package/dist/transaction/coordination/claimMeta.js +52 -0
  246. package/dist/transaction/coordination/createClaimStream.d.ts +64 -0
  247. package/dist/transaction/coordination/createClaimStream.js +475 -0
  248. package/dist/transaction/coordination/events.d.ts +74 -0
  249. package/dist/transaction/coordination/events.js +7 -0
  250. package/dist/transaction/coordination/index.d.ts +19 -0
  251. package/dist/transaction/coordination/index.js +45 -0
  252. package/dist/transaction/coordination/locator.d.ts +104 -0
  253. package/dist/transaction/coordination/locator.js +102 -0
  254. package/dist/transaction/coordination/schema.d.ts +1536 -0
  255. package/dist/transaction/coordination/schema.js +1177 -0
  256. package/dist/transaction/coordination/targetConflict.d.ts +2 -0
  257. package/dist/transaction/coordination/targetConflict.js +107 -0
  258. package/dist/{coordination → transaction/coordination}/trace.d.ts +7 -18
  259. package/dist/{coordination → transaction/coordination}/trace.js +18 -25
  260. package/dist/transaction/durableWrites.d.ts +62 -0
  261. package/dist/{client → transaction}/durableWrites.js +28 -3
  262. package/dist/transaction/environment.d.ts +105 -0
  263. package/dist/transaction/environment.js +108 -0
  264. package/dist/{errorCodes.d.ts → transaction/errorCodes.d.ts} +12 -12
  265. package/dist/{errorCodes.js → transaction/errorCodes.js} +45 -18
  266. package/dist/{errors.d.ts → transaction/errors.d.ts} +37 -13
  267. package/dist/{errors.js → transaction/errors.js} +85 -16
  268. package/dist/transaction/footprint.d.ts +111 -0
  269. package/dist/transaction/footprint.js +0 -0
  270. package/dist/transaction/index.d.ts +20 -0
  271. package/dist/transaction/index.js +20 -0
  272. package/dist/transaction/keys/index.d.ts +87 -0
  273. package/dist/transaction/keys/index.js +207 -0
  274. package/dist/transaction/log/syncDeltaRow.d.ts +158 -0
  275. package/dist/transaction/log/syncDeltaRow.js +95 -0
  276. package/dist/{sync/syncPosition.d.ts → transaction/logPosition.d.ts} +22 -8
  277. package/dist/{sync/syncPosition.js → transaction/logPosition.js} +15 -6
  278. package/dist/transaction/logger.d.ts +16 -0
  279. package/dist/transaction/logger.js +7 -0
  280. package/dist/transaction/observability.d.ts +53 -0
  281. package/dist/transaction/observability.js +19 -0
  282. package/dist/transaction/plugin.d.ts +285 -0
  283. package/dist/transaction/plugin.js +106 -0
  284. package/dist/{policy → transaction/policy}/types.d.ts +3 -3
  285. package/dist/{policy → transaction/policy}/types.js +2 -0
  286. package/dist/transaction/resources/httpResources.d.ts +321 -0
  287. package/dist/transaction/resources/httpResources.js +7 -0
  288. package/dist/transaction/resources/modelOperations.d.ts +427 -0
  289. package/dist/transaction/resources/modelOperations.js +12 -0
  290. package/dist/transaction/resources/mutationOptions.d.ts +66 -0
  291. package/dist/transaction/resources/mutationOptions.js +9 -0
  292. package/dist/transaction/resources/where.d.ts +101 -0
  293. package/dist/transaction/resources/where.js +115 -0
  294. package/dist/{client → transaction/resources}/writeOptionsSchema.d.ts +2 -6
  295. package/dist/{client → transaction/resources}/writeOptionsSchema.js +9 -5
  296. package/dist/{schema → transaction/schema}/field.d.ts +17 -23
  297. package/dist/{schema → transaction/schema}/field.js +5 -5
  298. package/dist/transaction/schema/fieldRef.d.ts +38 -0
  299. package/dist/transaction/schema/fieldRef.js +11 -0
  300. package/dist/transaction/schema/loadStrategy.d.ts +45 -0
  301. package/dist/transaction/schema/loadStrategy.js +46 -0
  302. package/dist/{schema → transaction/schema}/model.d.ts +50 -35
  303. package/dist/{schema → transaction/schema}/model.js +30 -20
  304. package/dist/transaction/schema/openapi.d.ts +58 -0
  305. package/dist/transaction/schema/openapi.js +501 -0
  306. package/dist/{schema → transaction/schema}/relation.d.ts +21 -16
  307. package/dist/{schema → transaction/schema}/relation.js +7 -7
  308. package/dist/{schema → transaction/schema}/residency.d.ts +0 -9
  309. package/dist/{schema → transaction/schema}/residency.js +0 -5
  310. package/dist/{schema → transaction/schema}/roles.d.ts +5 -5
  311. package/dist/{schema → transaction/schema}/roles.js +5 -5
  312. package/dist/{schema → transaction/schema}/schema.d.ts +39 -10
  313. package/dist/{schema → transaction/schema}/schema.js +24 -3
  314. package/dist/{schema → transaction/schema}/tenancy.d.ts +1 -1
  315. package/dist/{schema → transaction/schema}/tenancy.js +7 -4
  316. package/dist/transaction/transactionLayer.d.ts +82 -0
  317. package/dist/transaction/transactionLayer.js +24 -0
  318. package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.d.ts +5 -6
  319. package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.js +9 -13
  320. package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.d.ts +9 -2
  321. package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.js +5 -9
  322. package/dist/{transactions/durableWriteStore.d.ts → transaction/transactions/settlement/pendingWrite.d.ts} +11 -37
  323. package/dist/transaction/transactions/settlement/pendingWrite.js +20 -0
  324. package/dist/transaction/transport/commitFrames.d.ts +90 -0
  325. package/dist/transaction/transport/commitFrames.js +134 -0
  326. package/dist/transaction/transport/connectionManager.d.ts +215 -0
  327. package/dist/transaction/transport/connectionManager.js +673 -0
  328. package/dist/transaction/transport/credentialLifecycle.d.ts +177 -0
  329. package/dist/transaction/transport/credentialLifecycle.js +324 -0
  330. package/dist/{sync → transaction/transport}/heartbeat.d.ts +3 -1
  331. package/dist/{sync → transaction/transport}/heartbeat.js +6 -4
  332. package/dist/transaction/transport/httpClient.d.ts +131 -0
  333. package/dist/{client → transaction/transport}/httpClient.js +6 -5
  334. package/dist/transaction/transport/httpOptions.d.ts +33 -0
  335. package/dist/transaction/transport/httpOptions.js +12 -0
  336. package/dist/{client → transaction/transport}/httpTransport.js +295 -97
  337. package/dist/{sync/NetworkProbe.d.ts → transaction/transport/networkProbe.d.ts} +7 -4
  338. package/dist/{sync/NetworkProbe.js → transaction/transport/networkProbe.js} +14 -13
  339. package/dist/transaction/transport/wsFrameHandlers.d.ts +128 -0
  340. package/dist/transaction/transport/wsFrameHandlers.js +429 -0
  341. package/dist/transaction/transport/wsTransport.d.ts +574 -0
  342. package/dist/transaction/transport/wsTransport.js +1023 -0
  343. package/dist/transaction/types/assertExact.d.ts +17 -0
  344. package/dist/transaction/types/assertExact.js +1 -0
  345. package/dist/{types → transaction/types}/global.d.ts +17 -2
  346. package/dist/{types → transaction/types}/global.js +2 -1
  347. package/dist/{types → transaction/types}/index.d.ts +14 -46
  348. package/dist/{types → transaction/types}/index.js +7 -16
  349. package/dist/{types → transaction/types}/streams.d.ts +73 -45
  350. package/dist/transaction/utils/duration.d.ts +50 -0
  351. package/dist/{utils → transaction/utils}/duration.js +32 -0
  352. package/dist/{utils → transaction/utils}/json.d.ts +18 -0
  353. package/dist/transaction/utils/json.js +276 -0
  354. package/dist/transaction/wire/accountResponses.d.ts +420 -0
  355. package/dist/transaction/wire/accountResponses.js +290 -0
  356. package/dist/transaction/wire/auth.d.ts +56 -0
  357. package/dist/transaction/wire/auth.js +63 -0
  358. package/dist/transaction/wire/claimEvent.d.ts +76 -0
  359. package/dist/transaction/wire/claimEvent.js +73 -0
  360. package/dist/transaction/wire/claims.d.ts +530 -0
  361. package/dist/transaction/wire/claims.js +327 -0
  362. package/dist/{wire → transaction/wire}/commit.d.ts +125 -193
  363. package/dist/{wire → transaction/wire}/commit.js +68 -47
  364. package/dist/{wire → transaction/wire}/delta.d.ts +66 -17
  365. package/dist/{wire → transaction/wire}/delta.js +37 -13
  366. package/dist/transaction/wire/errorEnvelope.d.ts +72 -0
  367. package/dist/{wire → transaction/wire}/errorEnvelope.js +36 -5
  368. package/dist/transaction/wire/feedCursor.d.ts +60 -0
  369. package/dist/transaction/wire/feedCursor.js +82 -0
  370. package/dist/transaction/wire/feedEvent.d.ts +204 -0
  371. package/dist/transaction/wire/feedEvent.js +65 -0
  372. package/dist/transaction/wire/frames.d.ts +194 -0
  373. package/dist/transaction/wire/frames.js +50 -0
  374. package/dist/transaction/wire/inboundFrames.d.ts +562 -0
  375. package/dist/transaction/wire/inboundFrames.js +116 -0
  376. package/dist/transaction/wire/index.d.ts +54 -0
  377. package/dist/transaction/wire/index.js +83 -0
  378. package/dist/{wire → transaction/wire}/listEnvelope.d.ts +13 -14
  379. package/dist/transaction/wire/listEnvelope.js +42 -0
  380. package/dist/transaction/wire/modelMutations.d.ts +31 -0
  381. package/dist/transaction/wire/modelMutations.js +52 -0
  382. package/dist/transaction/wire/modelResponses.d.ts +85 -0
  383. package/dist/transaction/wire/modelResponses.js +43 -0
  384. package/dist/transaction/wire/modelShape.d.ts +78 -0
  385. package/dist/transaction/wire/modelShape.js +74 -0
  386. package/dist/transactions/{TransactionQueue.d.ts → mutations/MutationQueue.d.ts} +85 -38
  387. package/dist/transactions/{TransactionQueue.js → mutations/MutationQueue.js} +141 -80
  388. package/dist/transactions/{TransactionStore.d.ts → mutations/MutationStore.d.ts} +8 -8
  389. package/dist/transactions/{TransactionStore.js → mutations/MutationStore.js} +2 -2
  390. package/dist/transactions/{coalesceRules.d.ts → mutations/coalesceRules.d.ts} +10 -10
  391. package/dist/transactions/{coalesceRules.js → mutations/coalesceRules.js} +1 -1
  392. package/dist/transactions/mutations/commitLatency.d.ts +52 -0
  393. package/dist/transactions/mutations/commitLatency.js +130 -0
  394. package/dist/transactions/{commitOutboxStore.d.ts → mutations/commitOutboxStore.d.ts} +1 -5
  395. package/dist/transactions/{commitOutboxStore.js → mutations/commitOutboxStore.js} +1 -1
  396. package/dist/transactions/{commitPayload.d.ts → mutations/commitPayload.d.ts} +18 -16
  397. package/dist/transactions/{commitPayload.js → mutations/commitPayload.js} +15 -15
  398. package/dist/transactions/{deltaConfirmation.d.ts → mutations/deltaConfirmation.d.ts} +15 -11
  399. package/dist/transactions/{deltaConfirmation.js → mutations/deltaConfirmation.js} +14 -12
  400. package/dist/transactions/mutations/durableWriteStore.d.ts +14 -0
  401. package/dist/transactions/mutations/durableWriteStore.js +12 -0
  402. package/dist/transactions/{optimisticApply.d.ts → mutations/optimisticApply.d.ts} +7 -7
  403. package/dist/transactions/{replayValidation.d.ts → mutations/replayValidation.d.ts} +4 -3
  404. package/dist/transactions/{replayValidation.js → mutations/replayValidation.js} +7 -5
  405. package/dist/utils/mobxSetup.d.ts +1 -1
  406. package/dist/utils/mobxSetup.js +5 -2
  407. package/dist/{core → views}/QueryView.d.ts +2 -2
  408. package/dist/{core → views}/QueryView.js +2 -2
  409. package/dist/{core → views}/ViewRegistry.d.ts +1 -1
  410. package/dist/{core/queryUtils.d.ts → views/incrementalView.d.ts} +6 -6
  411. package/dist/{core/queryUtils.js → views/incrementalView.js} +6 -6
  412. package/dist/webhooks/events.d.ts +2 -2
  413. package/dist/wire/index.d.ts +1 -34
  414. package/dist/wire/index.js +8 -49
  415. package/docs/agent-messaging.md +3 -3
  416. package/docs/agents.md +20 -13
  417. package/docs/api-keys.md +14 -10
  418. package/docs/api.md +27 -61
  419. package/docs/audit.md +6 -3
  420. package/docs/cli.md +41 -13
  421. package/docs/client-behavior.md +11 -9
  422. package/docs/concurrency-convention.md +49 -57
  423. package/docs/coordination.md +283 -121
  424. package/docs/data-sources.md +7 -5
  425. package/docs/debugging.md +39 -15
  426. package/docs/deployment.md +267 -0
  427. package/docs/examples/agent-human.md +49 -42
  428. package/docs/examples/ai-sdk-tool.md +69 -44
  429. package/docs/examples/existing-python-backend.md +8 -6
  430. package/docs/examples/nextjs.md +129 -47
  431. package/docs/examples/scoped-agent.md +46 -45
  432. package/docs/examples/server-agent.md +46 -26
  433. package/docs/groups.md +87 -30
  434. package/docs/guarantees.md +41 -12
  435. package/docs/how-it-works.md +38 -12
  436. package/docs/idempotency.md +126 -0
  437. package/docs/identity.md +77 -74
  438. package/docs/index.md +172 -86
  439. package/docs/integration-guide.md +31 -19
  440. package/docs/mcp.md +46 -21
  441. package/docs/migration.md +95 -18
  442. package/docs/operating-on-your-database.md +3 -1
  443. package/docs/projects.md +3 -1
  444. package/docs/quickstart.md +22 -5
  445. package/docs/react.md +31 -18
  446. package/docs/schema-contract.md +5 -3
  447. package/docs/session-settings.md +108 -0
  448. package/docs/sessions.md +4 -2
  449. package/docs/webhooks.md +12 -10
  450. package/llms.txt +48 -18
  451. package/package.json +21 -26
  452. package/dist/agent/Agent.d.ts +0 -366
  453. package/dist/agent/Agent.js +0 -514
  454. package/dist/agent/index.d.ts +0 -115
  455. package/dist/agent/index.js +0 -128
  456. package/dist/agent/session.d.ts +0 -93
  457. package/dist/agent/session.js +0 -149
  458. package/dist/agent/types.d.ts +0 -68
  459. package/dist/agent/types.js +0 -9
  460. package/dist/cli.cjs +0 -286329
  461. package/dist/client/durableWrites.d.ts +0 -21
  462. package/dist/client/httpClient.d.ts +0 -80
  463. package/dist/coordination/schema.d.ts +0 -722
  464. package/dist/coordination/schema.js +0 -578
  465. package/dist/schema/openapi.d.ts +0 -29
  466. package/dist/schema/openapi.js +0 -124
  467. package/dist/testing/fixtures/bootstrap.d.ts +0 -49
  468. package/dist/testing/fixtures/bootstrap.js +0 -59
  469. package/dist/testing/fixtures/deltas.d.ts +0 -83
  470. package/dist/testing/fixtures/deltas.js +0 -136
  471. package/dist/testing/fixtures/models.d.ts +0 -83
  472. package/dist/testing/fixtures/models.js +0 -272
  473. package/dist/testing/helpers/reactWrapper.d.ts +0 -69
  474. package/dist/testing/helpers/reactWrapper.js +0 -67
  475. package/dist/testing/helpers/syncEngineHarness.d.ts +0 -54
  476. package/dist/testing/helpers/syncEngineHarness.js +0 -73
  477. package/dist/testing/helpers/wait.d.ts +0 -30
  478. package/dist/testing/helpers/wait.js +0 -49
  479. package/dist/testing/index.d.ts +0 -23
  480. package/dist/testing/index.js +0 -33
  481. package/dist/testing/mocks/FakeDatabase.d.ts +0 -18
  482. package/dist/testing/mocks/FakeDatabase.js +0 -10
  483. package/dist/testing/mocks/MockMutationExecutor.d.ts +0 -87
  484. package/dist/testing/mocks/MockMutationExecutor.js +0 -192
  485. package/dist/testing/mocks/MockNetworkMonitor.d.ts +0 -20
  486. package/dist/testing/mocks/MockNetworkMonitor.js +0 -46
  487. package/dist/testing/mocks/MockSyncContext.d.ts +0 -51
  488. package/dist/testing/mocks/MockSyncContext.js +0 -71
  489. package/dist/testing/mocks/MockSyncStore.d.ts +0 -88
  490. package/dist/testing/mocks/MockSyncStore.js +0 -171
  491. package/dist/testing/mocks/MockWebSocket.d.ts +0 -71
  492. package/dist/testing/mocks/MockWebSocket.js +0 -118
  493. package/dist/transactions/durableWriteStore.js +0 -30
  494. package/dist/utils/duration.d.ts +0 -25
  495. package/dist/utils/json.js +0 -88
  496. package/dist/wire/errorEnvelope.d.ts +0 -55
  497. package/dist/wire/frames.d.ts +0 -197
  498. package/dist/wire/frames.js +0 -49
  499. package/dist/wire/listEnvelope.js +0 -18
  500. package/docs/interaction-model.md +0 -97
  501. /package/dist/{core → query}/QueryProcessor.d.ts +0 -0
  502. /package/dist/{core → query}/QueryProcessor.js +0 -0
  503. /package/dist/{core/storeContract.js → storeContract.js} +0 -0
  504. /package/dist/{core → stores}/openIDBWithTimeout.d.ts +0 -0
  505. /package/dist/{core → stores}/openIDBWithTimeout.js +0 -0
  506. /package/dist/{client → transaction/auth}/credentialEndpoint.d.ts +0 -0
  507. /package/dist/{client → transaction/auth}/credentialEndpoint.js +0 -0
  508. /package/dist/{auth → transaction/auth}/credentialPolicy.d.ts +0 -0
  509. /package/dist/{auth → transaction/auth}/credentialPolicy.js +0 -0
  510. /package/dist/{auth → transaction/auth}/credentialSource.js +0 -0
  511. /package/dist/{client → transaction/auth}/hostedEndpoints.d.ts +0 -0
  512. /package/dist/{client → transaction/auth}/hostedEndpoints.js +0 -0
  513. /package/dist/{client → transaction}/persistence.d.ts +0 -0
  514. /package/dist/{client → transaction}/persistence.js +0 -0
  515. /package/dist/{client → transaction/resources}/functionalUpdate.d.ts +0 -0
  516. /package/dist/{client → transaction/resources}/functionalUpdate.js +0 -0
  517. /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.d.ts +0 -0
  518. /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.js +0 -0
  519. /package/dist/{client → transaction/transport}/httpTransport.d.ts +0 -0
  520. /package/dist/{types → transaction/types}/modelData.d.ts +0 -0
  521. /package/dist/{types → transaction/types}/modelData.js +0 -0
  522. /package/dist/{types → transaction/types}/participant.d.ts +0 -0
  523. /package/dist/{types → transaction/types}/participant.js +0 -0
  524. /package/dist/{types → transaction/types}/streams.js +0 -0
  525. /package/dist/{utils → transaction/utils}/asyncIterator.d.ts +0 -0
  526. /package/dist/{utils → transaction/utils}/asyncIterator.js +0 -0
  527. /package/dist/{wire → transaction/wire}/bootstrapReason.d.ts +0 -0
  528. /package/dist/{wire → transaction/wire}/bootstrapReason.js +0 -0
  529. /package/dist/{wire → transaction/wire}/protocol.d.ts +0 -0
  530. /package/dist/{wire → transaction/wire}/protocol.js +0 -0
  531. /package/dist/{wire → transaction/wire}/protocolVersion.d.ts +0 -0
  532. /package/dist/{wire → transaction/wire}/protocolVersion.js +0 -0
  533. /package/dist/transactions/{UnconfirmedWrites.d.ts → mutations/UnconfirmedWrites.d.ts} +0 -0
  534. /package/dist/transactions/{UnconfirmedWrites.js → mutations/UnconfirmedWrites.js} +0 -0
  535. /package/dist/transactions/{optimisticApply.js → mutations/optimisticApply.js} +0 -0
  536. /package/dist/{core → views}/ViewRegistry.js +0 -0
@@ -7,13 +7,16 @@
7
7
  */
8
8
  import { AbloClaimedError, AbloAuthenticationError, AbloConnectionError, AbloIdempotencyError, AbloValidationError, AbloNotFoundError, claimedError, translateHttpError, } from '../errors.js';
9
9
  import { v5 as uuidv5 } from 'uuid';
10
- import { reconcileFunctionalUpdate, } from './functionalUpdate.js';
11
- import { assertBrowserSafety, readProcessEnv, resolveApiKey, resolveApiKeyValue, resolveAuthToken, resolveBaseURL, resolveBootstrapBaseUrl, rejectRemovedDatabaseUrlOption, warnIfCliKeyMismatch, } from './auth.js';
10
+ import { z } from 'zod';
11
+ import { reconcileFunctionalUpdate, } from '../resources/functionalUpdate.js';
12
+ import { assertBrowserSafety, readProcessEnv, resolveApiKey, resolveApiKeyValue, resolveAuthToken, resolveBaseURL, resolveBootstrapBaseUrl, rejectRemovedDatabaseUrlOption, warnIfCliKeyMismatch, } from '../auth/apiKey.js';
12
13
  import { PROTOCOL_VERSION, PROTOCOL_VERSION_HEADER } from '../wire/protocolVersion.js';
13
14
  import { commitReceiptSchema } from '../wire/commit.js';
15
+ import { claimAcquireResponseSchema, claimHeartbeatBatchReplySchema, claimHeartbeatReplySchema, claimListResponseSchema, claimStateSchema, } from '../wire/claims.js';
16
+ import { modelListResponseSchema, modelReadResponseSchema, } from '../wire/modelResponses.js';
14
17
  import { toMs } from '../utils/duration.js';
15
- import { heartbeatCadenceMs, resolveHeartbeatOptions, startClaimHeartbeatLoop, } from './claimHeartbeatLoop.js';
16
- import { mintSession } from './sessionMint.js';
18
+ import { heartbeatCadenceMs, resolveHeartbeatOptions, resolveHeartbeatPlan, startClaimHeartbeatLoop, } from '../coordination/claimHeartbeatLoop.js';
19
+ import { mintSession } from '../auth/sessionMint.js';
17
20
  import { parseIdentityResolveResponse } from '../auth/schemas.js';
18
21
  /**
19
22
  * Interpret a heartbeat reply for a lease this handle HOLDS: anything other
@@ -31,12 +34,20 @@ function heldHeartbeatReply(reply, label) {
31
34
  }
32
35
  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' });
33
36
  }
34
- import { assertWriteOptions } from './writeOptionsSchema.js';
35
- import { createDurableHttpCommitEnvelope, canonicalHttpCommitBody, durableHttpCommitEnvelopeSchema, httpCommitEnvelopeRecordId, isHttpCommitReplayExpired, } from '../transactions/httpCommitEnvelope.js';
36
- import { resolveDurableWrites } from './durableWrites.js';
37
+ import { claimDescription, partName } from '../coordination/schema.js';
38
+ import { subTarget, streamTarget, batchFence, fenceTokenFor, } from '../coordination/locator.js';
39
+ import { declaredMeta, wireMeta } from '../coordination/claimMeta.js';
40
+ import { assertWriteOptions } from '../resources/writeOptionsSchema.js';
41
+ import { createDurableHttpCommitEnvelope, canonicalHttpCommitBody, durableHttpCommitEnvelopeSchema, httpCommitEnvelopeRecordId, isHttpCommitReplayExpired, } from '../transactions/settlement/httpCommitEnvelope.js';
42
+ import { resolveDurableWrites } from '../durableWrites.js';
37
43
  /** @internal Default per-request deadline for the private HTTP transport. */
38
44
  export const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
39
45
  const HTTP_CONFIRMATION_POLL_INTERVAL_MS = 250;
46
+ /**
47
+ * The server's acquire window, mirrored here as the client-side default for a
48
+ * claim that names no `ttl` — it sets the auto-heartbeat cadence.
49
+ */
50
+ const DEFAULT_CLAIM_TTL_MS = 60_000;
40
51
  function parseSuccessfulCommitResponse(value, idempotencyKey) {
41
52
  const parsed = commitReceiptSchema.safeParse(value);
42
53
  if (!parsed.success || parsed.data.clientTxId !== idempotencyKey) {
@@ -51,6 +62,10 @@ function parseSuccessfulCommitResponse(value, idempotencyKey) {
51
62
  }
52
63
  /** Decode the HTTP claim DTO into the one public Claim shape. */
53
64
  function claimFromModelClaim(claim) {
65
+ // The handle a caller reads back is a public claim, so its `meta` is the
66
+ // declared shape; the rest of the sub-entity locator crosses whole rather
67
+ // than member by member, which is how `fields` used to die on this hop.
68
+ const { meta, ...details } = subTarget(claim.target);
54
69
  return {
55
70
  object: 'claim',
56
71
  id: claim.id,
@@ -62,12 +77,9 @@ function claimFromModelClaim(claim) {
62
77
  expiresAt: claim.expiresAt,
63
78
  ...(claim.position !== undefined ? { position: claim.position } : {}),
64
79
  target: {
65
- type: claim.target.model,
66
- id: claim.target.id,
67
- ...(claim.target.path ? { path: claim.target.path } : {}),
68
- ...(claim.target.range ? { range: claim.target.range } : {}),
69
- ...(claim.target.field ? { field: claim.target.field } : {}),
70
- ...(claim.target.meta ? { meta: claim.target.meta } : {}),
80
+ ...streamTarget(claim.target),
81
+ ...details,
82
+ ...(meta !== undefined ? { meta: declaredMeta(meta) } : {}),
71
83
  },
72
84
  };
73
85
  }
@@ -159,7 +171,7 @@ export function createHttpTransport(options) {
159
171
  }
160
172
  : undefined;
161
173
  if (!scope) {
162
- const rawIdentity = await requestJson('/auth/identity', { method: 'GET' }, true);
174
+ const rawIdentity = await requestRaw('/auth/identity', { method: 'GET' }, true);
163
175
  const identity = parseIdentityResolveResponse(rawIdentity);
164
176
  scope = {
165
177
  organizationId: identity.accountScope,
@@ -226,7 +238,15 @@ export function createHttpTransport(options) {
226
238
  return target.toString();
227
239
  }
228
240
  const requestTimeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
229
- async function requestJson(path, init, skipReady = false) {
241
+ /**
242
+ * Issues one request and returns its decoded body without a contract.
243
+ *
244
+ * Use this only where the response has no shape worth checking — a release
245
+ * that answers `{}` — or where the caller runs a richer check of its own, as
246
+ * the commit paths do with their receipt schema. Everywhere else, go through
247
+ * {@link requestJson}, which will not let a response past unvalidated.
248
+ */
249
+ async function requestRaw(path, init, skipReady = false) {
230
250
  if (!skipReady)
231
251
  await ready();
232
252
  const { idempotencyKey, sealedProtocolVersion, ...requestInit } = init;
@@ -295,6 +315,22 @@ export function createHttpTransport(options) {
295
315
  }
296
316
  return body;
297
317
  }
318
+ /**
319
+ * Issues one request and validates its body against the route's schema.
320
+ *
321
+ * The schema is the route's response contract, declared once in `wire/` and
322
+ * shared with the server that produces it. A body that does not match is a
323
+ * version disagreement between the two, so it is refused whole rather than
324
+ * read field by field and half-trusted.
325
+ */
326
+ async function requestJson(path, init, responseSchema, skipReady = false) {
327
+ const body = await requestRaw(path, init, skipReady);
328
+ const parsed = responseSchema.safeParse(body);
329
+ if (!parsed.success) {
330
+ throw new AbloConnectionError(`The Ablo API returned a response for ${init.method ?? 'GET'} ${path} that this client could not read; nothing was applied.`, { code: 'malformed_response', cause: parsed.error });
331
+ }
332
+ return parsed.data;
333
+ }
298
334
  function isDefinitiveHttpRejection(error) {
299
335
  if (typeof error !== 'object' || error === null)
300
336
  return false;
@@ -388,7 +424,7 @@ export function createHttpTransport(options) {
388
424
  }, remaining)
389
425
  : null;
390
426
  try {
391
- const raw = await requestJson(request.path, {
427
+ const raw = await requestRaw(request.path, {
392
428
  method: request.method,
393
429
  idempotencyKey: request.idempotencyKey,
394
430
  ...(request.sealedProtocolVersion !== undefined
@@ -456,7 +492,7 @@ export function createHttpTransport(options) {
456
492
  a.id.localeCompare(b.id));
457
493
  for (const envelope of envelopes) {
458
494
  try {
459
- const raw = await requestJson(envelope.request.path, {
495
+ const raw = await requestRaw(envelope.request.path, {
460
496
  method: envelope.request.method,
461
497
  idempotencyKey: envelope.idempotencyKey,
462
498
  sealedProtocolVersion: envelope.protocolVersion,
@@ -613,7 +649,7 @@ export function createHttpTransport(options) {
613
649
  };
614
650
  let response;
615
651
  try {
616
- const raw = await requestJson(exactRequest.path, {
652
+ const raw = await requestRaw(exactRequest.path, {
617
653
  method: exactRequest.method,
618
654
  idempotencyKey: exactRequest.idempotencyKey,
619
655
  ...(exactRequest.sealedProtocolVersion !== undefined
@@ -654,11 +690,6 @@ export function createHttpTransport(options) {
654
690
  ? crypto.randomUUID()
655
691
  : `tx_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
656
692
  }
657
- function createClaimId() {
658
- return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
659
- ? `int_${crypto.randomUUID()}`
660
- : `int_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
661
- }
662
693
  function createModelId(modelName, idempotencyKey) {
663
694
  if (idempotencyKey) {
664
695
  return uuidv5(`${modelName}:${idempotencyKey}`, 'aa4ba6d4-bf0b-5b38-9c45-116f79a6e548');
@@ -667,7 +698,7 @@ export function createHttpTransport(options) {
667
698
  ? crypto.randomUUID()
668
699
  : `id_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
669
700
  }
670
- function normalizeCommitOperation(op, defaults, fenceToken) {
701
+ function normalizeCommitOperation(op, defaults, fence) {
671
702
  return {
672
703
  action: op.action,
673
704
  model: op.model,
@@ -676,18 +707,16 @@ export function createHttpTransport(options) {
676
707
  transactionId: op.transactionId ?? null,
677
708
  readAt: op.readAt ?? defaults.readAt ?? null,
678
709
  onStale: op.onStale ?? defaults.onStale ?? null,
679
- // The batch's claim (if any) supplies one token for every op, mirroring
680
- // how it supplies the batch `readAt`.
681
- fenceToken: op.fenceToken ?? fenceToken ?? null,
710
+ fenceToken: op.fenceToken ?? fenceTokenFor(fence, op.model, op.id ?? null),
682
711
  };
683
712
  }
684
- function normalizeCommitOperations(commitOptions, fenceToken) {
713
+ function normalizeCommitOperations(commitOptions, fence) {
685
714
  if (commitOptions.operations.length === 0) {
686
715
  throw new AbloValidationError('Commit requires a non-empty `operations` array.', {
687
716
  code: 'commit_operation_required',
688
717
  });
689
718
  }
690
- return commitOptions.operations.map((op) => normalizeCommitOperation(op, commitOptions, fenceToken));
719
+ return commitOptions.operations.map((op) => normalizeCommitOperation(op, commitOptions, fence));
691
720
  }
692
721
  async function listClaimState(target) {
693
722
  const params = new URLSearchParams();
@@ -698,14 +727,107 @@ export function createHttpTransport(options) {
698
727
  if (target?.field)
699
728
  params.set('field', target.field);
700
729
  const suffix = params.toString();
701
- const body = await requestJson(`/v1/claims${suffix ? `?${suffix}` : ''}`, {
702
- method: 'GET',
703
- });
730
+ const body = await requestJson(`/v1/claims${suffix ? `?${suffix}` : ''}`, { method: 'GET' }, claimListResponseSchema);
731
+ // One list, one resource in two states: holders and waiters are told
732
+ // apart by each entry's `status`, not by bespoke envelope members.
704
733
  return {
705
- active: body.claims ?? [],
706
- queue: body.queue ?? [],
734
+ active: body.data.filter((row) => row.status !== 'queued'),
735
+ queue: body.data.filter((row) => row.status === 'queued'),
707
736
  };
708
737
  }
738
+ // The claim-ticket surface: everything a caller does holding only a
739
+ // `claimId` — which is all a queued acquire leaves in its hand. Each method
740
+ // is a thin cast of its route; the reply schemas are the wire's own, so the
741
+ // surface cannot describe a response the server does not send.
742
+ const claims = {
743
+ retrieve({ claimId }) {
744
+ return requestJson(`/v1/claims/${encodeURIComponent(claimId)}`, { method: 'GET' }, claimStateSchema);
745
+ },
746
+ heartbeat({ claimId, ttl }) {
747
+ return requestJson(`/v1/claims/${encodeURIComponent(claimId)}/heartbeat`, {
748
+ method: 'POST',
749
+ body: JSON.stringify(ttl !== undefined ? { ttl } : {}),
750
+ }, claimHeartbeatReplySchema);
751
+ },
752
+ async heartbeatAll(options) {
753
+ const reply = await requestJson('/v1/claims/heartbeat', {
754
+ method: 'POST',
755
+ body: JSON.stringify(options?.ttl !== undefined ? { ttl: options.ttl } : {}),
756
+ }, claimHeartbeatBatchReplySchema);
757
+ return reply.results;
758
+ },
759
+ async release({ claimId }) {
760
+ await requestRaw(`/v1/claims/${encodeURIComponent(claimId)}`, {
761
+ method: 'DELETE',
762
+ });
763
+ },
764
+ };
765
+ // How the stateless client waits its turn. The queued slot is real server
766
+ // state, so one heartbeat per tick does both jobs: it refreshes this
767
+ // waiter's slot and reports the line's answer — `queued` (still waiting) or
768
+ // `held` (granted). The first check comes quickly because most holds are a
769
+ // short claim→write→release; after that the cadence relaxes, with jitter so
770
+ // a fleet of waiters doesn't beat in step.
771
+ const GRANT_POLL_FIRST_MS = 250;
772
+ const GRANT_POLL_INTERVAL_MS = 1_000;
773
+ // An abort cuts the sleep short so the wait ends within a tick of the
774
+ // signal, not at the next scheduled beat.
775
+ const sleep = (ms, signal) => new Promise((resolve) => {
776
+ const done = () => {
777
+ clearTimeout(timer);
778
+ signal?.removeEventListener('abort', done);
779
+ resolve();
780
+ };
781
+ const timer = setTimeout(done, ms);
782
+ signal?.addEventListener('abort', done, { once: true });
783
+ });
784
+ async function awaitGrantOverHttp(targetLabel, queued, options) {
785
+ // The queued reply is a claim resource in its waiting state, so the
786
+ // handle is its `id` — same rule as the 201 and the poll.
787
+ const claimId = queued.id;
788
+ const { signal } = options;
789
+ // Leave the line before rejecting: an abandoned slot would otherwise sit
790
+ // in the queue until its TTL lapses, stalling every waiter behind it.
791
+ const rejectAndLeave = async (error) => {
792
+ await claims.release({ claimId }).catch(() => { });
793
+ throw error;
794
+ };
795
+ if (options.maxQueueDepth !== undefined && queued.position >= options.maxQueueDepth) {
796
+ return rejectAndLeave(new AbloClaimedError(`Claim queue for ${targetLabel} is ${queued.position} deep (max ${options.maxQueueDepth}).`, { code: 'queue_too_deep' }));
797
+ }
798
+ const deadline = options.waitTimeoutMs !== undefined ? Date.now() + options.waitTimeoutMs : undefined;
799
+ let delay = GRANT_POLL_FIRST_MS;
800
+ for (;;) {
801
+ if (signal?.aborted) {
802
+ return rejectAndLeave(new AbloClaimedError(`The wait for the claim on ${targetLabel} was aborted before the grant arrived.`, { code: 'claim_wait_aborted' }));
803
+ }
804
+ if (deadline !== undefined && Date.now() >= deadline) {
805
+ return rejectAndLeave(new AbloClaimedError(`Timed out after ${options.waitTimeoutMs}ms waiting for the queue grant on ${targetLabel}.`, { code: 'grant_timeout' }));
806
+ }
807
+ await sleep(deadline !== undefined ? Math.min(delay, Math.max(0, deadline - Date.now())) : delay, signal);
808
+ if (signal?.aborted) {
809
+ return rejectAndLeave(new AbloClaimedError(`The wait for the claim on ${targetLabel} was aborted before the grant arrived.`, { code: 'claim_wait_aborted' }));
810
+ }
811
+ delay = GRANT_POLL_INTERVAL_MS * (0.85 + Math.random() * 0.3);
812
+ // A lease that ended answers the beat with 409 `claim_lost`, which the
813
+ // wire error mapping raises as AbloClaimedError before this reads
814
+ // anything — the wait fails with the loss, as the socket wait does.
815
+ const beat = await claims.heartbeat({ claimId });
816
+ if (beat.status !== 'held')
817
+ continue;
818
+ // Granted. The heartbeat ack does not carry the fence token — the claim
819
+ // state does, server-stamped at grant.
820
+ const state = await claims.retrieve({ claimId });
821
+ if (state.status !== 'active') {
822
+ return rejectAndLeave(new AbloClaimedError(`Claim lost while queued for ${targetLabel}.`, {
823
+ code: 'claim_lost',
824
+ }));
825
+ }
826
+ return state.fenceToken !== undefined
827
+ ? { id: claimId, fenceToken: state.fenceToken }
828
+ : { id: claimId };
829
+ }
830
+ }
709
831
  async function applyClaimedPolicy(target, options, defaultPolicy = 'return') {
710
832
  const policy = options?.ifClaimed ?? defaultPolicy;
711
833
  if (policy === 'return')
@@ -734,7 +856,7 @@ export function createHttpTransport(options) {
734
856
  ...commitOptions,
735
857
  readAt: commitOptions.readAt ?? claim?.readAt ?? null,
736
858
  onStale: commitOptions.onStale ?? (claim?.readAt !== undefined ? 'reject' : null),
737
- }, claim?.fenceToken ?? null);
859
+ }, batchFence(claim?.target, claim?.fenceToken));
738
860
  const requestBody = {
739
861
  operations,
740
862
  reads: commitOptions.reads,
@@ -761,14 +883,14 @@ export function createHttpTransport(options) {
761
883
  })));
762
884
  throw error;
763
885
  }
764
- // `requestJson` throws via `translateHttpError` on any non-2xx,
765
- // so reaching here implies success. Narrow `status` to the
766
- // `CommitWait`-compatible subset; `'rejected'` only appears on
767
- // the rejection body (already thrown).
768
- const status = body.status === 'queued' ? 'queued' : 'confirmed';
886
+ // `requestJson` throws via `translateHttpError` on any non-2xx, so
887
+ // reaching here implies success and `body` is already the success-only
888
+ // receipt union a rejection is a separate type that never arrives here.
889
+ // The settlement status therefore passes through verbatim: no branch may
890
+ // collapse a state the server reported into a different one.
769
891
  return {
770
892
  id: body.id ?? body.clientTxId,
771
- status,
893
+ status: body.status,
772
894
  lastSyncId: body.lastSyncId,
773
895
  ...(body.notifications && body.notifications.length > 0
774
896
  ? { notifications: body.notifications }
@@ -799,26 +921,26 @@ export function createHttpTransport(options) {
799
921
  }
800
922
  }
801
923
  const qs = params.toString();
802
- const res = await requestJson(`/v1/models/${encodeURIComponent(modelName)}${qs ? `?${qs}` : ''}`, { method: 'GET' });
803
- return res.data ?? [];
924
+ const res = await requestJson(`/v1/models/${encodeURIComponent(modelName)}${qs ? `?${qs}` : ''}`, { method: 'GET' }, modelListResponseSchema);
925
+ // The envelope is checked; the rows are not, and cannot be here. This
926
+ // transport is schema-agnostic — it moves rows for whatever schema the
927
+ // caller declared, and `T` is that declaration. Row validation belongs to
928
+ // the typed facade above, which holds the model's schema.
929
+ return res.data;
804
930
  }
805
931
  async function retrieveModel(modelName, params) {
806
932
  await applyClaimedPolicy({ model: modelName, id: params.id }, params);
807
- const query = await requestJson(`/v1/models/${encodeURIComponent(modelName)}/${encodeURIComponent(params.id)}`, {
808
- method: 'GET',
809
- });
933
+ const query = await requestJson(`/v1/models/${encodeURIComponent(modelName)}/${encodeURIComponent(params.id)}`, { method: 'GET' }, modelReadResponseSchema);
810
934
  // A miss is `data: undefined`, not a thrown error. The WebSocket client's
811
935
  // `retrieve` returns `T | undefined` for a missing row; throwing only here
812
936
  // made the obvious read ("does this row exist?") a hard edge that an agent
813
937
  // had to wrap in try/catch. Both transports agree: an absent row means absent
814
938
  // data. Callers branch on `.data` (the documented `.data?.x` usage).
815
939
  // Normalize a miss to `undefined` (the server may send `null` or omit it).
940
+ // The row itself is the caller's declared type — see the note in `listModel`
941
+ // on why this transport validates the envelope and not the row.
816
942
  const data = (query.data ?? undefined);
817
- return {
818
- data,
819
- stamp: query.stamp ?? 0,
820
- claims: query.claims ?? [],
821
- };
943
+ return { data, stamp: query.stamp, claims: query.claims };
822
944
  }
823
945
  /**
824
946
  * A single-operation mutation over the model-scoped routes — the canonical
@@ -884,13 +1006,12 @@ export function createHttpTransport(options) {
884
1006
  recordCoordinationConflict(error, clientTxId, [{ model: modelName, id }]);
885
1007
  throw error;
886
1008
  }
887
- // `requestJson` throws via `translateHttpError` on any non-2xx, so reaching
888
- // here implies success. Narrow `status` to the `CommitWait`-compatible
889
- // subset; `'rejected'` only appears on a thrown rejection body.
890
- const status = body.status === 'queued' ? 'queued' : 'confirmed';
1009
+ // Same contract as `commits.create` above: a non-2xx already threw, so
1010
+ // `body` is the success-only receipt union and its settlement status passes
1011
+ // through verbatim rather than through a catch-all branch.
891
1012
  return {
892
1013
  id: body.serverTxId,
893
- status,
1014
+ status: body.status,
894
1015
  lastSyncId: body.lastSyncId,
895
1016
  };
896
1017
  }
@@ -904,30 +1025,47 @@ export function createHttpTransport(options) {
904
1025
  value.object === 'claim' &&
905
1026
  typeof value.id === 'string' &&
906
1027
  typeof value.release === 'function';
907
- const claimMeta = (options) => options?.meta;
908
1028
  const acquireClaim = async (params) => {
909
- const body = await requestJson(claimPath(params.id), {
910
- method: 'POST',
911
- body: JSON.stringify({
912
- description: params.description ?? 'editing',
913
- ...(params.ttl !== undefined ? { ttl: params.ttl } : {}),
914
- ...(claimMeta(params) ? { meta: claimMeta(params) } : {}),
915
- // `queue` (default true) queue behind the holder; false → fail-fast
916
- // with AbloClaimedError (work-distribution dedup).
917
- queue: params.queue ?? true,
918
- }),
919
- });
1029
+ // The row is named by the URL, so `target` carries only the narrowing a
1030
+ // claim adds below it. Sending it is what makes a field-scoped claim
1031
+ // actually field-scoped: the server's conflict rule reads `path`,
1032
+ // `range`, and `field`, so a claim that keeps them client-side takes a
1033
+ // lease on the whole row while its handle says otherwise.
1034
+ // Projected in one move rather than member by member. The member-by-member
1035
+ // version is how `field` came to be sent while `fields` was not, which
1036
+ // left a set-scoped claim silently holding the whole row.
1037
+ const narrowing = subTarget(params);
1038
+ // Typed as the request contract rather than a bare literal — the omission
1039
+ // above was invisible for exactly as long as this was an untyped object.
1040
+ const request = {
1041
+ description: claimDescription(params),
1042
+ ...(params.ttl !== undefined ? { ttl: params.ttl } : {}),
1043
+ // The caller's `meta` is the declared shape; the body is wire-shaped,
1044
+ // so it crosses through the same conversion `subTarget` used above.
1045
+ ...(params.meta !== undefined ? { meta: wireMeta(params.meta) } : {}),
1046
+ ...(Object.keys(narrowing).length > 0 ? { target: narrowing } : {}),
1047
+ // `queue` (default true) → queue behind the holder; false → fail-fast
1048
+ // with AbloClaimedError (work-distribution dedup).
1049
+ queue: params.queue ?? true,
1050
+ };
1051
+ const body = await requestJson(claimPath(params.id), { method: 'POST', body: JSON.stringify(request) }, claimAcquireResponseSchema);
1052
+ // One resource, two states, discriminated by `status`. The queued arm
1053
+ // WAITS, exactly as the socket client does: `claim({ id })` means
1054
+ // "serialize me behind the holder" on every transport, and the grant
1055
+ // machinery is the SDK's to own, not a loop each caller re-derives.
1056
+ // (Being queued is still not an error — ADR 0018 — which is precisely
1057
+ // why it no longer surfaces as one here. The `claims` namespace remains
1058
+ // the manual ticket surface.)
920
1059
  if (body.status === 'queued') {
921
- throw new AbloClaimedError(`Target ${name}/${params.id} is held; queued at position ${body.position ?? 0}. ` +
922
- `The HTTP client cannot await the grant without a WebSocket.`, { code: 'claim_queued' });
1060
+ return awaitGrantOverHttp(`${name}/${params.id}`, body, params);
923
1061
  }
924
- // `claimId` is the field name the queued response uses; check it alongside
925
- // the other id shapes the response may carry.
926
- const id = body.claim?.id ?? body.id ?? body.claimId ?? createClaimId();
927
- const fenceToken = body.claim?.fenceToken;
928
- return fenceToken !== undefined ? { id, fenceToken } : { id };
1062
+ // The lease's own fields are mirrored at the top level, the same place
1063
+ // the poll puts them one reader for both answers.
1064
+ return body.fenceToken !== undefined
1065
+ ? { id: body.id, fenceToken: body.fenceToken }
1066
+ : { id: body.id };
929
1067
  };
930
- const releaseClaim = (params) => requestJson(claimPath(isClaimHandle(params) ? params.target.id : params.id), {
1068
+ const releaseClaim = (params) => requestRaw(claimPath(isClaimHandle(params) ? params.target.id : params.id), {
931
1069
  method: 'DELETE',
932
1070
  }).then(() => undefined);
933
1071
  // One beat on the held lease. A lapsed lease answers `claim_lost`
@@ -941,18 +1079,35 @@ export function createHttpTransport(options) {
941
1079
  ...(options.ttl !== undefined ? { ttl: options.ttl } : {}),
942
1080
  ...(options.details !== undefined ? { details: options.details } : {}),
943
1081
  }),
944
- });
1082
+ }, claimHeartbeatReplySchema);
945
1083
  return heldHeartbeatReply(reply, `claim ${claimId} on ${name}/${id}`);
946
1084
  };
947
1085
  async function claimImpl(params) {
948
- const { id: claimId, fenceToken } = await acquireClaim(params);
1086
+ let acquired;
1087
+ try {
1088
+ acquired = await acquireClaim(params);
1089
+ }
1090
+ catch (error) {
1091
+ // The try-claim: a held target is an expected outcome of `queue:
1092
+ // false`, not an error — resolve `null` and let the caller move on.
1093
+ // Every other failure (auth, validation, network) stays a rejection,
1094
+ // and the write-site claim path calls `acquireClaim` directly, so a
1095
+ // write that could not claim still fails loudly.
1096
+ if (params.queue === false &&
1097
+ error instanceof AbloClaimedError &&
1098
+ (error.code === 'entity_claimed' || error.code === 'claim_conflict')) {
1099
+ return null;
1100
+ }
1101
+ throw error;
1102
+ }
1103
+ const { id: claimId, fenceToken } = acquired;
949
1104
  observability?.captureClaim({
950
1105
  phase: 'acquired',
951
1106
  claimId,
952
1107
  model: name,
953
1108
  id: params.id,
954
- ...(params.field ? { field: params.field } : {}),
955
- reason: params.description ?? 'editing',
1109
+ ...(params.field ? { field: partName(params.field) } : {}),
1110
+ description: claimDescription(params),
956
1111
  });
957
1112
  const { data, stamp } = await retrieveModel(name, { id: params.id });
958
1113
  // A held claim hands back a snapshot; the typed `HeldClaim.data` is `T`.
@@ -961,43 +1116,49 @@ export function createHttpTransport(options) {
961
1116
  if (data === undefined) {
962
1117
  throw new AbloNotFoundError(`Cannot claim ${name}/${params.id}: it does not exist (or is outside this credential's scope).`, [params.id]);
963
1118
  }
1119
+ // One reading of the heartbeat options — cadence and callbacks from
1120
+ // whichever spelling the caller used (plan object, shorthand, or the
1121
+ // deprecated flat callbacks).
1122
+ const plan = resolveHeartbeatPlan(params);
964
1123
  const heartbeat = async (beatOptions) => {
965
1124
  const resolved = resolveHeartbeatOptions(beatOptions);
966
1125
  const beat = await heartbeatClaim(params.id, claimId, {
967
1126
  ttl: resolved.ttl ?? params.ttl,
968
1127
  ...(resolved.details !== undefined ? { details: resolved.details } : {}),
969
1128
  });
970
- params.onHeartbeat?.(beat);
1129
+ plan.onBeat?.(beat);
971
1130
  return beat;
972
1131
  };
973
1132
  // Opt-in auto-heartbeat — the background-worker cadence. The stateless
974
- // HTTP claim defaults to the server's 60s acquire window when no TTL
1133
+ // HTTP claim defaults to the server's acquire window when no TTL
975
1134
  // was requested, so the default cadence lands at 20s beats.
976
- const stopHeartbeatLoop = params.heartbeat
1135
+ const stopHeartbeatLoop = plan.loop
977
1136
  ? startClaimHeartbeatLoop({
978
1137
  beat: () => heartbeat(),
979
- intervalMs: heartbeatCadenceMs(params.ttl !== undefined ? toMs(params.ttl) : 60_000, params.heartbeat),
980
- ...(params.onHeartbeatLost ? { onLost: params.onHeartbeatLost } : {}),
1138
+ intervalMs: heartbeatCadenceMs(params.ttl !== undefined ? toMs(params.ttl) : DEFAULT_CLAIM_TTL_MS, plan.cadence),
1139
+ ...(plan.onLost ? { onLost: plan.onLost } : {}),
981
1140
  })
982
1141
  : undefined;
983
1142
  const release = () => {
984
1143
  stopHeartbeatLoop?.();
985
1144
  return releaseClaim(params);
986
1145
  };
1146
+ // The handle handed back is a public claim, so its `meta` is the declared
1147
+ // shape — the same crossing the two decodes above make, spelled the same
1148
+ // way. `subTarget` is wire-shaped by contract, including here, where the
1149
+ // value happens to have started out declared.
1150
+ const { meta, ...narrowed } = subTarget(params);
987
1151
  return {
988
1152
  object: 'claim',
989
1153
  id: claimId,
990
1154
  readAt: stamp,
991
1155
  ...(fenceToken !== undefined ? { fenceToken } : {}),
992
1156
  target: {
993
- type: name,
994
- id: params.id,
995
- ...(params.field ? { field: params.field } : {}),
996
- ...(params.path ? { path: params.path } : {}),
997
- ...(params.range ? { range: params.range } : {}),
998
- ...(claimMeta(params) ? { meta: claimMeta(params) } : {}),
1157
+ ...streamTarget({ model: name, id: params.id }),
1158
+ ...narrowed,
1159
+ ...(meta !== undefined ? { meta: declaredMeta(meta) } : {}),
999
1160
  },
1000
- description: params.description ?? 'editing',
1161
+ description: claimDescription(params),
1001
1162
  data,
1002
1163
  release,
1003
1164
  revoke: () => {
@@ -1007,23 +1168,26 @@ export function createHttpTransport(options) {
1007
1168
  [Symbol.asyncDispose]: release,
1008
1169
  };
1009
1170
  }
1010
- const claimsForEntity = async (params) => requestJson(`/v1/claims?model=${encodeURIComponent(name)}&id=${encodeURIComponent(params.id)}${params.field ? `&field=${encodeURIComponent(params.field)}` : ''}`, { method: 'GET' });
1171
+ const claimsForEntity = (params) => requestJson(`/v1/claims?model=${encodeURIComponent(name)}&id=${encodeURIComponent(params.id)}${params.field ? `&field=${encodeURIComponent(partName(params.field))}` : ''}`, { method: 'GET' }, claimListResponseSchema);
1011
1172
  const claim = Object.assign(claimImpl, {
1012
1173
  release: releaseClaim,
1013
1174
  state: async (params) => {
1014
1175
  const res = await claimsForEntity(params);
1015
- const first = res.claims?.[0];
1176
+ // Holders come first in the one list; a `queued` entry is a waiter.
1177
+ const first = res.data.find((row) => row.status !== 'queued');
1016
1178
  return first ? claimFromModelClaim(first) : null;
1017
1179
  },
1018
1180
  queue: async (params) => {
1019
1181
  const res = await claimsForEntity(params);
1020
1182
  return {
1021
1183
  object: 'list',
1022
- data: (res.queue ?? []).map(claimFromModelClaim),
1184
+ data: res.data
1185
+ .filter((row) => row.status === 'queued')
1186
+ .map(claimFromModelClaim),
1023
1187
  };
1024
1188
  },
1025
1189
  reorder: async (params) => {
1026
- await requestJson(`${claimPath(params.id)}/reorder`, {
1190
+ await requestRaw(`${claimPath(params.id)}/reorder`, {
1027
1191
  method: 'POST',
1028
1192
  // The reorder route's payload is `{ heldBy, claimId }[]` — a Claim's id
1029
1193
  // is the claimId.
@@ -1136,6 +1300,29 @@ export function createHttpTransport(options) {
1136
1300
  return mutateModel('delete', name, params.id, undefined, options);
1137
1301
  });
1138
1302
  },
1303
+ async track(params) {
1304
+ const dependency = {
1305
+ model: name.toLowerCase(),
1306
+ id: params.id,
1307
+ ...(params.readAt !== undefined ? { readAt: params.readAt } : {}),
1308
+ };
1309
+ // A track carries no write, so it rides the commit lane as a
1310
+ // zero-operation body — the shape `/v1/commits` accepts for registering
1311
+ // a premise without one. Going through the same durable lane as every
1312
+ // other commit means a disconnect replays the registration rather than
1313
+ // dropping it, and a notification that had already fired is not lost to
1314
+ // a retry.
1315
+ const body = await dispatchHttpCommit({
1316
+ path: '/v1/commits',
1317
+ method: 'POST',
1318
+ idempotencyKey: createClientTxId(),
1319
+ body: { track: [dependency] },
1320
+ wait: 'confirmed',
1321
+ });
1322
+ return body.notifications && body.notifications.length > 0
1323
+ ? { notifications: body.notifications }
1324
+ : {};
1325
+ },
1139
1326
  };
1140
1327
  }
1141
1328
  return {
@@ -1148,6 +1335,7 @@ export function createHttpTransport(options) {
1148
1335
  async dispose() { },
1149
1336
  async purge() { },
1150
1337
  commits,
1338
+ claims,
1151
1339
  model,
1152
1340
  sessions: {
1153
1341
  async create(params) {
@@ -1158,11 +1346,21 @@ export function createHttpTransport(options) {
1158
1346
  if (!apiKey) {
1159
1347
  throw new AbloAuthenticationError('sessions.create requires a secret (sk_) API key — call it from your backend, not the browser.', { code: 'apikey_missing' });
1160
1348
  }
1349
+ // A transport built without a schema has no way to translate `can`'s
1350
+ // schema keys into the type names the server gates on. Minting anyway
1351
+ // would spell every override wrong and surface as
1352
+ // `capability_scope_denied` on the agent's first write, so refuse here
1353
+ // instead of guessing.
1354
+ if (!options.modelTypenames) {
1355
+ throw new AbloValidationError('sessions.create needs the schema this client is bound to. Construct it ' +
1356
+ "through Ablo({ schema, apiKey, transport: 'http' }) rather than the " +
1357
+ 'bare transport.', { code: 'invalid_options', param: 'schema' });
1358
+ }
1161
1359
  return mintSession(params, {
1162
1360
  apiKey,
1163
1361
  baseUrl: apiBaseUrl,
1362
+ modelTypenames: options.modelTypenames,
1164
1363
  ...(options.fetch ? { fetch: options.fetch } : {}),
1165
- ...(options.modelTypenames ? { modelTypenames: options.modelTypenames } : {}),
1166
1364
  });
1167
1365
  },
1168
1366
  },