@abloatai/ablo 0.25.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/AGENTS.md +5 -3
  2. package/CHANGELOG.md +34 -0
  3. package/README.md +104 -88
  4. package/dist/BaseSyncedStore.d.ts +140 -266
  5. package/dist/BaseSyncedStore.js +338 -739
  6. package/dist/Database.d.ts +62 -77
  7. package/dist/Database.js +106 -127
  8. package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
  9. package/dist/{ObjectPool.js → InstanceCache.js} +91 -83
  10. package/dist/LazyReferenceCollection.d.ts +11 -15
  11. package/dist/LazyReferenceCollection.js +16 -15
  12. package/dist/Model.d.ts +37 -52
  13. package/dist/Model.js +52 -69
  14. package/dist/ModelRegistry.d.ts +46 -25
  15. package/dist/ModelRegistry.js +32 -30
  16. package/dist/NetworkMonitor.d.ts +5 -6
  17. package/dist/NetworkMonitor.js +6 -7
  18. package/dist/SyncClient.d.ts +119 -109
  19. package/dist/SyncClient.js +303 -224
  20. package/dist/SyncEngineContext.d.ts +1 -3
  21. package/dist/SyncEngineContext.js +1 -2
  22. package/dist/adapters/alwaysOnline.d.ts +6 -8
  23. package/dist/adapters/alwaysOnline.js +6 -8
  24. package/dist/adapters/inMemoryStorage.d.ts +9 -9
  25. package/dist/adapters/inMemoryStorage.js +9 -9
  26. package/dist/agent/Agent.d.ts +39 -31
  27. package/dist/agent/Agent.js +35 -23
  28. package/dist/agent/index.d.ts +4 -4
  29. package/dist/agent/index.js +5 -5
  30. package/dist/agent/session.d.ts +47 -44
  31. package/dist/agent/session.js +37 -48
  32. package/dist/agent/types.d.ts +26 -31
  33. package/dist/agent/types.js +6 -7
  34. package/dist/ai-sdk/coordinatedTool.d.ts +108 -0
  35. package/dist/ai-sdk/{coordinated-tool.js → coordinatedTool.js} +44 -38
  36. package/dist/ai-sdk/coordinationContext.d.ts +46 -0
  37. package/dist/ai-sdk/{coordination-context.js → coordinationContext.js} +30 -31
  38. package/dist/ai-sdk/index.d.ts +25 -22
  39. package/dist/ai-sdk/index.js +25 -22
  40. package/dist/ai-sdk/wrap.d.ts +7 -8
  41. package/dist/ai-sdk/wrap.js +2 -2
  42. package/dist/auth/credentialPolicy.d.ts +74 -71
  43. package/dist/auth/credentialPolicy.js +51 -56
  44. package/dist/auth/credentialSource.d.ts +7 -18
  45. package/dist/auth/credentialSource.js +10 -18
  46. package/dist/auth/index.d.ts +59 -58
  47. package/dist/auth/index.js +34 -40
  48. package/dist/auth/schemas.d.ts +5 -4
  49. package/dist/auth/schemas.js +5 -4
  50. package/dist/batching/index.d.ts +19 -21
  51. package/dist/batching/index.js +14 -17
  52. package/dist/cli.cjs +483 -369
  53. package/dist/client/Ablo.d.ts +107 -836
  54. package/dist/client/Ablo.js +174 -833
  55. package/dist/client/ApiClient.d.ts +44 -20
  56. package/dist/client/ApiClient.js +193 -44
  57. package/dist/client/auth.d.ts +51 -60
  58. package/dist/client/auth.js +137 -110
  59. package/dist/client/claimHeartbeatLoop.d.ts +50 -0
  60. package/dist/client/claimHeartbeatLoop.js +88 -0
  61. package/dist/client/consoleLogger.d.ts +35 -0
  62. package/dist/client/consoleLogger.js +44 -0
  63. package/dist/client/createInternalComponents.d.ts +14 -17
  64. package/dist/client/createInternalComponents.js +26 -31
  65. package/dist/client/createModelProxy.d.ts +130 -120
  66. package/dist/client/createModelProxy.js +158 -124
  67. package/dist/client/credentialEndpoint.d.ts +61 -0
  68. package/dist/client/credentialEndpoint.js +86 -0
  69. package/dist/client/functionalUpdate.d.ts +29 -27
  70. package/dist/client/functionalUpdate.js +21 -21
  71. package/dist/client/hostedEndpoints.d.ts +21 -0
  72. package/dist/client/hostedEndpoints.js +21 -0
  73. package/dist/client/httpClient.d.ts +58 -54
  74. package/dist/client/httpClient.js +29 -31
  75. package/dist/client/identity.d.ts +15 -20
  76. package/dist/client/identity.js +49 -59
  77. package/dist/client/modelRegistration.d.ts +10 -0
  78. package/dist/client/modelRegistration.js +301 -0
  79. package/dist/client/options.d.ts +373 -0
  80. package/dist/client/options.js +6 -0
  81. package/dist/client/registerDataSource.d.ts +9 -9
  82. package/dist/client/registerDataSource.js +15 -16
  83. package/dist/client/resourceTypes.d.ts +333 -0
  84. package/dist/client/resourceTypes.js +7 -0
  85. package/dist/client/schemaConfig.d.ts +44 -0
  86. package/dist/client/schemaConfig.js +176 -0
  87. package/dist/client/sessionMint.d.ts +17 -13
  88. package/dist/client/sessionMint.js +26 -31
  89. package/dist/client/validateAbloOptions.d.ts +12 -14
  90. package/dist/client/validateAbloOptions.js +9 -10
  91. package/dist/client/writeOptionsSchema.d.ts +18 -16
  92. package/dist/client/writeOptionsSchema.js +23 -20
  93. package/dist/client/wsMutationExecutor.d.ts +28 -0
  94. package/dist/client/wsMutationExecutor.js +71 -0
  95. package/dist/context.d.ts +6 -4
  96. package/dist/context.js +6 -7
  97. package/dist/coordination/index.d.ts +13 -4
  98. package/dist/coordination/index.js +29 -4
  99. package/dist/coordination/schema.d.ts +176 -128
  100. package/dist/coordination/schema.js +197 -133
  101. package/dist/coordination/trace.d.ts +9 -11
  102. package/dist/coordination/trace.js +13 -15
  103. package/dist/core/DatabaseManager.d.ts +5 -8
  104. package/dist/core/DatabaseManager.js +38 -40
  105. package/dist/core/QueryProcessor.d.ts +7 -9
  106. package/dist/core/QueryProcessor.js +27 -34
  107. package/dist/core/QueryView.d.ts +17 -5
  108. package/dist/core/QueryView.js +6 -7
  109. package/dist/core/StoreManager.d.ts +14 -16
  110. package/dist/core/StoreManager.js +26 -25
  111. package/dist/core/ViewRegistry.d.ts +5 -5
  112. package/dist/core/ViewRegistry.js +4 -4
  113. package/dist/core/index.d.ts +18 -13
  114. package/dist/core/index.js +32 -26
  115. package/dist/core/openIDBWithTimeout.d.ts +38 -36
  116. package/dist/core/openIDBWithTimeout.js +57 -54
  117. package/dist/core/queryUtils.d.ts +45 -0
  118. package/dist/core/queryUtils.js +69 -0
  119. package/dist/core/storeContract.d.ts +145 -0
  120. package/dist/core/storeContract.js +12 -0
  121. package/dist/environment.d.ts +28 -0
  122. package/dist/environment.js +21 -0
  123. package/dist/errorCodes.d.ts +118 -101
  124. package/dist/errorCodes.js +277 -260
  125. package/dist/errors.d.ts +170 -165
  126. package/dist/errors.js +161 -151
  127. package/dist/index.d.ts +30 -27
  128. package/dist/index.js +90 -82
  129. package/dist/interfaces/index.d.ts +108 -133
  130. package/dist/interfaces/index.js +5 -4
  131. package/dist/keys/index.d.ts +27 -29
  132. package/dist/keys/index.js +59 -49
  133. package/dist/mutators/RecordingTransaction.d.ts +16 -16
  134. package/dist/mutators/RecordingTransaction.js +31 -37
  135. package/dist/mutators/Transaction.d.ts +18 -26
  136. package/dist/mutators/Transaction.js +14 -20
  137. package/dist/mutators/UndoManager.d.ts +122 -131
  138. package/dist/mutators/UndoManager.js +149 -155
  139. package/dist/mutators/defineMutators.d.ts +24 -37
  140. package/dist/mutators/defineMutators.js +14 -20
  141. package/dist/mutators/inverseOp.d.ts +12 -15
  142. package/dist/mutators/inverseOp.js +12 -15
  143. package/dist/mutators/mutateActions.d.ts +10 -9
  144. package/dist/mutators/mutateActions.js +1 -1
  145. package/dist/mutators/readerActions.d.ts +9 -8
  146. package/dist/mutators/readerActions.js +2 -2
  147. package/dist/mutators/undoApply.d.ts +31 -27
  148. package/dist/mutators/undoApply.js +26 -24
  149. package/dist/policy/index.d.ts +5 -3
  150. package/dist/policy/index.js +5 -3
  151. package/dist/policy/types.d.ts +105 -101
  152. package/dist/policy/types.js +67 -66
  153. package/dist/query/client.d.ts +32 -16
  154. package/dist/query/client.js +103 -72
  155. package/dist/query/types.d.ts +37 -60
  156. package/dist/query/types.js +13 -33
  157. package/dist/react/AbloProvider.d.ts +7 -11
  158. package/dist/react/AbloProvider.js +24 -17
  159. package/dist/react/context.d.ts +27 -146
  160. package/dist/react/context.js +9 -10
  161. package/dist/react/index.d.ts +41 -42
  162. package/dist/react/index.js +37 -38
  163. package/dist/react/internalContext.d.ts +17 -19
  164. package/dist/react/useAblo.d.ts +23 -22
  165. package/dist/react/useAblo.js +17 -15
  166. package/dist/react/useCurrentUserId.d.ts +8 -7
  167. package/dist/react/useCurrentUserId.js +8 -7
  168. package/dist/react/useErrorListener.d.ts +7 -7
  169. package/dist/react/useErrorListener.js +11 -12
  170. package/dist/react/useMutationFailureListener.d.ts +8 -8
  171. package/dist/react/useMutationFailureListener.js +9 -9
  172. package/dist/react/useMutators.d.ts +11 -11
  173. package/dist/react/useMutators.js +10 -4
  174. package/dist/react/useReactive.js +2 -3
  175. package/dist/react/useSyncStatus.d.ts +4 -6
  176. package/dist/react/useUndoScope.d.ts +7 -9
  177. package/dist/react/useUndoScope.js +3 -3
  178. package/dist/schema/coordination.d.ts +21 -25
  179. package/dist/schema/coordination.js +21 -25
  180. package/dist/schema/ddl.d.ts +43 -39
  181. package/dist/schema/ddl.js +75 -68
  182. package/dist/schema/ddlLock.d.ts +35 -0
  183. package/dist/schema/ddlLock.js +46 -0
  184. package/dist/schema/diff.d.ts +99 -61
  185. package/dist/schema/diff.js +43 -34
  186. package/dist/schema/field.d.ts +37 -42
  187. package/dist/schema/field.js +36 -49
  188. package/dist/schema/generate.d.ts +12 -12
  189. package/dist/schema/generate.js +12 -12
  190. package/dist/schema/index.d.ts +5 -4
  191. package/dist/schema/index.js +29 -21
  192. package/dist/schema/model.d.ts +121 -146
  193. package/dist/schema/model.js +24 -35
  194. package/dist/schema/openapi.d.ts +10 -9
  195. package/dist/schema/openapi.js +7 -1
  196. package/dist/schema/queries.d.ts +30 -32
  197. package/dist/schema/queries.js +24 -25
  198. package/dist/schema/relation.d.ts +89 -99
  199. package/dist/schema/relation.js +13 -13
  200. package/dist/schema/residency.d.ts +38 -0
  201. package/dist/schema/residency.js +30 -0
  202. package/dist/schema/roles.d.ts +45 -27
  203. package/dist/schema/roles.js +52 -21
  204. package/dist/schema/schema.d.ts +36 -45
  205. package/dist/schema/schema.js +42 -39
  206. package/dist/schema/select.d.ts +13 -13
  207. package/dist/schema/select.js +13 -13
  208. package/dist/schema/serialize.d.ts +36 -39
  209. package/dist/schema/serialize.js +27 -31
  210. package/dist/schema/sugar.d.ts +17 -32
  211. package/dist/schema/sugar.js +14 -29
  212. package/dist/schema/{sync-delta-row.d.ts → syncDeltaRow.d.ts} +27 -50
  213. package/dist/schema/syncDeltaRow.js +89 -0
  214. package/dist/schema/tenancy.d.ts +44 -46
  215. package/dist/schema/tenancy.js +46 -48
  216. package/dist/server/adapter.d.ts +58 -58
  217. package/dist/server/adapter.js +13 -14
  218. package/dist/server/commit.d.ts +60 -64
  219. package/dist/server/index.d.ts +9 -10
  220. package/dist/server/index.js +1 -1
  221. package/dist/server/readConfig.d.ts +70 -0
  222. package/dist/server/readConfig.js +8 -0
  223. package/dist/server/storageMode.d.ts +23 -0
  224. package/dist/server/storageMode.js +17 -0
  225. package/dist/source/adapter.d.ts +31 -26
  226. package/dist/source/adapter.js +10 -10
  227. package/dist/source/adapters/drizzle.d.ts +28 -23
  228. package/dist/source/adapters/drizzle.js +34 -28
  229. package/dist/source/adapters/kysely.d.ts +27 -25
  230. package/dist/source/adapters/kysely.js +28 -26
  231. package/dist/source/adapters/memory.d.ts +8 -7
  232. package/dist/source/adapters/memory.js +10 -9
  233. package/dist/source/adapters/prisma.d.ts +13 -12
  234. package/dist/source/adapters/prisma.js +27 -29
  235. package/dist/source/conformance.d.ts +18 -11
  236. package/dist/source/conformance.js +27 -19
  237. package/dist/source/connector.d.ts +31 -32
  238. package/dist/source/connector.js +30 -28
  239. package/dist/source/connectorProtocol.d.ts +160 -0
  240. package/dist/source/connectorProtocol.js +162 -0
  241. package/dist/source/contract.d.ts +26 -27
  242. package/dist/source/contract.js +28 -29
  243. package/dist/source/factory.d.ts +94 -0
  244. package/dist/source/factory.js +268 -0
  245. package/dist/source/index.d.ts +10 -462
  246. package/dist/source/index.js +17 -421
  247. package/dist/source/migrations.d.ts +9 -9
  248. package/dist/source/migrations.js +9 -9
  249. package/dist/source/next.d.ts +10 -11
  250. package/dist/source/next.js +7 -8
  251. package/dist/source/pushQueue.d.ts +70 -48
  252. package/dist/source/pushQueue.js +36 -29
  253. package/dist/source/signing.d.ts +88 -0
  254. package/dist/source/signing.js +159 -0
  255. package/dist/source/types.d.ts +351 -0
  256. package/dist/source/types.js +43 -0
  257. package/dist/stores/ObjectStore.d.ts +11 -12
  258. package/dist/stores/ObjectStore.js +34 -35
  259. package/dist/stores/ObjectStoreContract.d.ts +12 -15
  260. package/dist/stores/SyncActionStore.d.ts +8 -12
  261. package/dist/stores/SyncActionStore.js +77 -46
  262. package/dist/surface.d.ts +28 -21
  263. package/dist/surface.js +28 -20
  264. package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +37 -45
  265. package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +101 -80
  266. package/dist/sync/ConnectionManager.d.ts +47 -50
  267. package/dist/sync/ConnectionManager.js +74 -70
  268. package/dist/sync/NetworkProbe.d.ts +27 -31
  269. package/dist/sync/NetworkProbe.js +67 -72
  270. package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +49 -36
  271. package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +79 -54
  272. package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +45 -59
  273. package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +44 -52
  274. package/dist/sync/SyncWebSocket.d.ts +175 -250
  275. package/dist/sync/SyncWebSocket.js +431 -769
  276. package/dist/sync/awaitClaimGrant.d.ts +18 -18
  277. package/dist/sync/awaitClaimGrant.js +38 -30
  278. package/dist/sync/bootstrapApply.d.ts +70 -0
  279. package/dist/sync/bootstrapApply.js +73 -0
  280. package/dist/sync/commitFrames.d.ts +44 -0
  281. package/dist/sync/commitFrames.js +94 -0
  282. package/dist/sync/createClaimStream.d.ts +23 -22
  283. package/dist/sync/createClaimStream.js +108 -25
  284. package/dist/sync/createPresenceStream.d.ts +19 -18
  285. package/dist/sync/createPresenceStream.js +25 -26
  286. package/dist/sync/createSnapshot.d.ts +13 -17
  287. package/dist/sync/createSnapshot.js +20 -26
  288. package/dist/sync/credentialLifecycle.d.ts +175 -0
  289. package/dist/sync/credentialLifecycle.js +322 -0
  290. package/dist/sync/deltaPipeline.d.ts +113 -0
  291. package/dist/sync/deltaPipeline.js +261 -0
  292. package/dist/sync/groupChange.d.ts +113 -0
  293. package/dist/sync/groupChange.js +242 -0
  294. package/dist/sync/heartbeat.d.ts +63 -0
  295. package/dist/sync/heartbeat.js +91 -0
  296. package/dist/sync/participants.d.ts +27 -27
  297. package/dist/sync/schemas.d.ts +3 -2
  298. package/dist/sync/schemas.js +14 -10
  299. package/dist/sync/syncCursor.d.ts +40 -0
  300. package/dist/sync/syncCursor.js +55 -0
  301. package/dist/sync/syncPlan.d.ts +54 -0
  302. package/dist/sync/syncPlan.js +50 -0
  303. package/dist/sync/syncPosition.d.ts +54 -49
  304. package/dist/sync/syncPosition.js +57 -52
  305. package/dist/sync/wsFrameHandlers.d.ts +116 -0
  306. package/dist/sync/wsFrameHandlers.js +374 -0
  307. package/dist/testing/fixtures/bootstrap.d.ts +21 -17
  308. package/dist/testing/fixtures/bootstrap.js +12 -6
  309. package/dist/testing/fixtures/deltas.d.ts +31 -34
  310. package/dist/testing/fixtures/deltas.js +30 -33
  311. package/dist/testing/fixtures/models.d.ts +11 -10
  312. package/dist/testing/fixtures/models.js +12 -10
  313. package/dist/testing/helpers/{react-wrapper.d.ts → reactWrapper.d.ts} +13 -10
  314. package/dist/testing/helpers/{react-wrapper.js → reactWrapper.js} +15 -12
  315. package/dist/testing/helpers/{sync-engine-harness.d.ts → syncEngineHarness.d.ts} +17 -18
  316. package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +14 -11
  317. package/dist/testing/helpers/wait.d.ts +13 -8
  318. package/dist/testing/helpers/wait.js +13 -8
  319. package/dist/testing/index.d.ts +4 -4
  320. package/dist/testing/index.js +3 -3
  321. package/dist/testing/mocks/MockMutationExecutor.d.ts +18 -17
  322. package/dist/testing/mocks/MockMutationExecutor.js +15 -14
  323. package/dist/testing/mocks/MockNetworkMonitor.d.ts +8 -8
  324. package/dist/testing/mocks/MockNetworkMonitor.js +8 -8
  325. package/dist/testing/mocks/MockSyncContext.d.ts +21 -34
  326. package/dist/testing/mocks/MockSyncContext.js +16 -45
  327. package/dist/testing/mocks/MockSyncStore.d.ts +14 -14
  328. package/dist/testing/mocks/MockSyncStore.js +11 -11
  329. package/dist/testing/mocks/MockWebSocket.d.ts +28 -24
  330. package/dist/testing/mocks/MockWebSocket.js +22 -21
  331. package/dist/transactions/TransactionQueue.d.ts +190 -221
  332. package/dist/transactions/TransactionQueue.js +424 -822
  333. package/dist/transactions/TransactionStore.d.ts +20 -0
  334. package/dist/transactions/TransactionStore.js +53 -0
  335. package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
  336. package/dist/transactions/UnconfirmedWrites.js +104 -0
  337. package/dist/transactions/coalesceRules.d.ts +58 -0
  338. package/dist/transactions/coalesceRules.js +140 -0
  339. package/dist/transactions/commitPayload.d.ts +130 -0
  340. package/dist/transactions/commitPayload.js +143 -0
  341. package/dist/transactions/deltaConfirmation.d.ts +58 -0
  342. package/dist/transactions/deltaConfirmation.js +215 -0
  343. package/dist/transactions/optimisticApply.d.ts +49 -0
  344. package/dist/transactions/optimisticApply.js +65 -0
  345. package/dist/transactions/replayValidation.d.ts +99 -0
  346. package/dist/transactions/replayValidation.js +111 -0
  347. package/dist/types/global.d.ts +46 -41
  348. package/dist/types/global.js +20 -19
  349. package/dist/types/index.d.ts +74 -80
  350. package/dist/types/index.js +22 -27
  351. package/dist/types/modelData.d.ts +10 -0
  352. package/dist/types/modelData.js +9 -0
  353. package/dist/types/participant.d.ts +20 -0
  354. package/dist/types/participant.js +10 -0
  355. package/dist/types/streams.d.ts +216 -209
  356. package/dist/types/streams.js +7 -7
  357. package/dist/utils/asyncIterator.d.ts +25 -32
  358. package/dist/utils/asyncIterator.js +25 -32
  359. package/dist/utils/duration.d.ts +12 -15
  360. package/dist/utils/duration.js +12 -15
  361. package/dist/utils/mobxSetup.d.ts +53 -0
  362. package/dist/utils/{mobx-setup.js → mobxSetup.js} +44 -100
  363. package/dist/webhooks/events.d.ts +21 -16
  364. package/dist/webhooks/events.js +10 -8
  365. package/dist/webhooks/index.d.ts +5 -7
  366. package/dist/webhooks/index.js +5 -7
  367. package/dist/{schema/sync-delta-wire.d.ts → wire/delta.d.ts} +58 -41
  368. package/dist/wire/delta.js +114 -0
  369. package/dist/wire/errorEnvelope.d.ts +35 -27
  370. package/dist/wire/errorEnvelope.js +38 -32
  371. package/dist/wire/frames.d.ts +150 -67
  372. package/dist/wire/frames.js +48 -1
  373. package/dist/wire/index.d.ts +18 -13
  374. package/dist/wire/index.js +36 -13
  375. package/dist/wire/listEnvelope.d.ts +16 -23
  376. package/dist/wire/listEnvelope.js +7 -6
  377. package/dist/wire/protocol.d.ts +38 -0
  378. package/dist/wire/protocol.js +38 -0
  379. package/dist/wire/protocolVersion.d.ts +60 -0
  380. package/dist/wire/protocolVersion.js +67 -0
  381. package/docs/api-keys.md +4 -3
  382. package/docs/coordination.md +59 -0
  383. package/docs/examples/existing-python-backend.md +3 -3
  384. package/docs/identity.md +4 -4
  385. package/docs/integration-guide.md +1 -1
  386. package/docs/react.md +1 -1
  387. package/docs/sessions.md +5 -7
  388. package/package.json +24 -21
  389. package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
  390. package/dist/ai-sdk/coordination-context.d.ts +0 -52
  391. package/dist/client/index.d.ts +0 -36
  392. package/dist/client/index.js +0 -33
  393. package/dist/config/index.d.ts +0 -10
  394. package/dist/config/index.js +0 -12
  395. package/dist/core/query-utils.d.ts +0 -34
  396. package/dist/core/query-utils.js +0 -59
  397. package/dist/interfaces/headless.d.ts +0 -95
  398. package/dist/interfaces/headless.js +0 -41
  399. package/dist/query/index.d.ts +0 -6
  400. package/dist/query/index.js +0 -5
  401. package/dist/realtime/index.d.ts +0 -10
  402. package/dist/realtime/index.js +0 -9
  403. package/dist/schema/plane.d.ts +0 -23
  404. package/dist/schema/plane.js +0 -19
  405. package/dist/schema/sync-delta-row.js +0 -103
  406. package/dist/schema/sync-delta-wire.js +0 -102
  407. package/dist/server/next.d.ts +0 -51
  408. package/dist/server/next.js +0 -47
  409. package/dist/server/read-config.d.ts +0 -67
  410. package/dist/server/read-config.js +0 -8
  411. package/dist/server/storage-mode.d.ts +0 -1
  412. package/dist/server/storage-mode.js +0 -18
  413. package/dist/source/connector-protocol.d.ts +0 -159
  414. package/dist/source/connector-protocol.js +0 -161
  415. package/dist/sync/OfflineFlush.d.ts +0 -9
  416. package/dist/sync/OfflineFlush.js +0 -22
  417. package/dist/sync/OfflineTransactionStore.d.ts +0 -37
  418. package/dist/sync/OfflineTransactionStore.js +0 -263
  419. package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
  420. package/dist/transactions/OptimisticEchoTracker.js +0 -104
  421. package/dist/transactions/index.d.ts +0 -16
  422. package/dist/transactions/index.js +0 -7
  423. package/dist/transactions/mutation-error-handler.d.ts +0 -5
  424. package/dist/transactions/mutation-error-handler.js +0 -39
  425. package/dist/utils/mobx-setup.d.ts +0 -42
