@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,24 +1,26 @@
1
1
  /**
2
- * groupChange sync-group change / shrinkage handling.
2
+ * Handles the delta types that change which sync groups a session can see. A
3
+ * sync group is a fan-out scope the server uses to decide which entities a
4
+ * client receives. When a session's membership changes, these handlers update
5
+ * the client's subscription list; when access is revoked, they clear cached
6
+ * data and trigger a full re-bootstrap so revoked rows cannot linger on the
7
+ * device.
3
8
  *
4
- * Extracted from BaseSyncedStore.ts as a cohesive leaf: the 'G'/'S' delta
5
- * handlers (incremental group-added, legacy group-diff, group-removed), the
6
- * group-set math, the force-re-bootstrap trigger, and the security-critical
7
- * shrinkage check. The store keeps thin protected delegates with unchanged
8
- * signatures — subclass override points stay overridable, and the leaf
9
- * routes every cross-handler call back through the minimal
10
- * {@link GroupChangeContext} so dynamic dispatch is preserved.
9
+ * Every handler takes a {@link GroupChangeContext}, the narrow facade through
10
+ * which it reaches the client's local storage and connection lifecycle hooks.
11
11
  */
12
12
  import { getContext } from '../context.js';
13
- /** Sentinel for a 'G'/'S' payload that could not be parsed (vs a valid
14
- * `null`/absent payload, which the handlers already tolerate). */
13
+ /**
14
+ * Marker returned when a group-change payload cannot be parsed, kept distinct
15
+ * from a valid null or absent payload, which the handlers accept normally.
16
+ */
15
17
  const MALFORMED_PAYLOAD = Symbol('malformed-group-change-payload');
16
18
  /**
17
- * Parse a group-change delta payload without ever throwing. The server
18
- * serializes these as JSON strings; a corrupt frame used to escape the
19
- * whole delta pipeline as an unhandled rejection AFTER the watermark had
20
- * advanced the delta is never re-delivered, so the security clear it
21
- * carried was permanently lost.
19
+ * Parses a group-change delta payload without ever throwing. The server sends
20
+ * these as JSON strings. If a frame is corrupt, this returns
21
+ * {@link MALFORMED_PAYLOAD} rather than raising, because an error escaping here
22
+ * would leave the delta pipeline after the watermark has already advanced. The
23
+ * delta is never re-delivered, so the security clear it carried would be lost.
22
24
  */
23
25
  function parseGroupChangePayload(delta) {
24
26
  if (typeof delta.data !== 'string')
@@ -36,41 +38,38 @@ function parseGroupChangePayload(delta) {
36
38
  }
37
39
  }
38
40
  /**
39
- * The legacy-clear fallback for an unparseable group-change delta: we know
40
- * access changed but not how, so treat it as a revocation clear cached
41
- * data (IDB + pool) and force a full re-bootstrap with server truth.
41
+ * Fallback for a group-change delta that could not be read. Because we know
42
+ * access changed but not how, this treats it as a revocation: it clears cached
43
+ * data from both local storage and the in-memory pool, then forces a full
44
+ * re-bootstrap from the server.
42
45
  */
43
46
  async function clearForUnknownGroupChange(ctx, delta, kind) {
44
47
  getContext().logger.debug(`[BaseSyncedStore] Unreadable ${kind} payload — clearing cached data and re-bootstrapping`, { syncId: delta.id });
45
- // SECURITY: same rationale as the legacy removedGroups path revoked data
46
- // must not persist if the device goes offline before the re-bootstrap.
48
+ // Revoked data must not persist if the device goes offline before the
49
+ // re-bootstrap, the same reasoning as the explicit removed-groups path.
47
50
  await ctx.database.clear();
48
51
  ctx.objectPool.clear();
49
52
  ctx.forceFullRebootstrap();
50
53
  }
