@abloatai/ablo 0.26.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 (398) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +101 -85
  3. package/dist/BaseSyncedStore.d.ts +85 -88
  4. package/dist/BaseSyncedStore.js +131 -147
  5. package/dist/Database.d.ts +54 -68
  6. package/dist/Database.js +97 -113
  7. package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
  8. package/dist/{ObjectPool.js → InstanceCache.js} +85 -83
  9. package/dist/LazyReferenceCollection.d.ts +11 -15
  10. package/dist/LazyReferenceCollection.js +12 -16
  11. package/dist/Model.d.ts +37 -52
  12. package/dist/Model.js +46 -61
  13. package/dist/ModelRegistry.d.ts +21 -19
  14. package/dist/ModelRegistry.js +23 -27
  15. package/dist/NetworkMonitor.d.ts +5 -6
  16. package/dist/NetworkMonitor.js +5 -6
  17. package/dist/SyncClient.d.ts +112 -112
  18. package/dist/SyncClient.js +165 -172
  19. package/dist/adapters/alwaysOnline.d.ts +6 -8
  20. package/dist/adapters/alwaysOnline.js +6 -8
  21. package/dist/adapters/inMemoryStorage.d.ts +9 -9
  22. package/dist/adapters/inMemoryStorage.js +9 -9
  23. package/dist/agent/Agent.d.ts +27 -32
  24. package/dist/agent/Agent.js +18 -19
  25. package/dist/agent/index.d.ts +4 -4
  26. package/dist/agent/index.js +5 -5
  27. package/dist/agent/session.d.ts +47 -44
  28. package/dist/agent/session.js +37 -48
  29. package/dist/agent/types.d.ts +26 -31
  30. package/dist/agent/types.js +6 -7
  31. package/dist/ai-sdk/coordinatedTool.d.ts +108 -0
  32. package/dist/ai-sdk/{coordinated-tool.js → coordinatedTool.js} +44 -38
  33. package/dist/ai-sdk/coordinationContext.d.ts +46 -0
  34. package/dist/ai-sdk/{coordination-context.js → coordinationContext.js} +26 -33
  35. package/dist/ai-sdk/index.d.ts +25 -22
  36. package/dist/ai-sdk/index.js +25 -22
  37. package/dist/ai-sdk/wrap.d.ts +6 -7
  38. package/dist/ai-sdk/wrap.js +1 -1
  39. package/dist/auth/credentialPolicy.d.ts +69 -74
  40. package/dist/auth/credentialPolicy.js +51 -56
  41. package/dist/auth/credentialSource.d.ts +6 -5
  42. package/dist/auth/credentialSource.js +9 -10
  43. package/dist/auth/index.d.ts +59 -58
  44. package/dist/auth/index.js +31 -37
  45. package/dist/auth/schemas.d.ts +5 -4
  46. package/dist/auth/schemas.js +5 -4
  47. package/dist/batching/index.d.ts +19 -21
  48. package/dist/batching/index.js +14 -17
  49. package/dist/cli.cjs +167 -119
  50. package/dist/client/Ablo.d.ts +73 -73
  51. package/dist/client/Ablo.js +125 -160
  52. package/dist/client/ApiClient.d.ts +30 -19
  53. package/dist/client/ApiClient.js +133 -38
  54. package/dist/client/auth.d.ts +47 -47
  55. package/dist/client/auth.js +108 -117
  56. package/dist/client/claimHeartbeatLoop.d.ts +50 -0
  57. package/dist/client/claimHeartbeatLoop.js +88 -0
  58. package/dist/client/consoleLogger.d.ts +5 -6
  59. package/dist/client/consoleLogger.js +5 -6
  60. package/dist/client/createInternalComponents.d.ts +14 -17
  61. package/dist/client/createInternalComponents.js +25 -30
  62. package/dist/client/createModelProxy.d.ts +130 -120
  63. package/dist/client/createModelProxy.js +152 -122
  64. package/dist/client/credentialEndpoint.d.ts +40 -42
  65. package/dist/client/credentialEndpoint.js +35 -36
  66. package/dist/client/functionalUpdate.d.ts +29 -27
  67. package/dist/client/functionalUpdate.js +21 -21
  68. package/dist/client/hostedEndpoints.d.ts +9 -12
  69. package/dist/client/hostedEndpoints.js +9 -12
  70. package/dist/client/httpClient.d.ts +57 -53
  71. package/dist/client/httpClient.js +29 -31
  72. package/dist/client/identity.d.ts +15 -20
  73. package/dist/client/identity.js +47 -58
  74. package/dist/client/modelRegistration.d.ts +5 -9
  75. package/dist/client/modelRegistration.js +67 -87
  76. package/dist/client/options.d.ts +134 -157
  77. package/dist/client/options.js +3 -7
  78. package/dist/client/registerDataSource.d.ts +9 -9
  79. package/dist/client/registerDataSource.js +15 -16
  80. package/dist/client/resourceTypes.d.ts +64 -75
  81. package/dist/client/resourceTypes.js +4 -10
  82. package/dist/client/schemaConfig.d.ts +31 -43
  83. package/dist/client/schemaConfig.js +38 -50
  84. package/dist/client/sessionMint.d.ts +16 -12
  85. package/dist/client/sessionMint.js +26 -31
  86. package/dist/client/validateAbloOptions.d.ts +12 -14
  87. package/dist/client/validateAbloOptions.js +8 -9
  88. package/dist/client/writeOptionsSchema.d.ts +18 -16
  89. package/dist/client/writeOptionsSchema.js +23 -20
  90. package/dist/client/wsMutationExecutor.d.ts +15 -20
  91. package/dist/client/wsMutationExecutor.js +17 -23
  92. package/dist/context.d.ts +6 -4
  93. package/dist/context.js +6 -4
  94. package/dist/coordination/index.d.ts +10 -8
  95. package/dist/coordination/index.js +14 -12
  96. package/dist/coordination/schema.d.ts +176 -128
  97. package/dist/coordination/schema.js +197 -133
  98. package/dist/coordination/trace.d.ts +9 -10
  99. package/dist/coordination/trace.js +13 -14
  100. package/dist/core/DatabaseManager.d.ts +5 -7
  101. package/dist/core/DatabaseManager.js +15 -19
  102. package/dist/core/QueryProcessor.d.ts +7 -9
  103. package/dist/core/QueryProcessor.js +22 -28
  104. package/dist/core/QueryView.d.ts +8 -8
  105. package/dist/core/QueryView.js +2 -2
  106. package/dist/core/StoreManager.d.ts +12 -14
  107. package/dist/core/StoreManager.js +21 -24
  108. package/dist/core/ViewRegistry.d.ts +5 -5
  109. package/dist/core/ViewRegistry.js +4 -4
  110. package/dist/core/index.d.ts +17 -12
  111. package/dist/core/index.js +32 -26
  112. package/dist/core/openIDBWithTimeout.d.ts +38 -36
  113. package/dist/core/openIDBWithTimeout.js +42 -43
  114. package/dist/core/queryUtils.d.ts +45 -0
  115. package/dist/core/queryUtils.js +69 -0
  116. package/dist/core/storeContract.d.ts +63 -61
  117. package/dist/core/storeContract.js +8 -12
  118. package/dist/environment.d.ts +28 -0
  119. package/dist/environment.js +21 -0
  120. package/dist/errorCodes.d.ts +107 -99
  121. package/dist/errorCodes.js +131 -132
  122. package/dist/errors.d.ts +160 -166
  123. package/dist/errors.js +155 -158
  124. package/dist/index.d.ts +30 -27
  125. package/dist/index.js +89 -86
  126. package/dist/interfaces/index.d.ts +102 -113
  127. package/dist/interfaces/index.js +5 -4
  128. package/dist/keys/index.d.ts +27 -29
  129. package/dist/keys/index.js +41 -40
  130. package/dist/mutators/RecordingTransaction.d.ts +16 -16
  131. package/dist/mutators/RecordingTransaction.js +31 -37
  132. package/dist/mutators/Transaction.d.ts +18 -26
  133. package/dist/mutators/Transaction.js +14 -20
  134. package/dist/mutators/UndoManager.d.ts +122 -131
  135. package/dist/mutators/UndoManager.js +145 -156
  136. package/dist/mutators/defineMutators.d.ts +23 -34
  137. package/dist/mutators/defineMutators.js +14 -20
  138. package/dist/mutators/inverseOp.d.ts +12 -15
  139. package/dist/mutators/inverseOp.js +12 -15
  140. package/dist/mutators/mutateActions.d.ts +10 -9
  141. package/dist/mutators/mutateActions.js +1 -1
  142. package/dist/mutators/readerActions.d.ts +9 -8
  143. package/dist/mutators/readerActions.js +2 -2
  144. package/dist/mutators/undoApply.d.ts +31 -27
  145. package/dist/mutators/undoApply.js +26 -24
  146. package/dist/policy/index.d.ts +5 -3
  147. package/dist/policy/index.js +5 -3
  148. package/dist/policy/types.d.ts +104 -100
  149. package/dist/policy/types.js +67 -66
  150. package/dist/query/client.d.ts +28 -23
  151. package/dist/query/client.js +45 -43
  152. package/dist/query/types.d.ts +37 -60
  153. package/dist/query/types.js +13 -33
  154. package/dist/react/AbloProvider.d.ts +1 -1
  155. package/dist/react/AbloProvider.js +2 -2
  156. package/dist/react/context.d.ts +25 -28
  157. package/dist/react/context.js +9 -10
  158. package/dist/react/index.d.ts +41 -42
  159. package/dist/react/index.js +37 -38
  160. package/dist/react/internalContext.d.ts +17 -19
  161. package/dist/react/useAblo.d.ts +23 -22
  162. package/dist/react/useAblo.js +16 -14
  163. package/dist/react/useCurrentUserId.d.ts +8 -7
  164. package/dist/react/useCurrentUserId.js +8 -7
  165. package/dist/react/useErrorListener.d.ts +7 -7
  166. package/dist/react/useErrorListener.js +10 -11
  167. package/dist/react/useMutationFailureListener.d.ts +8 -8
  168. package/dist/react/useMutationFailureListener.js +8 -8
  169. package/dist/react/useMutators.d.ts +11 -11
  170. package/dist/react/useMutators.js +3 -3
  171. package/dist/react/useReactive.js +2 -2
  172. package/dist/react/useSyncStatus.d.ts +4 -6
  173. package/dist/react/useUndoScope.d.ts +7 -9
  174. package/dist/react/useUndoScope.js +1 -1
  175. package/dist/schema/coordination.d.ts +21 -25
  176. package/dist/schema/coordination.js +21 -25
  177. package/dist/schema/ddl.d.ts +43 -39
  178. package/dist/schema/ddl.js +75 -68
  179. package/dist/schema/ddlLock.d.ts +20 -24
  180. package/dist/schema/ddlLock.js +18 -23
  181. package/dist/schema/diff.d.ts +99 -61
  182. package/dist/schema/diff.js +43 -34
  183. package/dist/schema/field.d.ts +37 -42
  184. package/dist/schema/field.js +35 -48
  185. package/dist/schema/generate.d.ts +12 -12
  186. package/dist/schema/generate.js +12 -12
  187. package/dist/schema/index.d.ts +2 -2
  188. package/dist/schema/index.js +21 -23
  189. package/dist/schema/model.d.ts +118 -143
  190. package/dist/schema/model.js +22 -33
  191. package/dist/schema/openapi.d.ts +10 -9
  192. package/dist/schema/openapi.js +5 -3
  193. package/dist/schema/queries.d.ts +29 -31
  194. package/dist/schema/queries.js +23 -25
  195. package/dist/schema/relation.d.ts +89 -99
  196. package/dist/schema/relation.js +13 -13
  197. package/dist/schema/residency.d.ts +16 -13
  198. package/dist/schema/residency.js +16 -13
  199. package/dist/schema/roles.d.ts +36 -43
  200. package/dist/schema/roles.js +31 -37
  201. package/dist/schema/schema.d.ts +33 -42
  202. package/dist/schema/schema.js +31 -32
  203. package/dist/schema/select.d.ts +13 -13
  204. package/dist/schema/select.js +13 -13
  205. package/dist/schema/serialize.d.ts +28 -31
  206. package/dist/schema/serialize.js +27 -31
  207. package/dist/schema/sugar.d.ts +17 -32
  208. package/dist/schema/sugar.js +14 -29
  209. package/dist/schema/{sync-delta-row.d.ts → syncDeltaRow.d.ts} +26 -49
  210. package/dist/schema/syncDeltaRow.js +89 -0
  211. package/dist/schema/tenancy.d.ts +44 -46
  212. package/dist/schema/tenancy.js +46 -48
  213. package/dist/server/adapter.d.ts +58 -58
  214. package/dist/server/adapter.js +13 -14
  215. package/dist/server/commit.d.ts +60 -64
  216. package/dist/server/index.d.ts +9 -10
  217. package/dist/server/index.js +1 -1
  218. package/dist/server/readConfig.d.ts +70 -0
  219. package/dist/server/readConfig.js +8 -0
  220. package/dist/server/storageMode.d.ts +23 -0
  221. package/dist/server/storageMode.js +17 -0
  222. package/dist/source/adapter.d.ts +30 -25
  223. package/dist/source/adapter.js +10 -10
  224. package/dist/source/adapters/drizzle.d.ts +28 -23
  225. package/dist/source/adapters/drizzle.js +30 -25
  226. package/dist/source/adapters/kysely.d.ts +27 -25
  227. package/dist/source/adapters/kysely.js +24 -23
  228. package/dist/source/adapters/memory.d.ts +8 -7
  229. package/dist/source/adapters/memory.js +9 -8
  230. package/dist/source/adapters/prisma.d.ts +13 -12
  231. package/dist/source/adapters/prisma.js +22 -25
  232. package/dist/source/conformance.d.ts +18 -11
  233. package/dist/source/conformance.js +17 -11
  234. package/dist/source/connector.d.ts +31 -32
  235. package/dist/source/connector.js +28 -28
  236. package/dist/source/connectorProtocol.d.ts +160 -0
  237. package/dist/source/connectorProtocol.js +162 -0
  238. package/dist/source/contract.d.ts +26 -27
  239. package/dist/source/contract.js +28 -29
  240. package/dist/source/factory.d.ts +46 -58
  241. package/dist/source/factory.js +22 -27
  242. package/dist/source/index.d.ts +7 -9
  243. package/dist/source/index.js +12 -14
  244. package/dist/source/migrations.d.ts +9 -9
  245. package/dist/source/migrations.js +9 -9
  246. package/dist/source/next.d.ts +9 -10
  247. package/dist/source/next.js +6 -7
  248. package/dist/source/pushQueue.d.ts +69 -47
  249. package/dist/source/pushQueue.js +32 -28
  250. package/dist/source/signing.d.ts +46 -17
  251. package/dist/source/signing.js +28 -11
  252. package/dist/source/types.d.ts +121 -104
  253. package/dist/source/types.js +13 -14
  254. package/dist/stores/ObjectStore.d.ts +10 -11
  255. package/dist/stores/ObjectStore.js +11 -12
  256. package/dist/stores/ObjectStoreContract.d.ts +12 -15
  257. package/dist/stores/SyncActionStore.d.ts +7 -11
  258. package/dist/stores/SyncActionStore.js +13 -17
  259. package/dist/surface.d.ts +27 -20
  260. package/dist/surface.js +27 -20
  261. package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +36 -42
  262. package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +76 -76
  263. package/dist/sync/ConnectionManager.d.ts +39 -50
  264. package/dist/sync/ConnectionManager.js +55 -66
  265. package/dist/sync/NetworkProbe.d.ts +24 -29
  266. package/dist/sync/NetworkProbe.js +63 -69
  267. package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +42 -41
  268. package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +59 -54
  269. package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +43 -57
  270. package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +43 -51
  271. package/dist/sync/SyncWebSocket.d.ts +139 -165
  272. package/dist/sync/SyncWebSocket.js +191 -223
  273. package/dist/sync/awaitClaimGrant.d.ts +18 -18
  274. package/dist/sync/awaitClaimGrant.js +11 -11
  275. package/dist/sync/bootstrapApply.d.ts +34 -24
  276. package/dist/sync/bootstrapApply.js +27 -19
  277. package/dist/sync/commitFrames.d.ts +21 -20
  278. package/dist/sync/commitFrames.js +18 -18
  279. package/dist/sync/createClaimStream.d.ts +23 -22
  280. package/dist/sync/createClaimStream.js +105 -23
  281. package/dist/sync/createPresenceStream.d.ts +19 -18
  282. package/dist/sync/createPresenceStream.js +25 -26
  283. package/dist/sync/createSnapshot.d.ts +12 -14
  284. package/dist/sync/createSnapshot.js +20 -26
  285. package/dist/sync/credentialLifecycle.d.ts +104 -104
  286. package/dist/sync/credentialLifecycle.js +140 -147
  287. package/dist/sync/deltaPipeline.d.ts +36 -34
  288. package/dist/sync/deltaPipeline.js +64 -65
  289. package/dist/sync/groupChange.d.ts +63 -61
  290. package/dist/sync/groupChange.js +74 -78
  291. package/dist/sync/heartbeat.d.ts +34 -33
  292. package/dist/sync/heartbeat.js +31 -31
  293. package/dist/sync/participants.d.ts +19 -19
  294. package/dist/sync/schemas.d.ts +3 -2
  295. package/dist/sync/schemas.js +14 -10
  296. package/dist/sync/syncCursor.d.ts +17 -21
  297. package/dist/sync/syncCursor.js +17 -21
  298. package/dist/sync/syncPlan.d.ts +28 -36
  299. package/dist/sync/syncPlan.js +18 -19
  300. package/dist/sync/syncPosition.d.ts +54 -49
  301. package/dist/sync/syncPosition.js +57 -52
  302. package/dist/sync/wsFrameHandlers.d.ts +35 -36
  303. package/dist/sync/wsFrameHandlers.js +63 -67
  304. package/dist/testing/fixtures/bootstrap.d.ts +12 -6
  305. package/dist/testing/fixtures/bootstrap.js +12 -6
  306. package/dist/testing/fixtures/deltas.d.ts +30 -33
  307. package/dist/testing/fixtures/deltas.js +30 -33
  308. package/dist/testing/fixtures/models.d.ts +11 -10
  309. package/dist/testing/fixtures/models.js +11 -10
  310. package/dist/testing/helpers/{react-wrapper.d.ts → reactWrapper.d.ts} +13 -10
  311. package/dist/testing/helpers/{react-wrapper.js → reactWrapper.js} +15 -12
  312. package/dist/testing/helpers/{sync-engine-harness.d.ts → syncEngineHarness.d.ts} +17 -15
  313. package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +12 -10
  314. package/dist/testing/helpers/wait.d.ts +13 -8
  315. package/dist/testing/helpers/wait.js +13 -8
  316. package/dist/testing/index.d.ts +3 -3
  317. package/dist/testing/index.js +2 -2
  318. package/dist/testing/mocks/MockMutationExecutor.d.ts +18 -17
  319. package/dist/testing/mocks/MockMutationExecutor.js +15 -14
  320. package/dist/testing/mocks/MockNetworkMonitor.d.ts +8 -8
  321. package/dist/testing/mocks/MockNetworkMonitor.js +8 -8
  322. package/dist/testing/mocks/MockSyncContext.d.ts +20 -17
  323. package/dist/testing/mocks/MockSyncContext.js +15 -13
  324. package/dist/testing/mocks/MockSyncStore.d.ts +10 -10
  325. package/dist/testing/mocks/MockSyncStore.js +11 -11
  326. package/dist/testing/mocks/MockWebSocket.d.ts +26 -22
  327. package/dist/testing/mocks/MockWebSocket.js +22 -21
  328. package/dist/transactions/TransactionQueue.d.ts +181 -176
  329. package/dist/transactions/TransactionQueue.js +338 -350
  330. package/dist/transactions/TransactionStore.d.ts +6 -4
  331. package/dist/transactions/TransactionStore.js +6 -4
  332. package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
  333. package/dist/transactions/UnconfirmedWrites.js +104 -0
  334. package/dist/transactions/coalesceRules.d.ts +41 -17
  335. package/dist/transactions/coalesceRules.js +40 -17
  336. package/dist/transactions/commitPayload.d.ts +48 -52
  337. package/dist/transactions/commitPayload.js +48 -57
  338. package/dist/transactions/deltaConfirmation.d.ts +20 -22
  339. package/dist/transactions/deltaConfirmation.js +37 -45
  340. package/dist/transactions/optimisticApply.d.ts +49 -0
  341. package/dist/transactions/optimisticApply.js +65 -0
  342. package/dist/transactions/{persistedReplay.d.ts → replayValidation.d.ts} +28 -22
  343. package/dist/transactions/{persistedReplay.js → replayValidation.js} +33 -27
  344. package/dist/types/global.d.ts +46 -41
  345. package/dist/types/global.js +20 -19
  346. package/dist/types/index.d.ts +71 -77
  347. package/dist/types/index.js +22 -22
  348. package/dist/types/modelData.d.ts +6 -8
  349. package/dist/types/modelData.js +5 -7
  350. package/dist/types/participant.d.ts +10 -11
  351. package/dist/types/participant.js +6 -8
  352. package/dist/types/streams.d.ts +208 -195
  353. package/dist/types/streams.js +7 -7
  354. package/dist/utils/asyncIterator.d.ts +25 -32
  355. package/dist/utils/asyncIterator.js +25 -32
  356. package/dist/utils/duration.d.ts +12 -15
  357. package/dist/utils/duration.js +12 -15
  358. package/dist/utils/mobxSetup.d.ts +53 -0
  359. package/dist/utils/{mobx-setup.js → mobxSetup.js} +42 -98
  360. package/dist/webhooks/events.d.ts +21 -16
  361. package/dist/webhooks/events.js +10 -8
  362. package/dist/webhooks/index.d.ts +5 -7
  363. package/dist/webhooks/index.js +5 -7
  364. package/dist/{schema/sync-delta-wire.d.ts → wire/delta.d.ts} +58 -41
  365. package/dist/wire/delta.js +114 -0
  366. package/dist/wire/errorEnvelope.d.ts +30 -31
  367. package/dist/wire/errorEnvelope.js +34 -40
  368. package/dist/wire/frames.d.ts +79 -86
  369. package/dist/wire/frames.js +26 -33
  370. package/dist/wire/index.d.ts +14 -12
  371. package/dist/wire/index.js +30 -26
  372. package/dist/wire/listEnvelope.d.ts +16 -23
  373. package/dist/wire/listEnvelope.js +7 -6
  374. package/dist/wire/protocol.d.ts +25 -32
  375. package/dist/wire/protocol.js +25 -32
  376. package/dist/wire/protocolVersion.d.ts +44 -40
  377. package/dist/wire/protocolVersion.js +44 -40
  378. package/docs/coordination.md +59 -0
  379. package/package.json +11 -10
  380. package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
  381. package/dist/ai-sdk/coordination-context.d.ts +0 -52
  382. package/dist/core/query-utils.d.ts +0 -34
  383. package/dist/core/query-utils.js +0 -59
  384. package/dist/schema/sync-delta-row.js +0 -103
  385. package/dist/schema/sync-delta-wire.js +0 -102
  386. package/dist/server/read-config.d.ts +0 -67
  387. package/dist/server/read-config.js +0 -8
  388. package/dist/server/storage-mode.d.ts +0 -8
  389. package/dist/server/storage-mode.js +0 -28
  390. package/dist/source/connector-protocol.d.ts +0 -159
  391. package/dist/source/connector-protocol.js +0 -161
  392. package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
  393. package/dist/transactions/OptimisticEchoTracker.js +0 -104
  394. package/dist/transactions/mutation-error-handler.d.ts +0 -5
  395. package/dist/transactions/mutation-error-handler.js +0 -39
  396. package/dist/transactions/optimistic.d.ts +0 -24
  397. package/dist/transactions/optimistic.js +0 -45
  398. package/dist/utils/mobx-setup.d.ts +0 -42