@@ -1,100 +1,122 @@
1
1
  /**
2
- * Customer-side push retry queue.
2
+ * A durable retry queue for delivering source events to the server.
3
3
  *
4
- * The push path (`POST /api/source/events` on Ablo Cloud) acks
5
- * synchronously. If the customer's app crashes mid-call, the network
6
- * drops, or Ablo returns 5xx, those events would otherwise be lost
7
- * the poll path is the durability backstop, but it's higher
8
- * latency.
4
+ * When your application changes a row, it can push the resulting event to
5
+ * the server, which acknowledges delivery synchronously. If your process
6
+ * crashes mid-call, the network drops, or the server returns a 5xx, that
7
+ * event would be lost without a safety net. This queue is the safety net:
8
+ * it persists each event first, then delivers it from a background worker
9
+ * with automatic retries. (Polling the change feed is the slower fallback
10
+ * for anything the queue never manages to deliver.)
9
11
  *
10
- * `PushQueue` lives in the customer's process and gives them a
11
- * queue+worker pattern matching Stripe / Svix semantics:
12
+ * The queue follows a familiar enqueue-and-worker shape:
12
13
  *
13
- * - `enqueue(events)` returns immediately after persisting
14
- * - background worker delivers and retries per the Standard
15
- * Webhooks schedule (0, 5s, 5m, 30m, 2h, 5h, 10h, 14h, 20h, 24h
16
- * — ~3 days total)
17
- * - exhausted items move to DLQ for customer-owned monitoring
14
+ * - `enqueue(events)` returns as soon as the events are persisted.
15
+ * - A background worker delivers them and retries on failure, following
16
+ * the Standard Webhooks schedule (0, 5s, 5m, 30m, 2h, 5h, 10h, 14h,
17
+ * 20h, 24h roughly three days in total).
18
+ * - Items that exhaust every retry move to a dead-letter queue you can
19
+ * monitor.
18
20
  *
19
- * Persistence is pluggable `InMemoryPushQueueStorage` for single-
20
- * process customers, a SQL implementation against the customer's own
21
- * outbox table for production.
21
+ * Persistence is pluggable through {@link PushQueueStorage}. Use
22
+ * {@link InMemoryPushQueueStorage} for a single process, or implement that
23
+ * interface against your own outbox table for production durability.
22
24
  */