51
54
  /**
52
- * Handle an actionType 'G' delta.
53
- *
54
- * The server emits 'G' via two distinct pathways, distinguished by payload
55
- * shape:
55
+ * Handles a 'G' (group-change) delta. The server sends two shapes of this
56
+ * delta, told apart by the payload:
56
57
  *
57
- * Incremental (EmitGroupAdded): { group, userId }
58
- * - The recipient was added to a single sync group.
59
- * - Subsequent 'C' (Covering) deltas deliver each newly-visible entity.
60
- * - No re-bootstrap — entities arrive via the normal insert path.
58
+ * Incremental — `{ group, userId }`: the recipient was added to a single
59
+ * sync group. No re-bootstrap follows; the newly visible entities arrive as
60
+ * ordinary 'C' (covering) deltas through the normal insert path.
61
61
  *
62
- * Legacy (EmitGroupChange): { addedGroups, removedGroups }
63
- * - Single delta carrying the full group membership diff.
64
- * - Forces a full re-bootstrap (disconnect + reconnect + fetch all).
65
- * - Deprecated on the server; kept here for wire-level backward compat.
62
+ * Full diff — `{ addedGroups, removedGroups }`: one delta carrying the whole
63
+ * membership change. This forces a full re-bootstrap (disconnect, reconnect,
64
+ * and refetch), clearing cached data first if any group was removed.
66
65
  */