@@ -1,68 +1,70 @@
1
1
  /**
2
- * The canonical Ablo error-code registry the **`code` tier** of the
3
- * Stripe-style two-tier error model.
2
+ * The registry of every stable error code Ablo can produce. Error handling has
3
+ * two levels, and this file defines the finer one.
4
4
  *
5
- * ### The two tiers (mirrors Stripe)
5
+ * - The `type` is the coarse category, and each one corresponds to an
6
+ * {@link AbloError} subclass such as `AbloPermissionError` or
7
+ * `AbloValidationError`. Catching by `instanceof` is equivalent to switching
8
+ * on `error.type`.
9
+ * - The `code` is the fine-grained, machine-readable identifier defined here,
10
+ * written in `snake_case` (for example `entity_claimed` or `queue_too_deep`).
11
+ * This is what you switch on to handle a specific situation, and what the
12
+ * documentation link for an error is built from.
6
13
  *
7
- * - **`type`** coarse category, 1:1 with an {@link AbloError} subclass
8
- * (`AbloPermissionError`, `AbloValidationError`, …). "Catch by
9
- * `instanceof`" "switch on `e.type`". This tier lives in `errors.ts`.
10
- * - **`code`** — the fine-grained, machine-readable identifier in this
11
- * file. `snake_case`, ordered noun→state (`entity_claimed`) or
12
- * condition→constraint (`queue_too_deep`). This is what callers
13
- * `switch` on for specific handling, and what `doc_url` is derived from.
14
+ * The client, the server, and the tool-calling boundary all speak this same
15
+ * vocabulary, which makes the registry part of the API contract. Two things
16
+ * follow from that:
14
17
  *
15
- * Because sync-engine sync-server MCP all speak this code vocabulary,
16
- * the registry **is** the wire contract. Two consequences:
17
- *
18
- * 1. `ErrorCode` is a *closed* union (plus the `policy:${string}`
19
- * dynamic family). Producing an unregistered code is a compile error
20
- * that's the whole point. {@link AbloError}'s constructor param is
21
- * narrowed to `ErrorCode`; only the wire-parse boundary
22
- * (`translateHttpError`, frame deserialization) casts an incoming
23
- * string to `ErrorCode`, so an *older* SDK still tolerates a *newer*
24
- * server's code (forward compat) while internal producers stay checked.
25
- * 2. The `surface: 'wire'` subset is what an HTTP/MCP boundary maps from
26
- * and what the public error docs are generated from. `surface:
27
- * 'client'` codes are local SDK invariants (you forgot to open the DB,
28
- * a model isn't registered) — never sent over the network, so they
29
- * carry no `httpStatus`, exactly as Stripe omits client-side
30
- * programmer errors from its published code list.
18
+ * 1. {@link ErrorCode} is a closed set plus the dynamic `policy:${string}`
19
+ * family so producing a code that is not registered here is a
20
+ * compile-time error. The {@link AbloError} constructor accepts only a
21
+ * registered code. The one place an arbitrary string is accepted as a code
22
+ * is where an incoming response is parsed, which lets an older client
23
+ * tolerate a code from a newer server it does not yet recognize.
24
+ * 2. The codes marked `surface: 'wire'` are the ones that cross the network
25
+ * and are mapped at the HTTP and tool-calling boundaries; the public error
26
+ * documentation is generated from them. Codes marked `surface: 'client'`
27
+ * describe local mistakes accessing the database before opening it, or
28
+ * writing to a model that was never registered and are never sent over
29
+ * the network, so they carry no HTTP status.
31
30
  */