23
- import { type SourceEvent } from './index.js';
25
+ import type { SourceEvent } from './types.js';
26
+ /** One queued delivery: a batch of source events plus its retry bookkeeping. */
24
27
  export interface PushQueueItem {
25
28
  readonly id: string;
26
29
  readonly events: readonly SourceEvent[];
27
30
  readonly attempts: number;
28
- /** Timestamp (ms) of the next attempt. Workers skip earlier items. */
31
+ /** When the next attempt is due, in epoch milliseconds. The worker skips items due later. */
29
32
  readonly nextAttemptAt: number;
30
- /** Most recent error message, when any attempt has failed. */
33
+ /** The most recent error message, set once an attempt has failed. */
31
34
  readonly lastError?: string;
32
- /** `dlq` once retries exhausted. */
35
+ /** `pending` while awaiting delivery, `delivered` on success, `dlq` once retries are exhausted. */
33
36
  readonly status: 'pending' | 'delivered' | 'dlq';
34
37
  }
38
+ /**
39
+ * The persistence behind a {@link PushQueue}. Implement it against your own
40
+ * durable table (or use {@link InMemoryPushQueueStorage}) so queued events
41
+ * survive a process restart. The queue calls these methods; you decide where
42
+ * the rows actually live.
43
+ */
35
44
  export interface PushQueueStorage {
36
45
  /**
37
- * Append a new item; returns the persisted record. Implementations
38
- * generate a stable id (used as the `webhook-id`) and set
39
- * `nextAttemptAt = now`.
46
+ * Append a new item and return the persisted record. Generate a stable id
47
+ * it doubles as the `webhook-id` on the delivered request — and set
48
+ * `nextAttemptAt` to the current time so the item is due immediately.
40
49
  */
41
50
  enqueue(events: readonly SourceEvent[]): Promise<PushQueueItem>;
42
- /** Items whose `nextAttemptAt <= now` and `status === 'pending'`. */
51
+ /** Return pending items whose `nextAttemptAt` is at or before `now`, up to `limit`. */
43
52
  due(now: number, limit: number): Promise<readonly PushQueueItem[]>;
44
- /** Bump attempt count + reschedule. */
53
+ /** Increase the attempt count and set the next attempt time after a failed delivery. */
45
54
  reschedule(id: string, nextAttemptAt: number, lastError: string): Promise<void>;
46
- /** Mark the item delivered (no further attempts). */
55
+ /** Mark the item delivered so no further attempts are made. */
47
56
  markDelivered(id: string): Promise<void>;
48
- /** Mark the item DLQ (retries exhausted). */
57
+ /** Move the item to the dead-letter queue after its retries are exhausted. */
49
58
  markDlq(id: string, lastError: string): Promise<void>;
50
- /** Read DLQ contents customer monitors this. */
59
+ /** Read the dead-letter queue. Your monitoring reads this to surface deliveries that never succeeded. */
51
60
  listDlq(): Promise<readonly PushQueueItem[]>;
52
61
  }