67
66
  export async function handleSyncGroupChange(ctx, delta) {
68
67
  const raw = parseGroupChangePayload(delta);
69
68
  if (raw === MALFORMED_PAYLOAD) {
70
- // Malformed payload we can't know WHICH groups changed, and this
71
- // delta will never be re-delivered (the watermark already advanced).
72
- // Degrade to the legacy security path: assume a removal, clear cached
73
- // data, and rebuild from server truth. Never throw out of the pipeline.
69
+ // The payload is unreadable, so we cannot tell which groups changed, and
70
+ // this delta will never be re-delivered because the watermark has already
71
+ // advanced. Fall back to the safe direction: assume a revocation, clear
72
+ // cached data, and rebuild from the server rather than throwing.
74
73
  await clearForUnknownGroupChange(ctx, delta, 'sync-group change');
75
74
  return;
76
75
  }
@@ -84,7 +83,7 @@ export async function handleSyncGroupChange(ctx, delta) {
84
83
  await ctx.handleGroupAdded(incremental, delta.id);
85
84
  return;
86
85
  }
87
- // Legacy payload: { addedGroups, removedGroups }
86
+ // Full-diff payload: { addedGroups, removedGroups }
88
87
  const payload = {
89
88
  removedGroups: rawObj.removedGroups ?? [],
90
89
  addedGroups: rawObj.addedGroups ?? [],
@@ -94,9 +93,9 @@ export async function handleSyncGroupChange(ctx, delta) {
94
93
  addedGroups: payload.addedGroups,
95
94
  syncId: delta.id,
96
95
  });
97
- // SECURITY: If groups were removed, clear cached data immediately.
98
- // This prevents revoked data from persisting if the device goes offline
99
- // before the full re-bootstrap completes.
96
+ // If any groups were removed, clear cached data immediately so revoked data
97
+ // cannot persist should the device go offline before the re-bootstrap
98
+ // completes.
100
99
  if (payload.removedGroups.length > 0) {
101
100
  await ctx.database.clear();
102
101
  ctx.objectPool.clear();
@@ -109,11 +108,10 @@ export async function handleSyncGroupChange(ctx, delta) {
109
108
  ctx.forceFullRebootstrap();
110
109
  }
111
110
  /**
112
- * Handle an incremental GroupAdded delta.
113
- *
114
- * Adds the new group to the subscription metadata without triggering a
115
- * re-bootstrap. The server will follow up with 'C' (Covering) deltas for
116
- * each newly-visible entity, which flow through the normal insert path.
111
+ * Handles an incremental group-added delta. It records the new sync group in
112
+ * the subscription metadata without forcing a re-bootstrap; the server then
113
+ * sends a 'C' (covering) delta for each newly visible entity, which flows
114
+ * through the normal insert path.
117
115
  */
118
116
  export async function handleGroupAdded(ctx, payload, syncId) {
119
117
  getContext().logger.info('[BaseSyncedStore] Group added (incremental)', {
@@ -123,26 +121,20 @@ export async function handleGroupAdded(ctx, payload, syncId) {
123
121
  const current = new Set(ctx.getSubscribedSyncGroups());
124
122
  current.add(payload.group);
125
123
  await ctx.database.updateWorkspaceMetadata({ subscribedSyncGroups: Array.from(current) });
126
- // Note: no forceFullRebootstrap() covering deltas will bring the entities.
124
+ // No forceFullRebootstrap() here; the covering deltas will bring the entities.
127
125
  }
128
126
  /**
129
- * Handle an actionType 'S' (GroupRemoved) delta.
130
- *
131
- * Signals that the recipient has lost access to a sync group. Because
132
- * the client does not track per-entity group membership, we can't
133
- * selectively purge entities belonging to that group. The safe fallback
134
- * is the legacy behavior: clear local state and force a re-bootstrap
135
- * with the updated group list.
136
- *
137
- * Future optimization: track group membership in the ObjectPool so 'S'
138
- * can do a targeted purge instead of a full re-bootstrap.
127
+ * Handles an 'S' (group-removed) delta, which signals the recipient has lost
128
+ * access to a sync group. The client does not track which entities belong to
129
+ * which group, so it cannot purge only the affected rows; instead it clears
130
+ * local state and forces a re-bootstrap with the updated group list.
139
131
  */
140
132
  export async function handleGroupRemoved(ctx, delta) {
141
133
  const raw = parseGroupChangePayload(delta);
142
134
  if (raw === MALFORMED_PAYLOAD) {
143
- // Malformed 'S' payload access WAS revoked but we can't tell which
144
- // group. Degrade to the legacy clear (the safe direction for a
145
- // security delta) instead of throwing out of the pipeline.
135
+ // The payload is unreadable: access was revoked but we cannot tell which
136
+ // group. Fall back to a full clear, the safe direction for an
137
+ // access-revocation delta, rather than throwing.
146
138
  await clearForUnknownGroupChange(ctx, delta, 'group-removed');
147
139
  return;
148
140
  }
@@ -158,9 +150,9 @@ export async function handleGroupRemoved(ctx, delta) {
158
150
  group: groupKey,
159
151
  syncId: delta.id,
160
152
  });
161
- // SECURITY: Clear cached data before re-bootstrap. This prevents
162
- // revoked-group data from persisting if the device goes offline
163
- // between receiving 'S' and completing the re-bootstrap.
153
+ // Clear cached data before the re-bootstrap so revoked-group data cannot
154
+ // persist if the device goes offline between receiving this delta and
155
+ // completing the re-bootstrap.
164
156
  await ctx.database.clear();
165
157
  ctx.objectPool.clear();
166
158
  // Update subscription metadata so the re-bootstrap fetches the
@@ -170,7 +162,7 @@ export async function handleGroupRemoved(ctx, delta) {
170
162
  await ctx.database.updateWorkspaceMetadata({ subscribedSyncGroups: Array.from(current) });
171
163
  ctx.forceFullRebootstrap();
172
164
  }
173
- /** Compute new sync groups after applying additions and removals */
165
+ /** Computes the new sync-group set after applying the additions and removals in a diff. */
174
166
  export function computeUpdatedSyncGroups(ctx, payload) {
175
167
  const current = new Set(ctx.getSubscribedSyncGroups());
176
168
  for (const g of payload.removedGroups)
@@ -179,12 +171,12 @@ export function computeUpdatedSyncGroups(ctx, payload) {
179
171
  current.add(g);
180
172
  return Array.from(current);
181
173
  }
182
- /** Force a full re-bootstrap via connection lifecycle event.
183
- *
184
- * No-op for `bootstrapMode: 'none'` participants they never pull
185
- * baseline state, so a "force re-bootstrap" trigger (sync-group
186
- * shrink, scope revocation) instead just flushes the local pool and
187
- * relies on covering deltas to repopulate the data they actually
174
+ /**
175
+ * Forces a full re-bootstrap by marking local storage as needing one,
176
+ * disconnecting, and emitting a connection lifecycle event that the reconnect
177
+ * path acts on. Does nothing for participants whose bootstrap mode is 'none':
178
+ * they never pull a baseline, so after a trigger such as a sync-group shrink or
179
+ * an access revocation they rely on covering deltas to repopulate the data they
188
180
  * subscribe to.
189
181
  */
190
182
  export function forceFullRebootstrap(ctx) {
@@ -197,12 +189,12 @@ export function forceFullRebootstrap(ctx) {
197
189
  ctx.emitConnectionEvent('WS_DISCONNECTED');
198
190
  }
199
191
  /**
200
- * Single source of truth for the sync-group list this session is
201
- * subscribed to. Server-issued (`context.syncGroups`) is authoritative.
202
- * When absent, the SDK subscribes to no explicit groups. Both
203
- * `checkSyncGroupShrinkage` and `setupWebSocketSync` resolve through
204
- * here so the WS subscription and the security-critical shrinkage
205
- * check can never disagree.
192
+ * Resolves the sync-group list this session subscribes to, and is the single
193
+ * place that decision is made. The server-issued `context.syncGroups` is
194
+ * authoritative; when it is absent, the session subscribes to no explicit
195
+ * groups. {@link checkSyncGroupShrinkage} and connection setup both read
196
+ * through here, so the live subscription and the access-revocation check can
197
+ * never disagree.
206
198
  */
207
199
  export function resolveSyncGroups(context) {
208
200
  if (context.syncGroups && context.syncGroups.length > 0) {
@@ -210,7 +202,11 @@ export function resolveSyncGroups(context) {
210
202
  }
211
203
  return [];
212
204
  }
213
- /** Check if sync groups shrank since last session — force full bootstrap if so */
205
+ /**
206
+ * Compares the session's current sync groups against the set stored from the
207
+ * last session. If any group is now missing, access has narrowed, so this
208
+ * clears cached data and forces a full bootstrap before recording the new set.
209
+ */
214
210
  export async function checkSyncGroupShrinkage(ctx) {
215
211
  const currentSyncGroups = ctx.getCurrentSyncGroups();
216
212
  if (!currentSyncGroups)
@@ -228,8 +224,8 @@ export async function checkSyncGroupShrinkage(ctx) {
228
224
  storedCount: stored.length,
229
225
  currentCount: currentGroups.size,
230
226
  });
231
- // SECURITY: Clear cached data before re-bootstrap to prevent
232
- // revoked-group data from persisting if device goes offline
227
+ // Clear cached data before the re-bootstrap so revoked-group data cannot
228
+ // persist if the device goes offline first.
233
229
  await ctx.database.clear();
234
230
  ctx.objectPool.clear();
235
231
  ctx.database.markRequiresFullBootstrap();
@@ -1,55 +1,56 @@
1
1
  /**
2
2
  * Application-level heartbeat for the sync WebSocket.
3
3
  *
4
- * The browser WebSocket API hides RFC 6455 protocol-level ping/pong from
5
- * JavaScript, so the server's `ws.ping()` keepalive can't be observed by
6
- * client code meaning the client cannot tell a healthy idle connection
7
- * apart from a "zombie" socket where TCP silently broke (laptop sleep,
8
- * NAT timeout, mobile handoff). We send an application-level
9
- * `{ type: 'ping' }` every 30s and force-close the socket if no inbound
10
- * traffic arrives within 10s. ANY inbound message counts as
11
- * proof-of-life the explicit `pong` is just a guarantee that something
12
- * will arrive even on an idle stream.
4
+ * The browser WebSocket API hides RFC 6455 protocol-level ping and pong frames
5
+ * from JavaScript, so the server's keepalive cannot be observed by client
6
+ * code. That leaves the client unable to tell a healthy idle connection apart
7
+ * from a "zombie" socket whose underlying TCP connection has silently broken
8
+ * (laptop sleep, NAT timeout, mobile handoff). To close the gap, the client
9
+ * sends an application-level `{ type: 'ping' }` every 30 seconds and
10
+ * force-closes the socket if no inbound traffic arrives within 10 seconds. Any
11
+ * inbound message counts as proof the connection is alive; the explicit `pong`
12
+ * merely guarantees that something arrives even on an otherwise idle stream.
13
13
  */
14
14
  /**
15
- * One cadence for both sides: the SDK's application-level ping runs at the
16
- * same `PING_INTERVAL_MS` as the server's RFC 6455 keepalive (and the claim
17
- * lease window is derived from it — see `wire/protocol.ts`).
15
+ * The interval between application-level pings, shared with both sides of the
16
+ * connection: the client pings at the same {@link PING_INTERVAL_MS} the server
17
+ * uses for its own keepalive, and the claim lease window is derived from the
18
+ * same constant.
18
19
  */
19
20
  export declare const HEARTBEAT_INTERVAL_MS = 30000;
20
21
  export declare const HEARTBEAT_TIMEOUT_MS = 10000;
21
22
  /**
22
- * The slice of the socket the heartbeat needs kept minimal so this
23
- * leaf never depends on the SyncWebSocket class itself.
23
+ * The narrow slice of the socket the heartbeat depends on. Keeping it small
24
+ * lets the {@link HeartbeatController} work without depending on the full
25
+ * WebSocket transport.
24
26
  */
25
27
  export interface HeartbeatTransport {
26
- /** True only while the underlying socket is `OPEN`. */
28
+ /** True only while the underlying socket is open. */
27
29
  isSocketOpen(): boolean;
28
- /** Send the `{ type: 'ping' }` frame; throws when the socket is already dead. */
30
+ /** Sends the `{ type: 'ping' }` frame; throws when the socket is already dead. */
29
31
  sendPing(): void;
30
32
  /**
31
- * Force-close the socket from the client side (private 4xxx code) so
32
- * `onclose` fires and runs the owner's reconnect / handshake-failed
33
- * dispatch.
33
+ * Closes the socket from the client side with a private 4xxx code, so the
34
+ * socket's close event fires and the owner's reconnect or handshake-failure
35
+ * handling runs.
34
36
  */
35
37
  forceClose(reason: string): void;
36
38
  }
37
39
  /**
38
- * Every `HEARTBEAT_INTERVAL_MS` while `OPEN`, send `{ type: 'ping' }`
39
- * and arm a `HEARTBEAT_TIMEOUT_MS` watchdog. Any inbound frame (the
40
- * owner's `onmessage` calls {@link clearHeartbeatTimeout}) clears the
41
- * watchdog. If the watchdog fires, we treat the connection as zombie
42
- * and force-close it `onclose` then triggers the existing reconnect
43
- * path.
40
+ * Runs the application-level heartbeat for one socket. While the socket is
41
+ * open, it sends a `{ type: 'ping' }` frame every {@link HEARTBEAT_INTERVAL_MS}
42
+ * and arms a {@link HEARTBEAT_TIMEOUT_MS} watchdog. Any inbound frame clears
43
+ * the watchdog the owner calls {@link HeartbeatController.clearHeartbeatTimeout}
44
+ * from its message handler. If the watchdog fires first, the connection is
45
+ * treated as a zombie and force-closed, which lets the owner's reconnect path
46
+ * run.
44
47
  *
45
- * Why both sides need this:
46
- * - The server sends RFC 6455 protocol pings via `ws.ping()` every
47
- * 30s. Browsers auto-respond with a pong but DO NOT expose either
48
- * frame to JavaScript, so the client is blind to its own keepalive.
49
- * - On a half-open TCP (laptop wake, NAT timeout, mobile handoff)
50
- * the browser may keep `readyState === OPEN` for minutes before
51
- * the OS surfaces the broken connection. App-level traffic is
52
- * the only signal we can observe.
48
+ * The heartbeat exists because the client cannot see the protocol-level
49
+ * keepalive: browsers answer the server's pings automatically but never expose
50
+ * those frames to JavaScript. On a half-open connection (laptop wake, NAT
51
+ * timeout, mobile handoff) the socket can report itself open for minutes
52
+ * before the operating system surfaces the break, so observable application
53
+ * traffic is the only reliable signal.
53
54
  */
54
55
  export declare class HeartbeatController {
55
56
  private readonly transport;
@@ -1,41 +1,41 @@
1
1
  /**
2
2
  * Application-level heartbeat for the sync WebSocket.
3
3
  *
4
- * The browser WebSocket API hides RFC 6455 protocol-level ping/pong from
5
- * JavaScript, so the server's `ws.ping()` keepalive can't be observed by
6
- * client code meaning the client cannot tell a healthy idle connection
7
- * apart from a "zombie" socket where TCP silently broke (laptop sleep,
8
- * NAT timeout, mobile handoff). We send an application-level
9
- * `{ type: 'ping' }` every 30s and force-close the socket if no inbound
10
- * traffic arrives within 10s. ANY inbound message counts as
11
- * proof-of-life the explicit `pong` is just a guarantee that something
12
- * will arrive even on an idle stream.
4
+ * The browser WebSocket API hides RFC 6455 protocol-level ping and pong frames
5
+ * from JavaScript, so the server's keepalive cannot be observed by client
6
+ * code. That leaves the client unable to tell a healthy idle connection apart
7
+ * from a "zombie" socket whose underlying TCP connection has silently broken
8
+ * (laptop sleep, NAT timeout, mobile handoff). To close the gap, the client
9
+ * sends an application-level `{ type: 'ping' }` every 30 seconds and
10
+ * force-closes the socket if no inbound traffic arrives within 10 seconds. Any
11
+ * inbound message counts as proof the connection is alive; the explicit `pong`
12
+ * merely guarantees that something arrives even on an otherwise idle stream.
13
13
  */
14
14
  import { getContext } from '../context.js';
15
15
  import { PING_INTERVAL_MS } from '../wire/protocol.js';
16
16
  /**
17
- * One cadence for both sides: the SDK's application-level ping runs at the
18
- * same `PING_INTERVAL_MS` as the server's RFC 6455 keepalive (and the claim
19
- * lease window is derived from it — see `wire/protocol.ts`).
17
+ * The interval between application-level pings, shared with both sides of the
18
+ * connection: the client pings at the same {@link PING_INTERVAL_MS} the server
19
+ * uses for its own keepalive, and the claim lease window is derived from the
20
+ * same constant.
20
21
  */
21
22
  export const HEARTBEAT_INTERVAL_MS = PING_INTERVAL_MS;
22
23
  export const HEARTBEAT_TIMEOUT_MS = 10_000;
23
24
  /**
24
- * Every `HEARTBEAT_INTERVAL_MS` while `OPEN`, send `{ type: 'ping' }`
25
- * and arm a `HEARTBEAT_TIMEOUT_MS` watchdog. Any inbound frame (the
26
- * owner's `onmessage` calls {@link clearHeartbeatTimeout}) clears the
27
- * watchdog. If the watchdog fires, we treat the connection as zombie
28
- * and force-close it `onclose` then triggers the existing reconnect
29
- * path.
25
+ * Runs the application-level heartbeat for one socket. While the socket is
26
+ * open, it sends a `{ type: 'ping' }` frame every {@link HEARTBEAT_INTERVAL_MS}
27
+ * and arms a {@link HEARTBEAT_TIMEOUT_MS} watchdog. Any inbound frame clears
28
+ * the watchdog the owner calls {@link HeartbeatController.clearHeartbeatTimeout}
29
+ * from its message handler. If the watchdog fires first, the connection is
30
+ * treated as a zombie and force-closed, which lets the owner's reconnect path
31
+ * run.
30
32
  *
31
- * Why both sides need this:
32
- * - The server sends RFC 6455 protocol pings via `ws.ping()` every
33
- * 30s. Browsers auto-respond with a pong but DO NOT expose either
34
- * frame to JavaScript, so the client is blind to its own keepalive.
35
- * - On a half-open TCP (laptop wake, NAT timeout, mobile handoff)
36
- * the browser may keep `readyState === OPEN` for minutes before
37
- * the OS surfaces the broken connection. App-level traffic is
38
- * the only signal we can observe.
33
+ * The heartbeat exists because the client cannot see the protocol-level
34
+ * keepalive: browsers answer the server's pings automatically but never expose
35
+ * those frames to JavaScript. On a half-open connection (laptop wake, NAT
36
+ * timeout, mobile handoff) the socket can report itself open for minutes
37
+ * before the operating system surfaces the break, so observable application
38
+ * traffic is the only reliable signal.
39
39
  */
40
40
  export class HeartbeatController {
41
41
  transport;
@@ -49,8 +49,8 @@ export class HeartbeatController {
49
49
  this.heartbeatTimer = setInterval(() => {
50
50
  if (!this.transport.isSocketOpen())
51
51
  return;
52
- // Send the ping. If `send` throws, the socket is already dead
53
- // force-close so onclose triggers the reconnect cycle.
52
+ // Send the ping. If it throws, the socket is already dead, so
53
+ // force-close it to let the close event drive the reconnect cycle.
54
54
  try {
55
55
  this.transport.sendPing();
56
56
  }
@@ -62,9 +62,9 @@ export class HeartbeatController {
62
62
  this.transport.forceClose('heartbeat-send-failed');
63
63
  return;
64
64
  }
65
- // Arm the timeout. ANY inbound message clears it (see the owner's
66
- // onmessage). We don't require an explicit `pong` a delta or any
67
- // other frame is equally good proof-of-life.
65
+ // Arm the timeout. Any inbound message clears it; an explicit `pong` is
66
+ // not required, since a delta or any other frame is equally good proof
67
+ // that the connection is alive.
68
68
  if (this.heartbeatTimeoutTimer)
69
69
  clearTimeout(this.heartbeatTimeoutTimer);
70
70
  this.heartbeatTimeoutTimer = setTimeout(() => {
@@ -3,9 +3,9 @@ import type { Schema } from '../schema/schema.js';
3
3
  import type { Claim, Activity, ClaimTarget, ClaimStream, Peer, PresenceStream, PresenceTarget } from '../types/streams.js';
4
4
  import type { AttachableClaimStream } from './createClaimStream.js';
5
5
  /**
6
- * Scope accepted by participant APIs. The normal SDK shape is an
7
- * entity target (`{ type, id }`). Raw sync-group strings remain an
8
- * advanced transport escape hatch.
6
+ * The scope a participant can be joined to. The usual form is an entity target
7
+ * (`{ type, id }`); raw sync-group strings are an advanced escape hatch for
8
+ * addressing a transport scope directly.
9
9
  */
10
10
  export type ParticipantScope = ClaimTarget | readonly ClaimTarget[] | string | readonly string[] | {
11
11
  readonly syncGroup: string;
@@ -19,26 +19,27 @@ export interface EngineParticipant {
19
19
  }
20
20
  export interface ParticipantJoinOptions {
21
21
  /**
22
- * Initial focus target: customer schema vocabulary, optionally
23
- * narrowed to a path, field, or range. When `scope` is omitted,
24
- * this also becomes the routing scope.
22
+ * The initial focus target, named in your schema's vocabulary and optionally
23
+ * narrowed to a path, field, or range. When `scope` is omitted, this target
24
+ * also becomes the routing scope.
25
25
  */
26
26
  readonly target?: PresenceTarget;
27
27
  /** Alias for `target` when the participant is joined to a broader scope. */
28
28
  readonly focus?: PresenceTarget;
29
29
  /**
30
- * Routing scope. Can be one entity, many entities, or a raw
31
- * sync-group escape hatch. Use this for "joined to folder, focused
32
- * on file" shapes.
30
+ * The routing scope: one entity, many entities, or a raw sync-group escape
31
+ * hatch. Use it for "joined to the folder, focused on one file" shapes,
32
+ * where the participant listens more broadly than its focus target.
33
33
  */
34
34
  readonly scope?: ParticipantScope;
35
35
  /** Present a narrower capability for this logical participant. */
36
36
  readonly capabilityToken?: string;
37
- /** Claim TTL, in seconds or a compact duration string (`30s`, `5m`). */
37
+ /** How long the claim lives, in seconds or a compact duration string (`30s`, `5m`). */
38
38
  readonly ttlSeconds?: number | string | null;
39
39
  /**
40
- * Activity to announce immediately after the claim acks. Defaults to
41
- * `reading` when `target` is present. Pass false to join silently.
40
+ * The activity to announce as soon as the claim is acknowledged. Defaults to
41
+ * `reading` when a `target` is present. Pass `false` to join without
42
+ * announcing anything.
42
43
  */
43
44
  readonly activity?: 'reading' | 'viewing' | 'editing' | false;
44
45
  readonly detail?: string;
@@ -63,17 +64,16 @@ export interface ScopedClaimOptions {
63
64
  /** Free-form reason. Defaults to `'editing'`. Common: `'editing'`,
64
65
  * `'writing'`, `'reviewing'`, app-specific phases. */
65
66
  readonly reason?: string;
66
- /** TTL server auto-expires the claim after this. */
67
+ /** How long the claim lives; the server expires it automatically after this. */
67
68
  readonly ttl?: import('../types/streams.js').Duration;
68
69
  }
69
70
  export interface ScopedClaims {
70
71
  readonly focus: ClaimTarget | null;
71
72
  readonly others: readonly Claim[];
72
73
  /**
73
- * Claim an exclusive claim on the participant's focus target (or
74
- * an explicit override via `opts.target`). Single verb the old
75
- * `editing / writing / announce / claim(reason, opts)` overloads
76
- * collapsed into this one method.
74
+ * Takes an exclusive claim on the participant's focus target, or on an
75
+ * explicit target passed via `opts.target`. While the claim is held, other
76
+ * participants that request an overlapping target are rejected.
77
77
  */
78
78
  claim(opts?: ScopedClaimOptions): Claim;
79
79
  onRejected(listener: Parameters<ClaimStream['onRejected']>[0]): () => void;
@@ -84,10 +84,10 @@ export interface ParticipantFocusOptions {
84
84
  readonly detail?: string;
85
85
  }
86
86
  export interface JoinedParticipant {
87
- /** Current exact thing this participant is reading/editing. */
87
+ /** The exact entity this participant is currently reading or editing. */
88
88
  readonly target: ClaimTarget | null;
89
89
  readonly focusTarget: ClaimTarget | null;
90
- /** Transport scopes this participant is joined to for visibility/fan-out. */
90
+ /** The transport scopes this participant is joined to, which govern what it sees and receives. */
91
91
  readonly syncGroups: readonly string[];
92
92
  readonly presence: ScopedPresence;
93
93
  readonly claims: ScopedClaims;
@@ -74,7 +74,8 @@ export declare const BootstrapResponseSchema: z.ZodObject<{
74
74
  }, z.core.$loose>;
75
75
  export type ValidatedBootstrapResponse = z.infer<typeof BootstrapResponseSchema>;
76
76
  /**
77
- * Validate a raw bootstrap response from the server.
78
- * Logs validation failures via SyncObservability and throws a descriptive error.
77
+ * Validates a raw bootstrap response from the server and returns the typed
78
+ * result. On failure it records a diagnostic breadcrumb and throws an
79
+ * {@link AbloValidationError} describing which fields were invalid.
79
80
  */
80
81
  export declare function parseBootstrapResponse(raw: unknown): ValidatedBootstrapResponse;
@@ -8,7 +8,8 @@ import { z } from 'zod';
8
8
  import { getContext } from "../context.js";
9
9
  import { AbloValidationError } from "../errors.js";
10
10
  // ─── Sync Action Types ───────────────────────────────────────────────────────
11
- // Mirror of SyncActionType from sync-engine/types.ts
11
+ // The action codes a server delta can carry, matching the wire protocol's
12
+ // action-type set.
12
13
  const SYNC_ACTION_VALUES = ['I', 'U', 'D', 'A', 'C', 'G', 'S', 'V'];
13
14
  // ─── Server Delta Schema ─────────────────────────────────────────────────────
14
15
  export const ServerDeltaSchema = z
@@ -23,11 +24,12 @@ export const ServerDeltaSchema = z
23
24
  })
24
25
  .passthrough();
25
26
  // ─── Model Value Schema ─────────────────────────────────────────────────────
26
- // Server model values arrive in multiple shapes depending on Go serialization:
27
- // - Array: already-parsed JSON array (most common)
28
- // - String: double-encoded JSON string from json.RawMessage
29
- // - null: from PostgreSQL jsonb_agg with no matching rows
30
- // This schema normalizes all variants into unknown[] before downstream use.
27
+ // A model's values can arrive in more than one shape depending on how the
28
+ // server serialized them:
29
+ // - Array: an already-parsed JSON array (the common case)
30
+ // - String: a JSON array still encoded as a string, which must be parsed
31
+ // - null: no matching rows
32
+ // This schema normalizes every variant into an array before downstream use.
31
33
  const ModelValueSchema = z
32
34
  .union([z.array(z.unknown()), z.string(), z.null()])
33
35
  .transform((val) => {
@@ -54,15 +56,17 @@ export const BootstrapResponseSchema = z
54
56
  deltaCount: z.number().optional(),
55
57
  failedModels: z.array(z.string()).optional(),
56
58
  timestamp: z.number().default(() => Date.now()),
57
- // Server's active schema hash (drift detection). Optional: absent from
58
- // older servers / tenants that have never pushed a schema.
59
+ // The server's active schema hash, used to detect schema drift. Optional:
60
+ // absent when the server predates this field or the tenant has never
61
+ // pushed a schema.
59
62
  schemaHash: z.string().optional(),
60
63
  })
61
64
  .passthrough();
62
65
  // ─── Parse Helpers ───────────────────────────────────────────────────────────
63
66
  /**
64
- * Validate a raw bootstrap response from the server.
65
- * Logs validation failures via SyncObservability and throws a descriptive error.
67
+ * Validates a raw bootstrap response from the server and returns the typed
68
+ * result. On failure it records a diagnostic breadcrumb and throws an
69
+ * {@link AbloValidationError} describing which fields were invalid.
66
70
  */
67
71
  export function parseBootstrapResponse(raw) {
68
72
  const result = BootstrapResponseSchema.safeParse(raw);