32
31
  import { z } from 'zod';
33
32
  /**
34
- * Version of the error contract the envelope shape + the set of codes and
35
- * their semantics. Date-based, like Stripe's API versions. Bump it (and only
36
- * it) when the contract changes in a way consumers can observe: a new/removed
37
- * code, a changed HTTP status, an envelope field. Emitted in `errors.json`
38
- * and on the `Ablo-Version` response header so a consumer can detect drift.
33
+ * The version of the error contract: the envelope shape together with the set of
34
+ * codes and their meanings. It is date-based, and changes only when the contract
35
+ * changes in a way a consumer can observe a code added or removed, an HTTP
36
+ * status changed, or an envelope field changed. It is emitted in the generated
37
+ * error documentation and returned on the `Ablo-Version` response header, so a
38
+ * consumer can detect when its expected contract has drifted from the server's.
39
39
  */
40
40
  export const ERROR_CONTRACT_VERSION = '2026-07-03';
41
41
  /**
42
- * The closed taxonomy of *how a failure recovers*one rung above the raw
43
- * `code`. Where `code` says **what** went wrong, `RecoveryClass` says **what
44
- * the client should do about it**, which is exactly the discriminant the sync
45
- * FSM and the network probe need. It collapses what used to be three scattered
46
- * booleans (`retryable`, `authBlocked`, `sessionValid`) into one exhaustive,
47
- * Zod-validated enum so the connection layer branches on a single value with
48
- * compile-time completeness instead of ad-hoc `if (!isRetryableCode(...))`
49
- * chains.
42
+ * A closed classification of how a failure can be recovered from a level above
43
+ * the raw {@link ErrorCode}. Where a code says what went wrong, a recovery class
44
+ * says what a client should do about it, which is exactly the distinction the
45
+ * connection layer needs to decide between retrying, re-minting a credential, and
46
+ * signing the user out. Every code maps to one of these, and the set is
47
+ * validated at runtime.
50
48
  *
51
- * - `access_credential_expiry` — the Stripe-style ephemeral key (`ek_`/`rk_`)
52
- * the sync-engine presents as its Bearer has expired. The long-lived login
53
- * is fine; the remedy is to silently RE-MINT a fresh key from the session
54
- * and retry the same request. This MUST NOT sign the user out (the whole
55
- * point of the wake-from-sleep fix: a 15-min `ek_` dying after a laptop nap
56
- * is routine, not a logout).
57
- * - `session_expiry` — the LONG-LIVED login itself is gone. Terminal:
49
+ * - `access_credential_expiry` — the short-lived access credential the client
50
+ * presents (its ephemeral `ek_` or `rk_` key) has expired, while the
51
+ * underlying login is still valid. The remedy is to mint a fresh key from the
52
+ * session and retry the same request. This does not sign the user out; a
53
+ * short-lived key expiring for example after a laptop resumes from sleep —
54
+ * is routine.
55
+ * - `session_expiry` — the long-lived login itself is gone. This is terminal:
58
56
  * sign out and route to re-authentication.
59
- * - `auth_blocked` — reachable, but the credential TYPE/config was rejected
60
- * (wrong key kind, untrusted issuer, no org). Re-auth re-mints the same
61
- * rejected credential and loops, so STOP don't reconnect, don't sign out.
62
- * - `permission` a 403 authorization denial (scope/role/membership).
63
- * - `transient` — retry the same request unchanged (5xx, lease contention…).
64
- * - `none` — not a recoverable-auth condition (validation, not-found, local
65
- * invariants, and any forward-compat code an older SDK doesn't know).
57
+ * - `auth_blocked` — the server was reachable but rejected the kind or
58
+ * configuration of the credential (wrong key type, untrusted issuer, no
59
+ * organization). Re-authenticating would present the same rejected credential
60
+ * and loop, so the client should stop rather than reconnect or sign out.
61
+ * - `permission` — an authorization denial (403) based on scope, role, or
62
+ * membership.
63
+ * - `transient` a temporary failure, such as a server error or lease
64
+ * contention, that may succeed if the same request is retried unchanged.
65
+ * - `none` — not a recoverable authentication condition: validation errors,
66
+ * not-found, local invariants, and any code an older client does not
67
+ * recognize.
66
68
  */