53
62
  /**
54
- * Standard Webhooks retry schedule. Index = attempt number; value =
55
- * delay-ms after the previous attempt. After the last entry, items
56
- * move to DLQ.
63
+ * The default retry schedule, taken from the Standard Webhooks
64
+ * specification. The index is the attempt number and the value is the delay
65
+ * in milliseconds after the previous attempt failed. Once an item runs off
66
+ * the end of this array, it moves to the dead-letter queue.
57
67
  *
58
- * Source: https://www.standardwebhooks.com/
68
+ * See https://www.standardwebhooks.com/.
59
69
  */
60
70
  export declare const STANDARD_WEBHOOKS_RETRY_SCHEDULE: readonly number[];
71
+ /** Configuration for {@link createPushQueue}. */
61
72
  export interface PushQueueOptions {
73
+ /** The URL the worker delivers events to. */
62
74
  readonly endpoint: string;
75
+ /** The API key used to sign each delivery. */
63
76
  readonly apiKey: string;
77
+ /** Where queued items are persisted. */
64
78
  readonly storage: PushQueueStorage;
65
79
  /**
66
- * Override the retry delays. Default: Standard Webhooks schedule.
67
- * The number of attempts equals the array length; the i-th entry
68
- * is the delay after attempt `i` failed.
80
+ * Override the retry delays. Defaults to {@link STANDARD_WEBHOOKS_RETRY_SCHEDULE}.
81
+ * The number of attempts equals the array length, and the i-th entry is the
82
+ * delay after attempt `i` failed.
69
83
  */
70
84
  readonly retrySchedule?: readonly number[];
71
- /** Worker poll interval. Default 1000ms. */
85
+ /** How often the worker checks for due items, in milliseconds. Defaults to 1000. */
72
86
  readonly tickIntervalMs?: number;
73
- /** Max items pulled per tick. Default 50. */
87
+ /** The most items the worker delivers per tick. Defaults to 50. */
74
88
  readonly batchSize?: number;
75
- /** Pluggable for tests / non-Node fetch impls. */
89
+ /** A custom fetch implementation, for tests or runtimes without a global `fetch`. */
76
90
  readonly fetch?: typeof fetch;
77
- /** Pluggable for tests. */
91
+ /** A custom clock source, mainly for tests. Defaults to `Date.now`. */
78
92
  readonly now?: () => number;
79
- /** Random jitter on retry delays. Default ±10%. Set to 0 to disable. */
93
+ /** Random jitter applied to each retry delay, as a fraction. Defaults to ±10%; set 0 to disable. */
80
94
  readonly jitter?: number;
95
+ /** Called when an item is dead-lettered or the worker loop hits an error. */
81
96
  readonly onError?: (item: PushQueueItem, err: unknown) => void;
82
97
  }
98
+ /** A running push queue: persist events, deliver them, and recover dead-lettered ones. */
83
99
  export interface PushQueue {
100
+ /** Persist a batch of events for delivery and return the queued item. */
84
101
  enqueue(events: readonly SourceEvent[]): Promise<PushQueueItem>;
85
- /** Run the worker loop until `signal` aborts. */
102
+ /** Run the delivery worker until `signal` aborts. */
86
103
  run(signal: AbortSignal): Promise<void>;
87
- /** Drain the DLQ by re-enqueueing — customer-triggered redrive. */
104
+ /**
105
+ * Re-enqueue every dead-lettered item for another round of delivery. Call
106
+ * this yourself once you have fixed whatever caused the failures. Returns
107
+ * the number of items re-enqueued.
108
+ */
88
109
  redriveDlq(): Promise<number>;
89
110
  }
111
+ /** Create a {@link PushQueue} from the given {@link PushQueueOptions}. */
90
112
  export declare function createPushQueue(options: PushQueueOptions): PushQueue;
113
+ /**
114
+ * A {@link PushQueueStorage} that keeps items in memory. It is not durable —
115
+ * items are lost when the process restarts — so it suits development and
116
+ * low-volume use. For production, implement {@link PushQueueStorage} against
117
+ * your own table.
118
+ */
91
119
  export declare class InMemoryPushQueueStorage implements PushQueueStorage {
92
- /**
93
- * Real implementation, not a mock. Suitable for low-volume single-
94
- * process customers; not durable across restarts (in-flight items
95
- * are lost). Production customers should swap in a SQL-backed
96
- * storage that writes to their existing outbox table.
97
- */
98
120
  private items;
99
121
  private nextId;
100
122
  private readonly now;
@@ -1,32 +1,35 @@
1
1
  /**
2
- * Customer-side push retry queue.
2
+ * A durable retry queue for delivering source events to the server.
3
3
  *
4
- * The push path (`POST /api/source/events` on Ablo Cloud) acks
5
- * synchronously. If the customer's app crashes mid-call, the network
6
- * drops, or Ablo returns 5xx, those events would otherwise be lost
7
- * the poll path is the durability backstop, but it's higher
8
- * latency.
4
+ * When your application changes a row, it can push the resulting event to
5
+ * the server, which acknowledges delivery synchronously. If your process
6
+ * crashes mid-call, the network drops, or the server returns a 5xx, that
7
+ * event would be lost without a safety net. This queue is the safety net:
8
+ * it persists each event first, then delivers it from a background worker
9
+ * with automatic retries. (Polling the change feed is the slower fallback
10
+ * for anything the queue never manages to deliver.)
9
11
  *
10
- * `PushQueue` lives in the customer's process and gives them a
11
- * queue+worker pattern matching Stripe / Svix semantics:
12
+ * The queue follows a familiar enqueue-and-worker shape:
12
13
  *
13
- * - `enqueue(events)` returns immediately after persisting
14
- * - background worker delivers and retries per the Standard
15
- * Webhooks schedule (0, 5s, 5m, 30m, 2h, 5h, 10h, 14h, 20h, 24h
16
- * — ~3 days total)
17
- * - exhausted items move to DLQ for customer-owned monitoring
14
+ * - `enqueue(events)` returns as soon as the events are persisted.
15
+ * - A background worker delivers them and retries on failure, following
16
+ * the Standard Webhooks schedule (0, 5s, 5m, 30m, 2h, 5h, 10h, 14h,
17
+ * 20h, 24h roughly three days in total).
18
+ * - Items that exhaust every retry move to a dead-letter queue you can
19
+ * monitor.
18
20
  *
19
- * Persistence is pluggable `InMemoryPushQueueStorage` for single-
20
- * process customers, a SQL implementation against the customer's own
21
- * outbox table for production.
21
+ * Persistence is pluggable through {@link PushQueueStorage}. Use
22
+ * {@link InMemoryPushQueueStorage} for a single process, or implement that
23
+ * interface against your own outbox table for production durability.
22
24
  */
23
- import { ABLO_SOURCE_HEADERS, signAbloSourceRequest, } from './index.js';
25
+ import { ABLO_SOURCE_HEADERS, signAbloSourceRequest } from './signing.js';
24
26
  /**
25
- * Standard Webhooks retry schedule. Index = attempt number; value =
26
- * delay-ms after the previous attempt. After the last entry, items
27
- * move to DLQ.
27
+ * The default retry schedule, taken from the Standard Webhooks
28
+ * specification. The index is the attempt number and the value is the delay
29
+ * in milliseconds after the previous attempt failed. Once an item runs off
30
+ * the end of this array, it moves to the dead-letter queue.
28
31
  *
29
- * Source: https://www.standardwebhooks.com/
32
+ * See https://www.standardwebhooks.com/.
30
33
  */
31
34
  export const STANDARD_WEBHOOKS_RETRY_SCHEDULE = [
32
35
  0, // immediate
@@ -40,6 +43,7 @@ export const STANDARD_WEBHOOKS_RETRY_SCHEDULE = [
40
43
  20 * 60 * 60_000, // 20h
41
44
  24 * 60 * 60_000, // 24h
42
45
  ];
46
+ /** Create a {@link PushQueue} from the given {@link PushQueueOptions}. */
43
47
  export function createPushQueue(options) {
44
48
  const tickIntervalMs = options.tickIntervalMs ?? 1000;
45
49
  const batchSize = options.batchSize ?? 50;
@@ -133,22 +137,25 @@ export function createPushQueue(options) {
133
137
  }
134
138
  async function reschedule(item, error) {
135
139
  const nextAttempt = item.attempts + 1;
136
- if (nextAttempt >= schedule.length) {
140
+ // Past the end of the backoff schedule: no attempts left, so dead-letter
141
+ // the item.
142
+ const backoff = schedule[nextAttempt];
143
+ if (backoff === undefined) {
137
144
  await options.storage.markDlq(item.id, error);
138
145
  options.onError?.(item, new Error(error));
139
146
  return;
140
147
  }
141
- const delay = applyJitter(schedule[nextAttempt], jitter);
148
+ const delay = applyJitter(backoff, jitter);
142
149
  await options.storage.reschedule(item.id, now() + delay, error);
143
150
  }
144
151
  }
152
+ /**
153
+ * A {@link PushQueueStorage} that keeps items in memory. It is not durable —
154
+ * items are lost when the process restarts — so it suits development and
155
+ * low-volume use. For production, implement {@link PushQueueStorage} against
156
+ * your own table.
157
+ */
145
158
  export class InMemoryPushQueueStorage {
146
- /**
147
- * Real implementation, not a mock. Suitable for low-volume single-
148
- * process customers; not durable across restarts (in-flight items
149
- * are lost). Production customers should swap in a SQL-backed
150
- * storage that writes to their existing outbox table.
151
- */
152
159
  items = new Map();
153
160
  nextId = 0;
154
161
  now;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Signs and verifies source requests using the Standard Webhooks scheme.
3
+ *
4
+ * Every request Ablo sends to a data source is signed with that source's API
5
+ * key, and the receiving endpoint verifies the signature before any handler
6
+ * runs. This module provides both halves: {@link signAbloSourceRequest} for
7
+ * the sender and {@link verifyAbloSourceRequest} for the receiver. Because the
8
+ * signature format follows the Standard Webhooks specification, you can verify
9
+ * it with any compatible library instead of these functions if you prefer.
10
+ */
11
+ /** Inputs to {@link signAbloSourceRequest}. */
12
+ export interface SourceSignatureOptions {
13
+ /** The API key to sign with; the receiver verifies against the same key. */
14
+ readonly apiKey: string;
15
+ /** The exact request body being signed. */
16
+ readonly body: string;
17
+ /**
18
+ * The unique message id, sent as the `webhook-id` header. It is folded into
19
+ * the signature to defend against replay, and receivers may deduplicate on
20
+ * it. Retries of the same request must reuse the same id.
21
+ */
22
+ readonly messageId: string;
23
+ /** The signing time as a Unix timestamp in seconds. Defaults to the current time. */
24
+ readonly timestamp?: number;
25
+ }
26
+ /** Inputs to {@link verifyAbloSourceRequest}. */
27
+ export interface SourceSignatureVerificationOptions {
28
+ /** The incoming request, whose headers carry the signature to check. */
29
+ readonly request: Request;
30
+ /** The request body, which must match what was signed. */
31
+ readonly body: string;
32
+ /** The API key to verify against. */
33
+ readonly apiKey: string;
34
+ /**
35
+ * How far the request's timestamp may differ from the current clock, in
36
+ * milliseconds, before it is rejected as expired. Defaults to five minutes.
37
+ */
38
+ readonly toleranceMs?: number;
39
+ }
40
+ /** What {@link verifyAbloSourceRequest} returns once a signature checks out. */
41
+ export interface SourceSignatureVerificationResult {
42
+ /** The verified `webhook-id` of the request. */
43
+ readonly messageId: string;
44
+ /** The time the request was signed, as a Unix timestamp in seconds. */
45
+ readonly signedAt: number;
46
+ }
47
+ /**
48
+ * The HTTP header names carried on a signed source request. They follow the
49
+ * Standard Webhooks specification (https://www.standardwebhooks.com/), so you
50
+ * can verify the signature with any compatible library rather than
51
+ * {@link verifyAbloSourceRequest} if you prefer.
52
+ */
53
+ export declare const ABLO_SOURCE_HEADERS: {
54
+ readonly signature: "webhook-signature";
55
+ readonly timestamp: "webhook-timestamp";
56
+ readonly id: "webhook-id";
57
+ readonly idempotencyKey: "Idempotency-Key";
58
+ };
59
+ /**
60
+ * Thrown when a source request fails signature verification. The
61
+ * {@link SourceSignatureError.code} names the specific reason — a missing
62
+ * header, a malformed or expired timestamp, or a signature that does not
63
+ * match.
64
+ */
65
+ export declare class SourceSignatureError extends Error {
66
+ readonly code: 'source_signature_missing' | 'source_id_missing' | 'source_timestamp_missing' | 'source_timestamp_invalid' | 'source_timestamp_expired' | 'source_signature_invalid' | 'source_forbidden';
67
+ constructor(code: SourceSignatureError['code'], message: string);
68
+ }
69
+ /**
70
+ * Sign a source request and return the headers to send with it. The signature
71
+ * covers the message id, the timestamp, and the body, so any change to those
72
+ * invalidates it. The receiver checks it with {@link verifyAbloSourceRequest}.
73
+ */
74
+ export declare function signAbloSourceRequest(options: SourceSignatureOptions): Promise<{
75
+ readonly headers: Record<string, string>;
76
+ readonly signedAt: number;
77
+ readonly signature: string;
78
+ }>;
79
+ /**
80
+ * Verify a signed source request, throwing {@link SourceSignatureError} when
81
+ * the message id, timestamp, or signature is missing, malformed, or outside
82
+ * the allowed clock-skew window. On success it returns the request's message
83
+ * id and signing time. This is the counterpart to {@link signAbloSourceRequest}.
84
+ */
85
+ export declare function verifyAbloSourceRequest(options: SourceSignatureVerificationOptions): Promise<SourceSignatureVerificationResult>;
86
+ export type DataSourceSignatureOptions = SourceSignatureOptions;
87
+ export type DataSourceSignatureVerificationOptions = SourceSignatureVerificationOptions;
88
+ export type DataSourceSignatureVerificationResult = SourceSignatureVerificationResult;
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Signs and verifies source requests using the Standard Webhooks scheme.
3
+ *
4
+ * Every request Ablo sends to a data source is signed with that source's API
5
+ * key, and the receiving endpoint verifies the signature before any handler
6
+ * runs. This module provides both halves: {@link signAbloSourceRequest} for
7
+ * the sender and {@link verifyAbloSourceRequest} for the receiver. Because the
8
+ * signature format follows the Standard Webhooks specification, you can verify
9
+ * it with any compatible library instead of these functions if you prefer.
10
+ */
11
+ /**
12
+ * The HTTP header names carried on a signed source request. They follow the
13
+ * Standard Webhooks specification (https://www.standardwebhooks.com/), so you
14
+ * can verify the signature with any compatible library rather than
15
+ * {@link verifyAbloSourceRequest} if you prefer.
16
+ */
17
+ export const ABLO_SOURCE_HEADERS = {
18
+ signature: 'webhook-signature',
19
+ timestamp: 'webhook-timestamp',
20
+ id: 'webhook-id',
21
+ idempotencyKey: 'Idempotency-Key',
22
+ };
23
+ /**
24
+ * Thrown when a source request fails signature verification. The
25
+ * {@link SourceSignatureError.code} names the specific reason — a missing
26
+ * header, a malformed or expired timestamp, or a signature that does not
27
+ * match.
28
+ */
29
+ export class SourceSignatureError extends Error {
30
+ code;
31
+ constructor(code, message) {
32
+ super(message);
33
+ this.name = 'SourceSignatureError';
34
+ this.code = code;
35
+ }
36
+ }
37
+ const DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
38
+ function getHeader(request, name) {
39
+ const headers = request.headers;
40
+ if (!headers)
41
+ return null;
42
+ if (typeof headers.get === 'function') {
43
+ return headers.get(name);
44
+ }
45
+ const record = headers;
46
+ return record[name] ?? record[name.toLowerCase()] ?? null;
47
+ }
48
+ /**
49
+ * Parse a `webhook-signature` header per the Standard Webhooks spec.
50
+ * Values are space-delimited `<scheme>,<base64>` pairs (e.g.
51
+ * `v1,abc== v1,def==` during a key rotation window). Returns the set
52
+ * of `v1` signatures so the verifier can accept any of them.
53
+ */
54
+ function parseSignatureHeader(raw) {
55
+ if (!raw)
56
+ return [];
57
+ const out = [];
58
+ for (const part of raw.split(/\s+/)) {
59
+ const trimmed = part.trim();
60
+ if (!trimmed)
61
+ continue;
62
+ const commaAt = trimmed.indexOf(',');
63
+ if (commaAt === -1)
64
+ continue;
65
+ const scheme = trimmed.slice(0, commaAt);
66
+ const value = trimmed.slice(commaAt + 1);
67
+ if (scheme === 'v1' && value.length > 0) {
68
+ out.push(value);
69
+ }
70
+ }
71
+ return out;
72
+ }
73
+ function bufferToBase64(buffer) {
74
+ // Node + browsers both expose `btoa` on the global; we feed it
75
+ // a binary string built from the byte view.
76
+ let binary = '';
77
+ for (const byte of new Uint8Array(buffer)) {
78
+ binary += String.fromCharCode(byte);
79
+ }
80
+ return btoa(binary);
81
+ }
82
+ async function hmacSha256Base64(apiKey, payload) {
83
+ const crypto = globalThis.crypto?.subtle;
84
+ if (!crypto) {
85
+ throw new SourceSignatureError('source_signature_invalid', 'WebCrypto HMAC support is unavailable in this runtime');
86
+ }
87
+ const encoder = new TextEncoder();
88
+ const key = await crypto.importKey('raw', encoder.encode(apiKey), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
89
+ return bufferToBase64(await crypto.sign('HMAC', key, encoder.encode(payload)));
90
+ }
91
+ /**
92
+ * Constant-time string equality. Used over `===` so a malicious
93
+ * signature can't be probed byte-by-byte via timing differences.
94
+ */
95
+ function timingSafeEqual(expected, actual) {
96
+ const max = Math.max(expected.length, actual.length);
97
+ let diff = expected.length ^ actual.length;
98
+ for (let i = 0; i < max; i++) {
99
+ diff |= (expected.charCodeAt(i) || 0) ^ (actual.charCodeAt(i) || 0);
100
+ }
101
+ return diff === 0;
102
+ }
103
+ /**
104
+ * Sign a source request and return the headers to send with it. The signature
105
+ * covers the message id, the timestamp, and the body, so any change to those
106
+ * invalidates it. The receiver checks it with {@link verifyAbloSourceRequest}.
107
+ */
108
+ export async function signAbloSourceRequest(options) {
109
+ const signedAt = options.timestamp ?? Math.floor(Date.now() / 1000);
110
+ // Standard Webhooks signing input: `${msg_id}.${timestamp}.${payload}`
111
+ const signature = await hmacSha256Base64(options.apiKey, `${options.messageId}.${signedAt}.${options.body}`);
112
+ return {
113
+ signedAt,
114
+ signature,
115
+ headers: {
116
+ [ABLO_SOURCE_HEADERS.id]: options.messageId,
117
+ [ABLO_SOURCE_HEADERS.timestamp]: String(signedAt),
118
+ [ABLO_SOURCE_HEADERS.signature]: `v1,${signature}`,
119
+ },
120
+ };
121
+ }
122
+ /**
123
+ * Verify a signed source request, throwing {@link SourceSignatureError} when
124
+ * the message id, timestamp, or signature is missing, malformed, or outside
125
+ * the allowed clock-skew window. On success it returns the request's message
126
+ * id and signing time. This is the counterpart to {@link signAbloSourceRequest}.
127
+ */
128
+ export async function verifyAbloSourceRequest(options) {
129
+ const messageId = getHeader(options.request, ABLO_SOURCE_HEADERS.id);
130
+ if (!messageId) {
131
+ throw new SourceSignatureError('source_id_missing', 'Missing webhook-id header');
132
+ }
133
+ const rawTimestamp = getHeader(options.request, ABLO_SOURCE_HEADERS.timestamp);
134
+ if (!rawTimestamp) {
135
+ throw new SourceSignatureError('source_timestamp_missing', 'Missing webhook-timestamp header');
136
+ }
137
+ const signedAt = Number(rawTimestamp);
138
+ if (!Number.isFinite(signedAt)) {
139
+ throw new SourceSignatureError('source_timestamp_invalid', 'Invalid webhook-timestamp header');
140
+ }
141
+ const toleranceMs = options.toleranceMs ?? DEFAULT_SIGNATURE_TOLERANCE_MS;
142
+ const nowSeconds = Math.floor(Date.now() / 1000);
143
+ const toleranceSeconds = Math.ceil(toleranceMs / 1000);
144
+ if (Math.abs(nowSeconds - signedAt) > toleranceSeconds) {
145
+ throw new SourceSignatureError('source_timestamp_expired', 'webhook-timestamp is outside the allowed clock-skew window');
146
+ }
147
+ const presented = parseSignatureHeader(getHeader(options.request, ABLO_SOURCE_HEADERS.signature));
148
+ if (presented.length === 0) {
149
+ throw new SourceSignatureError('source_signature_missing', 'Missing webhook-signature header');
150
+ }
151
+ const expected = await hmacSha256Base64(options.apiKey, `${messageId}.${signedAt}.${options.body}`);
152
+ // Accept any presented signature that matches — supports key
153
+ // rotation per the Standard Webhooks spec.
154
+ const ok = presented.some((sig) => timingSafeEqual(expected, sig));
155
+ if (!ok) {
156
+ throw new SourceSignatureError('source_signature_invalid', 'Invalid webhook-signature');
157
+ }
158
+ return { messageId, signedAt };
159
+ }