67
69
  export const RECOVERY_CLASSES = [
68
70
  'access_credential_expiry',
@@ -72,25 +74,26 @@ export const RECOVERY_CLASSES = [
72
74
  'transient',
73
75
  'none',
74
76
  ];
75
- /** Zod enum derived from {@link RECOVERY_CLASSES} the runtime-validatable
76
- * form of the recovery taxonomy. */
77
+ /** A Zod enum over {@link RECOVERY_CLASSES}, for validating a recovery class at
78
+ * runtime. */
77
79
  export const recoveryClassSchema = z.enum(RECOVERY_CLASSES);
78
80
  const wire = (category, httpStatus, retryable, message, recovery) => ({ category, surface: 'wire', httpStatus, retryable, message, recovery });
79
81
  const client = (category, message) => ({ category, surface: 'client', retryable: false, message });
80
82
  /**
81
- * The closed set of stable error codes. Add a code here BEFORE throwing it
82
- * the narrowed {@link AbloError} constructor param enforces this.
83
+ * The complete set of stable error codes, keyed by code. A code must be added
84
+ * here before it can be thrown, since the {@link AbloError} constructor accepts
85
+ * only codes from this set.
83
86
  */
84
87
  export const ERROR_CODES = {
85
88
  // ── auth (401) ─────────────────────────────────────────────────────
86
89
  apikey_invalid: wire('auth', 401, false, "This API key isn't one Ablo recognizes — it may be mistyped, truncated, or belong to a different environment. Check the key and try again."),
87
90
  apikey_revoked: wire('auth', 401, false, 'This API key has been revoked and can no longer be used. Mint a new key from the dashboard.'),
88
- // THE sync-engine access credential — the Stripe-style ephemeral key
89
- // (`ek_` for users, `rk_` for agents) minted server-side from the login and
90
- // presented as a Bearer. Its expiry is routine and re-mintable: get a fresh
91
- // key from the still-valid session and retry NEVER a sign-out. (An agent's
92
- // expired `rk_` must not log a human out either.) This is the ONLY code on
93
- // the silent re-mint path; see RecoveryClass `access_credential_expiry`.
91
+ // The short-lived access credential — the ephemeral key (`ek_` for users,
92
+ // `rk_` for agents) minted from the login and presented as a bearer token.
93
+ // Its expiry is routine and re-mintable: get a fresh key from the still-valid
94
+ // session and retry, rather than signing out. An agent's expired `rk_` must
95
+ // not sign a human out either. This is the one code on the silent re-mint
96
+ // path; see the `access_credential_expiry` recovery class.
94
97
  apikey_expired: wire('auth', 401, false, 'This ephemeral API key has expired. Mint a fresh key from your still-valid session and retry the request.', 'access_credential_expiry'),
95
98
  apikey_missing: wire('auth', 401, false, 'The request arrived without an API key. Send one as `Authorization: Bearer <key>`.'),
96
99
  api_key_required: wire('auth', 401, false, 'This operation requires an API key, and none was presented. Send one as `Authorization: Bearer <key>`.'),
@@ -99,12 +102,13 @@ export const ERROR_CODES = {
99
102
  identity_resolve_failed: wire('auth', 401, false, 'The server could not resolve an identity for this credential — the identity lookup was rejected. Check that the credential is still valid.'),
100
103
  auth_no_credentials: wire('auth', 401, false, 'No recognized authentication credential was presented — no API key and no bearer JWT. Send `Authorization: Bearer <token>`.'),
101
104
  identity_missing_organization: wire('auth', 401, false, 'Authentication succeeded, but the credential resolves to no organization, so requests cannot be scoped. Check that the key or token carries an organization.'),
102
- // The long-lived login is gone terminal, drives sign-out + re-auth.
105
+ // The long-lived login is gone; this is terminal and drives sign-out and
106
+ // re-authentication.
103
107
  session_expired: wire('auth', 401, false, 'Your session has expired or is no longer valid. Sign in again to continue.', 'session_expiry'),
104
- // `jwt_invalid` is the residual fallback; the codes below split out the
105
- // specific failure modes so an integrating customer can tell "I registered
106
- // the wrong JWKS" from "my token has no org claim" from "wrong audience"
107
- // rather than getting one opaque code for all of them.
108
+ // `jwt_invalid` is the general fallback; the codes below it split out specific
109
+ // failure modes, so an integrator can tell a wrong JWKS registration from a
110
+ // token with no organization claim from a wrong audience, instead of getting
111
+ // one opaque code for all of them.
108
112
  jwt_invalid: wire('auth', 401, false, "The bearer JWT failed validation for a reason the server could not classify further. Check the token's issuer, signature, audience, and expiry."),
109
113
  jwt_malformed: wire('auth', 401, false, 'The bearer token is not a well-formed JWT and could not be decoded. Check that the full, unmodified token was sent.'),
110
114
  jwt_missing_issuer: wire('auth', 401, false, 'The bearer JWT has no `iss` (issuer) claim, so it cannot be routed to a trusted issuer.'),
@@ -113,11 +117,10 @@ export const ERROR_CODES = {
113
117
  jwt_audience_mismatch: wire('auth', 401, false, "The bearer JWT's `aud` (audience) claim does not match the audience this issuer is registered with."),
114
118
  jwt_missing_subject: wire('auth', 401, false, 'The bearer JWT has no `sub` (subject) claim to identify the user.'),
115
119
  jwt_missing_organization: wire('auth', 401, false, 'The bearer JWT carries no organization context — neither a fixed org for the issuer nor the configured organization claim.'),
116
- // Trusted-issuer / BYO-IdP path only Ablo's own sync-engine no longer
117
- // authenticates with JWTs (it uses the Stripe-style ephemeral key, below).
118
- // When a customer DOES present an external-IdP JWT, its expiry means
119
- // re-authenticate against that IdP, so it classifies as a session expiry
120
- // (which also keeps `isSessionErrorResponse` behaviour unchanged).
120
+ // Applies only to the trusted-issuer path, where a customer authenticates with
121
+ // a JWT from their own identity provider. When such a token expires, the
122
+ // remedy is to re-authenticate against that provider, so it classifies as a
123
+ // session expiry.
121
124
  jwt_expired: wire('auth', 401, false, 'The bearer JWT has expired. Obtain a fresh token from your identity provider and retry.', 'session_expiry'),
122
125
  jwt_org_membership_denied: wire('auth', 403, false, "The bearer JWT's subject is not an active member of the organization in its `org_id` claim (removed, suspended, or the claim does not match a membership)."),
123
126
  file_upload_auth_required: wire('auth', 401, false, 'File uploads require an authenticated session. Sign in and retry.'),
@@ -136,22 +139,18 @@ export const ERROR_CODES = {
136
139
  database_role_unreadable: wire('permission', 403, false, 'Ablo could not introspect the database role it connects with, so it cannot verify that row-level security is enforced.'),
137
140
  database_tables_unforced_rls: wire('permission', 403, false, 'Some synced tables do not have `FORCE ROW LEVEL SECURITY` applied, so the table owner can bypass row isolation. Run `ALTER TABLE ... FORCE ROW LEVEL SECURITY` on each synced table.'),
138
141
  database_host_not_allowed: wire('permission', 403, false, "The database host resolves to a private, loopback, or link-local address, which Ablo's servers will not connect to. Use a publicly resolvable host."),
139
- // Deprecated spellings of the `database_*` codes above still emitted by
140
- // older servers; kept so they classify identically. Do not use in new code.
142
+ // Older spellings of the `database_*` codes above, still sent by some servers
143
+ // and kept so they classify identically. Prefer the `database_*` codes.
141
144
  byo_role_cannot_enforce_rls: wire('permission', 403, false, 'The direct Postgres connector role cannot enforce row-level security.'),
142
145
  byo_role_unreadable: wire('permission', 403, false, 'The direct Postgres connector role could not be introspected.'),
143
146
  byo_tenant_tables_unforced_rls: wire('permission', 403, false, 'Tenant tables do not have RLS forced under the direct Postgres connector role.'),
144
147
  byo_host_not_allowed: wire('permission', 403, false, 'The direct Postgres connector host resolves to a private, loopback, or link-local address and cannot be used.'),
145
148
  // ── claim / claim conflict (409) ──────────────────────────────────
146
- // Held-claim rejections are NOT queue-retryable (gRPC FAILED_PRECONDITION /
147
- // ABORTED semantics; Replicache/Zero SETTLE a rejected mutation reject the
148
- // caller, roll back the optimistic effect instead of resending it).
149
- // Blindly re-sending the same payload cannot succeed while the lease is
150
- // held, and a lease can outlive any sane retry budget. The correct recovery
151
- // lives at the CALLER: take a claim (`ablo.<model>.claim` queues fairly
152
- // behind the holder) or re-read and rebase. `retryable: true` here turned
153
- // every cross-client claim conflict into an infinite client resend loop
154
- // (~150ms storm — found by the claims journey, 2026-06-10).
149
+ // A rejection because another participant holds a claim is not retryable.
150
+ // Re-sending the same write cannot succeed while the claim is held, and a
151
+ // claim can outlive any reasonable retry budget, so an automatic retry would
152
+ // only loop. Recovery belongs to the caller: take a claim, which queues fairly
153
+ // behind the holder (`ablo.<model>.claim`), or re-read and rebase.
155
154
  claim_conflict: wire('claim', 409, false, 'Another participant holds a claim on this row, so the write was rejected. Take a claim with `ablo.<model>.claim` to queue fairly behind the holder, or re-read and rebase.'),
156
155
  claim_lost: wire('claim', 409, false, 'The claim held on this row was lost before the write could apply. Re-acquire the claim and retry.'),
157
156
  entity_claimed: wire('claim', 409, false, 'This row is currently claimed by another participant, so the write was blocked. Queue behind the holder with `ablo.<model>.claim`, or wait for the claim to clear.'),
@@ -164,9 +163,9 @@ export const ERROR_CODES = {
164
163
  // ── stale context / idempotency (409) ──────────────────────────────
165
164
  stale_context: wire('conflict', 409, true, "The row changed after you read it — the write's `readAt` watermark is older than the current row version. Re-read the row and retry."),
166
165
  // Raised by the functional `update(id, current => next)` form once its
167
- // internal reconcile budget is exhausted the row stayed continuously
168
- // contended. Client-side: the SDK already retried; the caller decides whether
169
- // to back off, raise `retries`, or move the row to the WebSocket transport.
166
+ // internal reconcile budget is exhausted, because the row stayed continuously
167
+ // contended. The SDK has already retried; the caller decides whether to back
168
+ // off, raise `retries`, or move the row to the WebSocket transport.
170
169
  contention_exhausted: client('conflict', 'A functional update kept losing to concurrent writes and exhausted its reconcile budget. Back off and retry, raise `retries`, or move the row to the WebSocket transport.'),
171
170
  update_aborted: client('conflict', 'The functional update was aborted via its `AbortSignal` before the write landed; nothing was written.'),
172
171
  idempotency_conflict: wire('conflict', 409, false, 'This `Idempotency-Key` was already used with a different request body. Reuse a key only to retry an identical request; otherwise generate a new one.'),
@@ -175,21 +174,20 @@ export const ERROR_CODES = {
175
174
  write_options_invalid: client('validation', 'The write options (`idempotencyKey` / `label` / `wait` / `readAt` / `onStale` / `claim`) failed validation against the write-options schema.'),
176
175
  source_operation_id_required: client('validation', 'A data-source operation arrived without the entity `id` it targets.'),
177
176
  source_adapter_misconfigured: client('validation', 'The data-source ORM adapter could not map a schema model onto the backing client — the client exposes no matching delegate or model. Check that the adapter and schema agree on model names.'),
178
- // Wire since 2026-07-01: the sync-server validates every pushed/polled
179
- // source event before appending to the log and rejects the whole batch
180
- // with this code (`param` names the offending index + field path, e.g.
181
- // `events[3].entityId`). Also raised client-side by
182
- // `sourceEventForOperation` when an outbox event cannot be built.
177
+ // The server validates every incoming data-source event before appending it
178
+ // to the log and rejects the whole batch with this code; `param` names the
179
+ // offending index and field path, such as `events[3].entityId`. It is also
180
+ // raised on the client when an outbound source event cannot be built.
183
181
  source_event_invalid: wire('validation', 400, false, 'A data-source event was malformed — missing or invalid id, model, entityId, type, or field value. The whole event batch was rejected and nothing was ingested; fix the offending outbox row and re-send.'),
184
182
  duration_invalid: client('validation', 'A duration value was not a number of seconds or a "500ms" | "30s" | "3m" | "24h" string.'),
185
183
  schema_definition_invalid: client('validation', 'A schema definition value was invalid (bad column identifier, non-finite backfill, or unsupported schema-JSON version).'),
186
184
  cli_invalid_arguments: client('validation', 'The CLI was invoked with an unknown flag or a malformed flag value.'),
187
185
  turn_validation_failed: wire('validation', 422, false, 'The agent turn payload failed server-side validation and was not applied.'),
188
186
  commit_operation_required: wire('validation', 400, false, 'A commit must carry `operation` or `operations`.'),
189
- // Wire since 2026-07-01: both commit transports (WS `commit` frame and HTTP
190
- // `/v1/commits`) validate every operation against `commitOperationSchema`
191
- // (`wire/frames.ts`) and reject the whole batch with this code. `param`
192
- // names the offending index + field path (e.g. `operations[3].readAt`).
187
+ // Both commit transports the WebSocket `commit` frame and the HTTP
188
+ // `/v1/commits` endpoint — validate every operation and reject the whole batch
189
+ // with this code. `param` names the offending index and field path, such as
190
+ // `operations[3].readAt`.
193
191
  commit_operation_invalid: wire('validation', 400, false, 'A commit operation failed validation against the wire commit-operation schema — wrong field type (e.g. a string `readAt`), unknown `type`, or missing `model`. The whole batch was rejected; the error names the offending operation index and field path.'),
194
192
  commit_operation_model_required: wire('validation', 400, false, 'A commit operation is missing its `model`.'),
195
193
  commit_operations_ambiguous: wire('validation', 400, false, 'A commit supplied both `operation` and `operations`. Send one or the other, not both.'),
@@ -206,13 +204,12 @@ export const ERROR_CODES = {
206
204
  model_not_found: wire('not_found', 404, false, 'No row of this model exists with the requested id. It may have been deleted, or the id may belong to a different environment.'),
207
205
  mutate_update_entity_not_found: wire('not_found', 404, false, 'The row targeted by this update does not exist — it may have been deleted since you read it. Re-read before retrying.'),
208
206
  task_id_missing: wire('server', 502, true, 'The task-create response arrived without a task id, so the result cannot be used. Retry the request.'),
209
- // ── data integrity / DB constraints ────────────────────────────────
210
- // Emitted when a write is rejected by a database integrity constraint
211
- // (Postgres class-23). All NON-retryable: the same payload re-sent
212
- // unchanged will fail identically, so the client must roll back, not
213
- // retry. The server normalizer maps SQLSTATE → these codes and tucks the
214
- // raw constraint/column/table detail into `details` rather than leaking
215
- // the driver's message text onto the wire.
207
+ // ── data integrity / database constraints ──────────────────────────
208
+ // Emitted when a database integrity constraint rejects a write. None are
209
+ // retryable: the same payload re-sent unchanged fails identically, so the
210
+ // client must roll back rather than retry. The server maps the underlying SQL
211
+ // constraint to one of these codes and places the raw constraint, column, and
212
+ // table detail in `details` instead of exposing the driver's message text.
216
213
  not_null_violation: wire('validation', 400, false, 'The database rejected the write because a required column was left empty — a not-null constraint. The error details name the column; supply a value and retry.'),
217
214
  foreign_key_violation: wire('conflict', 409, false, 'The database rejected the write on a foreign-key constraint: a referenced row does not exist, or the row being deleted is still referenced by others. The error details name the constraint.'),
218
215
  unique_violation: wire('conflict', 409, false, 'The write duplicates a value that must be unique — another row already holds it. Choose a different value, or update the existing row.'),
@@ -276,17 +273,18 @@ export const ERROR_CODES = {
276
273
  // ── quota / rate limit (429) ──────────────────────────────────────
277
274
  quota_exceeded: wire('rate_limit', 429, true, 'Your organization has used up its configured usage quota. Requests will succeed again once the quota resets or the limit is raised.'),
278
275
  connection_limit_exceeded: wire('rate_limit', 429, true, 'Too many concurrent WebSocket connections for this principal or organization. Close idle connections, or retry once others drain.'),
279
- // Per-CREDENTIAL request-rate limit — the fast (RPS/burst) axis, distinct from
280
- // the slow-axis `quota_exceeded` (org daily/monthly usage). Keyed per API key,
281
- // so one noisy key backs off without affecting the rest of the org. The
282
- // `Retry-After` header carries the bucket-refill delay.
276
+ // A per-key request-rate limit — the fast, requests-per-second axis, as
277
+ // opposed to `quota_exceeded`, which is the slower organization-wide usage
278
+ // limit. It is keyed per API key, so one noisy key backs off without affecting
279
+ // the rest of the organization. The `Retry-After` header carries the delay
280
+ // before the next request is allowed.
283
281
  rate_limit_exceeded: wire('rate_limit', 429, true, 'This API key is sending requests faster than its rate limit allows. Slow down and retry after the delay in the `Retry-After` header.'),
284
282
  // ── server (5xx) ───────────────────────────────────────────────────
285
283
  internal_error: wire('server', 500, true, "Something went wrong on Ablo's side — an unexpected server error. It is safe to retry."),
286
284
  quota_lookup_failed: wire('server', 503, true, "The server could not load this organization's quota state, so the request was rejected rather than admitted unchecked. Retry shortly."),
287
- // The per-key rate-limiter backend (Redis) was unreachable and the API is
288
- // configured to FAIL CLOSED on that path, so the request was rejected rather
289
- // than admitted unchecked. Retryable: the next attempt re-probes the backend.
285
+ // The rate-limiter backend was unreachable and this endpoint is configured to
286
+ // fail closed, so the request was rejected rather than admitted unchecked. It
287
+ // is retryable: the next attempt re-probes the backend.
290
288
  rate_limiter_unavailable: wire('server', 503, true, 'The rate-limiter backend is unavailable and this endpoint is configured to fail closed; retry shortly.'),
291
289
  turn_open_failed: wire('server', 500, true, 'The agent turn could not be opened on the server. It is safe to retry.'),
292
290
  turn_close_failed: wire('server', 500, true, 'The agent turn could not be closed cleanly on the server. It is safe to retry the close.'),
@@ -329,7 +327,7 @@ export const ERROR_CODES = {
329
327
  undo_entry_invalid: client('client', 'An undo entry failed inverse-op schema validation.'),
330
328
  mock_mutation_failed: client('client', 'A mock mutation adapter was configured to fail.'),
331
329
  mock_unsupported_operation: client('client', 'A mock adapter received an unsupported operation.'),
332
- // ── HTTP route edge codes (egress through app.onError) ─────────────
330
+ // ── HTTP route edge codes ──────────────────────────────────────────
333
331
  invalid_body: wire('validation', 400, false, 'The request body was missing, unparseable, or the wrong shape.'),
334
332
  invalid_json: wire('validation', 400, false, 'The request body was not valid JSON.'),
335
333
  capability_id_required: wire('validation', 400, false, 'A capability id is required for this request.'),
@@ -359,6 +357,7 @@ export const ERROR_CODES = {
359
357
  protocol_version_unsupported: wire('transport', 426, false, 'The client sync-protocol version is outside the range this server supports — upgrade the SDK (or the server was rolled back mid-fleet).'),
360
358
  database_unreachable: wire('validation', 400, false, "Ablo could not reach this database to check that it can stream replication. The connection string may be wrong, the host may not be reachable from Ablo's servers, or the credentials may not be accepted."),
361
359
  database_not_replication_ready: wire('validation', 400, false, 'This database is not set up for logical replication yet. Every failing item — wal_level, the publication, the replication grant, a replica identity — is listed in the error details with its exact fix. `ablo connect` prints the one-time setup; `ablo connect --check` verifies it.'),
360
+ replication_publication_drift: wire('validation', 400, false, 'Your schema maps to tables that are not members of the replication publication, so their changes silently never stream and the source looks frozen. The missing tables and the exact `ALTER PUBLICATION … ADD TABLE …` to add them are in the error details — Ablo never alters your database for you.'),
362
361
  query_unknown_relation: wire('validation', 400, false, 'The query references a relation the model does not define. Check the relation name against the schema.'),
363
362
  query_relation_target_unknown: wire('schema', 500, false, 'A relation in the query targets a model the schema does not define.'),
364
363
  query_invalid_identifier: wire('validation', 400, false, 'The query contained an invalid identifier.'),
@@ -390,32 +389,33 @@ export const ERROR_CODES = {
390
389
  invalid_schema: wire('validation', 400, false, 'The submitted schema could not be parsed.'),
391
390
  incompatible_change: wire('conflict', 409, false, 'The schema change is incompatible with the schema currently deployed and cannot be applied as-is.'),
392
391
  };
393
- /** Look up an error code's spec. Returns `undefined` for the dynamic
394
- * `policy:*` family and for any forward-compat code an older SDK doesn't
395
- * yet know. */
392
+ /** Looks up the {@link ErrorCodeSpec} for a code. Returns `undefined` for the
393
+ * dynamic `policy:*` family and for any newer code this client does not yet
394
+ * recognize. */
396
395
  export function errorCodeSpec(code) {
397
396
  return ERROR_CODES[code];
398
397
  }
399
- /** Whether a code's spec marks it retryable. Unknown / dynamic codes
400
- * default to non-retryable (safe default don't auto-retry the unknown). */
398
+ /** Reports whether a code is marked retryable. Unknown and dynamic codes
399
+ * default to non-retryable, so an unrecognized failure is never retried
400
+ * automatically. */
401
401
  export function isRetryableCode(code) {
402
402
  return errorCodeSpec(code)?.retryable ?? false;
403
403
  }
404
404
  /**
405
- * Classify a `code` into its {@link RecoveryClass} — the single discriminant
406
- * the connection FSM and the network probe branch on.
405
+ * Classifies a code into its {@link RecoveryClass} — the single value the
406
+ * connection layer and the network probe branch on to decide how to recover.
407
407
  *
408
- * The registry stays the source of truth: an explicit `spec.recovery` wins
409
- * (set only on the few auth codes whose remedy the status can't reveal), and
410
- * everything else is DERIVED from the spec so the registry stays terse:
411
- * - retryable → `transient`
412
- * - 403 → `permission`
413
- * - residual `auth`-category → `auth_blocked` (the 401 credential-type codes)
414
- * - otherwise / unknown → `none`
408
+ * The registry is the source of truth. An explicit `recovery` on the code's spec
409
+ * wins; it is set only on the few authentication codes whose remedy the HTTP
410
+ * status cannot reveal. Every other code is derived from its spec:
411
+ * - retryable → `transient`
412
+ * - HTTP 403 → `permission`
413
+ * - remaining `auth` category → `auth_blocked` (the credential-type 401s)
414
+ * - anything else, or unknown → `none`
415
415
  *
416
- * Unknown / dynamic `policy:*` / forward-compat codes (`spec === undefined`)
417
- * default to `none`, mirroring {@link isRetryableCode}'s safe default — never
418
- * silently treat an unrecognised code as a credential expiry or a logout.
416
+ * An unknown code, a dynamic `policy:*` code, or a code this client predates
417
+ * (no spec) defaults to `none`, the same safe default as {@link isRetryableCode}:
418
+ * an unrecognized code is never treated as a credential expiry or a sign-out.
419
419
  */
420
420
  export function classifyRecovery(code) {
421
421
  const spec = errorCodeSpec(code);
@@ -432,10 +432,9 @@ export function classifyRecovery(code) {
432
432
  return 'none';
433
433
  }
434
434
  /**
435
- * Compile-time exhaustiveness guard: forces every {@link RecoveryClass} to be
436
- * acknowledged here, so adding a class to {@link RECOVERY_CLASSES} without
437
- * deciding its meaning is a type error rather than a silent gap. (Mirrors the
438
- * closed-union discipline `ERROR_CODES` itself uses via `satisfies`.)
435
+ * A compile-time exhaustiveness guard: it forces every {@link RecoveryClass} to
436
+ * be listed here, so adding a class to {@link RECOVERY_CLASSES} without deciding
437
+ * its meaning is a type error rather than a silent gap.
439
438
  */
440
439
  const _RECOVERY_CLASS_EXHAUSTIVE = {
441
440
  access_credential_expiry: true,