@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,18 @@
1
1
  /**
2
- * ConnectionManager single source of truth for the sync engine's
3
- * connection lifecycle. Absorbs the FSM every SDK consumer used to
4
- * rebuild by hand (apps/web's `ConnectionStore` was the reference
5
- * implementation 605 LOC of FSM + watchdog + backoff).
2
+ * Owns the sync engine's connection lifecycle: the state machine that carries a
3
+ * client from a healthy connection, through a dropout, and back to live. It
4
+ * watches the browser's online/offline and visibility events, probes real
5
+ * connectivity and session validity with {@link probeNetwork}, applies retry
6
+ * backoff with a ceiling and jitter, and runs a watchdog for the cases where
7
+ * the browser never fires an event (VPN flips, captive portals). When it
8
+ * decides to recover, it drives the reconnect sequence — bootstrap, then
9
+ * WebSocket connect — through the {@link ConnectionCallbacks.onReconnect}
10
+ * callback and reacts to the outcome.
6
11
  *
7
- * What it owns:
8
- * - Browser online/offline + visibility events
9
- * - Network probe orchestration (via `probeNetwork`)
10
- * - Session-validity checks (HEAD /api/auth/check)
11
- * - Retry backoff with ceiling, jitter, and offline-aware parking
12
- * - Watchdog for browser events that never fire (VPN, captive portal)
13
- * - The reconnect → bootstrap → WebSocket connect sequence
14
- *
15
- * What it DOES NOT own:
16
- * - The actual bootstrap / IndexedDB / ObjectPool work — that lives in
17
- * `BaseSyncedStore.performReconnect()`. This class calls it via the
18
- * `onReconnect` callback and reacts to the outcome.
19
- *
20
- * Designed to be embedded by `BaseSyncedStore`: one instance per store,
21
- * started on first successful connect, disposed on teardown.
12
+ * It deliberately does not perform the bootstrap, local-storage, or object-pool
13
+ * work itself; that lives in the embedding store, which the manager reaches
14
+ * only through its callbacks. One manager is created per store, started on the
15
+ * first successful connect and disposed on teardown.
22
16
  *
23
17
  * CONNECTED ──(socket drop)──► PROBING_NETWORK ──► RECONNECTING ──► CONNECTED
24
18
  * │ │ │
@@ -29,20 +23,15 @@
29
23
  * ▼
30
24
  * WAITING_FOR_NETWORK
31
25
  *
32
- * Includes three fixes over the original app-side FSM:
33
- * 1. `backoff` accepts `NETWORK_ONLINE` / `TAB_VISIBLE` jumps to
34
- * probing immediately when the network comes back, without
35
- * waiting for the backoff timer to elapse.
36
- * 2. `scheduleBackoff` parks in `waiting_for_network` (resetting
37
- * `attempt`) when `navigator.onLine === false` at max retries,
38
- * instead of hard-reloading an already-offline browser.
39
- * 3. A socket drop (`WS_DISCONNECTED`, typically code 1006) goes
40
- * STRAIGHT to `probing_network`, not the passive `offline` state.
41
- * 1006 is browser-local and carries no connectivity signal, so on a
42
- * healthy machine no `online`/`offline` event ever fires — parking in
43
- * `offline` stranded recovery until the 30s watchdog, long enough for
44
- * queued commits to roll back. Only a genuine OS-level `NETWORK_LOST`
45
- * parks in `offline` and waits for the `online` event.
26
+ * Three behaviors are worth calling out. A network return or tab focus during a
27
+ * backoff delay jumps straight to probing instead of waiting out the full
28
+ * interval. Reaching the retry ceiling while the browser reports offline parks
29
+ * in `waiting_for_network` rather than hard-reloading a browser that has no
30
+ * network. And a socket drop (close code 1006) goes straight to probing rather
31
+ * than the passive `offline` state 1006 is generated locally and carries no
32
+ * connectivity signal, so on a healthy machine no online/offline event ever
33
+ * fires; only a genuine operating-system network loss parks in `offline` to
34
+ * wait for the `online` event.
46
35
  */
47
36
  import { type ProbeResult } from './NetworkProbe.js';
48
37
  import type { AuthTokenGetter } from '../auth/credentialSource.js';
@@ -93,17 +82,18 @@ export interface ConnectionCallbacks {
93
82
  /** Run bootstrap + WebSocket reconnect. Returns the outcome. */
94
83
  onReconnect: () => Promise<'success' | 'session_error' | 'network_error'>;
95
84
  /**
96
- * Re-mint the short-lived access credential (the Stripe-style `ek_`/`rk_`)
97
- * and push it into the credential source, then report the outcome. Invoked
98
- * on `refreshing_credential` — i.e. when a probe found the access key stale
99
- * (`PROBE_CREDENTIAL_STALE`). Mirrors the `getToken` contract:
100
- * - `'refreshed'` → a fresh credential is in place; re-probe & reconnect.
101
- * - `'session_error'` → the LONG-LIVED login is gone (mint returned null →
102
- * 401/403); terminal sign out.
103
- * - `'network_error'` → couldn't reach the mint endpoint (offline/5xx/throw);
104
- * transient back off and retry, never sign out.
105
- * Optional: a deployment with no re-mint path (e.g. a static `apiKey`) omits
106
- * it, and the FSM falls back to a plain re-probe.
85
+ * Re-mints the short-lived access credential (the `ek_`/`rk_`) and pushes it
86
+ * into the credential source, then reports the outcome. Invoked in the
87
+ * `refreshing_credential` state that is, when a probe found the access key
88
+ * stale (`PROBE_CREDENTIAL_STALE`). The three outcomes map onto recovery:
89
+ * - `'refreshed'` → a fresh credential is in place; re-probe and reconnect.
90
+ * - `'session_error'` → the long-lived login itself is gone (the mint
91
+ * returned null, a 401/403); terminal, so sign out.
92
+ * - `'network_error'` → the mint endpoint was unreachable (offline, 5xx, or
93
+ * a throw); transient, so back off and retry rather
94
+ * than sign out.
95
+ * Optional: a deployment with no re-mint path (for example a static `apiKey`)
96
+ * omits it, and the state machine falls back to a plain re-probe.
107
97
  */
108
98
  onRefreshCredential?: () => Promise<'refreshed' | 'session_error' | 'network_error'>;
109
99
  /** Called when the session is confirmed expired — route to signin. */
@@ -111,12 +101,11 @@ export interface ConnectionCallbacks {
111
101
  /** Called to tear down the WebSocket when entering a dead state. */
112
102
  onDisconnectWebSocket: () => void;
113
103
  /**
114
- * Fired on every FSM state transition. Lets the embedding store
115
- * mirror recovery progress into its visible `syncStatus` so the UI
116
- * can show "Reconnecting…" instead of a sticky "offline" while the
117
- * FSM cycles through `probing_network` → `reconnecting` → `backoff`.
118
- * Optional omitting it preserves the previous behavior where the
119
- * FSM was opaque to the UI.
104
+ * Fires on every state transition. It lets the embedding store mirror
105
+ * recovery progress into its visible `syncStatus`, so the UI can show
106
+ * "Reconnecting…" instead of a sticky "offline" while the machine cycles
107
+ * through `probing_network` → `reconnecting` → `backoff`. Optional; when
108
+ * omitted, the state machine is simply opaque to the UI.
120
109
  */
121
110
  onStateChange?: (next: ConnectionState, prev: ConnectionState) => void;
122
111
  }
@@ -1,24 +1,18 @@
1
1
  /**
2
- * ConnectionManager single source of truth for the sync engine's
3
- * connection lifecycle. Absorbs the FSM every SDK consumer used to
4
- * rebuild by hand (apps/web's `ConnectionStore` was the reference
5
- * implementation 605 LOC of FSM + watchdog + backoff).
2
+ * Owns the sync engine's connection lifecycle: the state machine that carries a
3
+ * client from a healthy connection, through a dropout, and back to live. It
4
+ * watches the browser's online/offline and visibility events, probes real
5
+ * connectivity and session validity with {@link probeNetwork}, applies retry
6
+ * backoff with a ceiling and jitter, and runs a watchdog for the cases where
7
+ * the browser never fires an event (VPN flips, captive portals). When it
8
+ * decides to recover, it drives the reconnect sequence — bootstrap, then
9
+ * WebSocket connect — through the {@link ConnectionCallbacks.onReconnect}
10
+ * callback and reacts to the outcome.
6
11
  *
7
- * What it owns:
8
- * - Browser online/offline + visibility events
9
- * - Network probe orchestration (via `probeNetwork`)
10
- * - Session-validity checks (HEAD /api/auth/check)
11
- * - Retry backoff with ceiling, jitter, and offline-aware parking
12
- * - Watchdog for browser events that never fire (VPN, captive portal)
13
- * - The reconnect → bootstrap → WebSocket connect sequence
14
- *
15
- * What it DOES NOT own:
16
- * - The actual bootstrap / IndexedDB / ObjectPool work — that lives in
17
- * `BaseSyncedStore.performReconnect()`. This class calls it via the
18
- * `onReconnect` callback and reacts to the outcome.
19
- *
20
- * Designed to be embedded by `BaseSyncedStore`: one instance per store,
21
- * started on first successful connect, disposed on teardown.
12
+ * It deliberately does not perform the bootstrap, local-storage, or object-pool
13
+ * work itself; that lives in the embedding store, which the manager reaches
14
+ * only through its callbacks. One manager is created per store, started on the
15
+ * first successful connect and disposed on teardown.
22
16
  *
23
17
  * CONNECTED ──(socket drop)──► PROBING_NETWORK ──► RECONNECTING ──► CONNECTED
24
18
  * │ │ │
@@ -29,20 +23,15 @@
29
23
  * ▼
30
24
  * WAITING_FOR_NETWORK
31
25
  *
32
- * Includes three fixes over the original app-side FSM:
33
- * 1. `backoff` accepts `NETWORK_ONLINE` / `TAB_VISIBLE` jumps to
34
- * probing immediately when the network comes back, without
35
- * waiting for the backoff timer to elapse.
36
- * 2. `scheduleBackoff` parks in `waiting_for_network` (resetting
37
- * `attempt`) when `navigator.onLine === false` at max retries,
38
- * instead of hard-reloading an already-offline browser.
39
- * 3. A socket drop (`WS_DISCONNECTED`, typically code 1006) goes
40
- * STRAIGHT to `probing_network`, not the passive `offline` state.
41
- * 1006 is browser-local and carries no connectivity signal, so on a
42
- * healthy machine no `online`/`offline` event ever fires — parking in
43
- * `offline` stranded recovery until the 30s watchdog, long enough for
44
- * queued commits to roll back. Only a genuine OS-level `NETWORK_LOST`
45
- * parks in `offline` and waits for the `online` event.
26
+ * Three behaviors are worth calling out. A network return or tab focus during a
27
+ * backoff delay jumps straight to probing instead of waiting out the full
28
+ * interval. Reaching the retry ceiling while the browser reports offline parks
29
+ * in `waiting_for_network` rather than hard-reloading a browser that has no
30
+ * network. And a socket drop (close code 1006) goes straight to probing rather
31
+ * than the passive `offline` state 1006 is generated locally and carries no
32
+ * connectivity signal, so on a healthy machine no online/offline event ever
33
+ * fires; only a genuine operating-system network loss parks in `offline` to
34
+ * wait for the `online` event.
46
35
  */
47
36
  import { makeAutoObservable, runInAction } from 'mobx';
48
37
  import { getContext } from '../context.js';
@@ -158,13 +147,13 @@ export class ConnectionManager {
158
147
  // work.
159
148
  return 'offline';
160
149
  case 'WS_DISCONNECTED':
161
- // The socket died (typically code 1006) but the OS network is
162
- // almost certainly fine — 1006 is generated locally when the TCP
163
- // conn vanishes and carries NO connectivity signal, so the browser
164
- // fires no online/offline event. Probe IMMEDIATELY rather than
165
- // landing in the passive `offline` dead-end (which only escaped via
166
- // the 30s watchdog, long after queued commits rolled back). The
167
- // probe fast-fails if we genuinely ARE offline → waiting_for_network.
150
+ // The socket died (typically code 1006) but the operating-system
151
+ // network is almost certainly fine — 1006 is generated locally when
152
+ // the TCP connection vanishes and carries no connectivity signal, so
153
+ // the browser fires no online/offline event. Probe immediately rather
154
+ // than landing in the passive `offline` dead-end (which only escaped
155
+ // via the 30s watchdog, long after queued commits rolled back). The
156
+ // probe fast-fails if we genuinely are offline → waiting_for_network.
168
157
  return 'probing_network';
169
158
  case 'WS_SESSION_ERROR':
170
159
  case 'BOOTSTRAP_FAILED_SESSION':
@@ -233,15 +222,15 @@ export class ConnectionManager {
233
222
  return null;
234
223
  }
235
224
  case 'refreshing_credential':
236
- // Re-minting the short-lived access key (the Stripe-style `ek_`/`rk_`).
237
- // The login is presumed valid; this is NOT a sign-out state.
225
+ // Re-minting the short-lived access key (the `ek_`/`rk_`). The login is
226
+ // presumed valid; this is not a sign-out state.
238
227
  switch (event.type) {
239
228
  case 'CREDENTIAL_REFRESHED':
240
229
  // Fresh key in hand — re-probe so we reconnect with it.
241
230
  return 'probing_network';
242
231
  case 'BOOTSTRAP_FAILED_SESSION':
243
232
  // The re-mint hit a genuine 401/403: the long-lived login itself is
244
- // gone. THIS is the only path from here to sign-out.
233
+ // gone. This is the only path from here to sign-out.
245
234
  return 'session_expired';
246
235
  case 'RECONNECT_FAILED':
247
236
  // Couldn't reach the mint endpoint (offline/5xx/throw) — transient.
@@ -336,15 +325,15 @@ export class ConnectionManager {
336
325
  this.callbacks?.onDisconnectWebSocket();
337
326
  break;
338
327
  case 'probing_network':
339
- // A socket drop (`WS_DISCONNECTED`) now lands here directly so recovery
340
- // starts immediately. Tear the dead socket down FIRSTthis is what
328
+ // A socket drop (`WS_DISCONNECTED`) lands here directly so recovery
329
+ // starts immediately. Tear the dead socket down firstthat is what
341
330
  // sets SyncWebSocket's `isManualClose=true` and suppresses its own
342
- // scheduleReconnect, keeping the FSM the single reconnect authority on
343
- // the human path. The teardown runs synchronously inside the
344
- // `disconnected` emit, before `SyncWebSocket.onclose` checks the flag,
345
- // so the timing matches the previous `offline`-entry teardown. We gate
346
- // on the drop event specifically: the other paths into `probing_network`
347
- // (TAB_VISIBLE re-validation, handshake retry, backoff elapse) must NOT
331
+ // reconnect, keeping this machine the single reconnect authority on the
332
+ // human path. The teardown runs synchronously inside the `disconnected`
333
+ // emit, before `SyncWebSocket.onclose` checks the flag, so the timing
334
+ // matches the previous `offline`-entry teardown. We gate on the drop
335
+ // event specifically: the other paths into `probing_network`
336
+ // (tab-focus re-validation, handshake retry, backoff elapse) must not
348
337
  // tear down a socket that may still be live.
349
338
  if (event.type === 'WS_DISCONNECTED') {
350
339
  this.callbacks?.onDisconnectWebSocket();
@@ -364,11 +353,12 @@ export class ConnectionManager {
364
353
  this.scheduleBackoff();
365
354
  break;
366
355
  case 'auth_blocked':
367
- // Stop — reachable but the credential was rejected (e.g.
368
- // api_key_required / jwt_issuer_untrusted from the data plane). Neither
369
- // reconnecting nor re-auth fixes it. Drop the socket and wait for a
370
- // manual retry / re-probe. Crucially NOT onSessionExpired (no sign-out)
371
- // and NOT a reconnect — that's the whole point of this state.
356
+ // Stop here the server is reachable but rejected the credential (for
357
+ // example api_key_required or jwt_issuer_untrusted from the data plane).
358
+ // Neither reconnecting nor re-authenticating fixes it. Drop the socket
359
+ // and wait for a manual retry or a re-probe. This deliberately does not
360
+ // call onSessionExpired (no sign-out) and does not reconnect — that is
361
+ // the whole point of the state.
372
362
  this.clearBackoffTimer();
373
363
  this.callbacks?.onDisconnectWebSocket();
374
364
  getContext().observability.breadcrumb('Auth blocked — reachable but credential rejected; not reconnecting or signing out', 'sync.offline', 'error');
@@ -602,16 +592,15 @@ export class ConnectionManager {
602
592
  this.watchdogTimer = setInterval(() => {
603
593
  if (this.disposed)
604
594
  return;
605
- // "Stuck" = parked in a non-active recovery state (offline,
606
- // waiting_for_network, backoff). We deliberately do NOT gate on
607
- // `navigator.onLine === true` here: per MDN, `navigator.onLine`
608
- // is only reliable when it returns false ("definitely offline"),
609
- // and even that lies after laptop wake / VPN flips. Gating the
610
- // watchdog on `onLine` was the actual "offline forever" bug
611
- // when the browser briefly reported offline and never re-fired
612
- // the `online` event, the FSM had no escape from `'offline'`.
613
- // The probe itself fast-fails when truly offline (NetworkProbe.ts),
614
- // so an unconditional retry costs nothing in the genuine case.
595
+ // "Stuck" means parked in a non-active recovery state (offline,
596
+ // waiting_for_network, backoff). This deliberately does not gate on
597
+ // `navigator.onLine === true`: per MDN, `navigator.onLine` is only
598
+ // reliable when it returns false ("definitely offline"), and even that
599
+ // lies after laptop wake or VPN flips. Gating the watchdog on `onLine`
600
+ // was what stranded the machine in `offline` forever when the browser
601
+ // briefly reported offline and never re-fired the `online` event, there
602
+ // was no escape. The probe itself fast-fails when truly offline, so an
603
+ // unconditional retry costs nothing in the genuine case.
615
604
  const isStuck = this.state !== 'connected' &&
616
605
  this.state !== 'session_expired' &&
617
606
  this.state !== 'probing_network' &&
@@ -1,36 +1,33 @@
1
1
  /**
2
- * NetworkProbe - Reliable network + session connectivity detection
2
+ * Detects real network and session connectivity for the sync engine. It exists
3
+ * because `navigator.onLine` is unreliable: it reports true whenever the device
4
+ * has a local network connection, even with no route to the internet, and after
5
+ * sleep/wake it can report true before Wi-Fi or DNS are working again.
3
6
  *
4
- * navigator.onLine is unreliable: it reports true whenever the device has a LAN
5
- * connection, even without actual internet access (MDN docs confirm this).
6
- * After laptop sleep/wake, it may report true before WiFi/DNS are functional.
7
- *
8
- * This module provides an authenticated probe against the sync server to verify
9
- * real connectivity + credential validity in a single round-trip. The probe
10
- * hits `/api/auth/check`, which runs the SAME auth middleware as the WebSocket
11
- * upgrade path, and classifies the response into a single {@link ProbeOutcome}
12
- * via the closed recovery taxonomy ({@link classifyRecovery}):
7
+ * The probe makes one authenticated request to the sync server's
8
+ * `/api/auth/check` endpoint which runs the same auth middleware as the
9
+ * WebSocket upgrade and classifies the response into a single
10
+ * {@link ProbeOutcome} through the recovery taxonomy ({@link classifyRecovery}):
13
11
  * 204 No Content → `reachable` (credential valid)
14
- * 401 `apikey_expired` (ephemeral key) → `credential_stale` (re-mint & retry, NO sign-out)
12
+ * 401 `apikey_expired` (ephemeral key) → `credential_stale` (re-mint and retry, no sign-out)
15
13
  * 401 `session_expired` / bare 401 → `session_expired` (sign out)
16
- * 401/403 credential-type/config/perm → `auth_blocked` (stop, no loop, no sign-out)
17
- * network fail / offline → `unreachable`
14
+ * 401/403 credential-type/config/perm → `auth_blocked` (stop; no loop, no sign-out)
15
+ * network failure / offline → `unreachable`
18
16
  *
19
- * This closes a real gap: the browser's WebSocket API hides HTTP status from
20
- * the handshake, so a 401 on the WS upgrade surfaces only as `close code
21
- * 1006`. Without this HTTP probe, the client cannot distinguish auth failure
22
- * from a network blip and loops reconnecting forever instead of redirecting
23
- * the user to sign-in.
17
+ * This closes a real gap. The browser's WebSocket API hides the HTTP status of
18
+ * a failed handshake, so a 401 on the upgrade surfaces only as close code 1006.
19
+ * Without this HTTP probe, the client cannot tell an auth failure from a network
20
+ * blip, and loops reconnecting forever instead of sending the user to sign in.
24
21
  *
25
22
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine
26
23
  */
27
24
  import { z } from 'zod';
28
25
  import { type AuthTokenGetter } from '../auth/credentialSource.js';
29
26
  /**
30
- * The closed set of probe outcomes one value carrying both reachability and
31
- * credential disposition, so the {@link ConnectionManager} branches on a single
32
- * exhaustive discriminant instead of reconstructing claim from a trio of
33
- * booleans. Mirrors the {@link RecoveryClass} taxonomy at the connectivity tier.
27
+ * The complete set of probe outcomes. Each value carries both reachability and
28
+ * credential state, so {@link ConnectionManager} can branch on one exhaustive
29
+ * discriminant instead of piecing the situation together from several booleans.
30
+ * It mirrors the {@link RecoveryClass} taxonomy at the connectivity layer.
34
31
  */
35
32
  export declare const PROBE_OUTCOMES: readonly ["reachable", "unreachable", "session_expired", "credential_stale", "auth_blocked"];
36
33
  /** Zod enum derived from {@link PROBE_OUTCOMES}. */
@@ -74,13 +71,11 @@ export interface NetworkProbeOptions {
74
71
  authToken?: string | null;
75
72
  }
76
73
  /**
77
- * Probe the sync engine server with a lightweight HEAD request.
78
- *
79
- * Returns reachability AND session status in a single call, so the
80
- * ConnectionStore can make the right state transition without guessing.
74
+ * Probes the sync server with a lightweight HEAD request, returning both
75
+ * reachability and session status in a single call so {@link ConnectionManager}
76
+ * can pick the right state transition without guessing.
81
77
  *
82
- * @param input The sync-server base URL (HTTP or WS scheme accepted), or an
83
- * options bag with `authToken`. A bare string is still accepted
84
- * for backwards compatibility.
78
+ * @param input The sync-server base URL (an HTTP or WS scheme is accepted), or
79
+ * an options bag. A bare string is also accepted.
85
80
  */
86
81
  export declare function probeNetwork(input?: string | NetworkProbeOptions): Promise<ProbeResult>;
@@ -1,26 +1,23 @@
1
1
  /**
2
- * NetworkProbe - Reliable network + session connectivity detection
2
+ * Detects real network and session connectivity for the sync engine. It exists
3
+ * because `navigator.onLine` is unreliable: it reports true whenever the device
4
+ * has a local network connection, even with no route to the internet, and after
5
+ * sleep/wake it can report true before Wi-Fi or DNS are working again.
3
6
  *
4
- * navigator.onLine is unreliable: it reports true whenever the device has a LAN
5
- * connection, even without actual internet access (MDN docs confirm this).
6
- * After laptop sleep/wake, it may report true before WiFi/DNS are functional.
7
- *
8
- * This module provides an authenticated probe against the sync server to verify
9
- * real connectivity + credential validity in a single round-trip. The probe
10
- * hits `/api/auth/check`, which runs the SAME auth middleware as the WebSocket
11
- * upgrade path, and classifies the response into a single {@link ProbeOutcome}
12
- * via the closed recovery taxonomy ({@link classifyRecovery}):
7
+ * The probe makes one authenticated request to the sync server's
8
+ * `/api/auth/check` endpoint which runs the same auth middleware as the
9
+ * WebSocket upgrade and classifies the response into a single
10
+ * {@link ProbeOutcome} through the recovery taxonomy ({@link classifyRecovery}):
13
11
  * 204 No Content → `reachable` (credential valid)
14
- * 401 `apikey_expired` (ephemeral key) → `credential_stale` (re-mint & retry, NO sign-out)
12
+ * 401 `apikey_expired` (ephemeral key) → `credential_stale` (re-mint and retry, no sign-out)
15
13
  * 401 `session_expired` / bare 401 → `session_expired` (sign out)
16
- * 401/403 credential-type/config/perm → `auth_blocked` (stop, no loop, no sign-out)
17
- * network fail / offline → `unreachable`
14
+ * 401/403 credential-type/config/perm → `auth_blocked` (stop; no loop, no sign-out)
15
+ * network failure / offline → `unreachable`
18
16
  *
19
- * This closes a real gap: the browser's WebSocket API hides HTTP status from
20
- * the handshake, so a 401 on the WS upgrade surfaces only as `close code
21
- * 1006`. Without this HTTP probe, the client cannot distinguish auth failure
22
- * from a network blip and loops reconnecting forever instead of redirecting
23
- * the user to sign-in.
17
+ * This closes a real gap. The browser's WebSocket API hides the HTTP status of
18
+ * a failed handshake, so a 401 on the upgrade surfaces only as close code 1006.
19
+ * Without this HTTP probe, the client cannot tell an auth failure from a network
20
+ * blip, and loops reconnecting forever instead of sending the user to sign in.
24
21
  *
25
22
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine
26
23
  */
@@ -30,24 +27,24 @@ import { classifyRecovery } from '../errors.js';
30
27
  import { withAuthHeaders } from '../auth/credentialSource.js';
31
28
  import { ABLO_DEFAULT_BASE_URL } from '../client/hostedEndpoints.js';
32
29
  /**
33
- * The closed set of probe outcomes one value carrying both reachability and
34
- * credential disposition, so the {@link ConnectionManager} branches on a single
35
- * exhaustive discriminant instead of reconstructing claim from a trio of
36
- * booleans. Mirrors the {@link RecoveryClass} taxonomy at the connectivity tier.
30
+ * The complete set of probe outcomes. Each value carries both reachability and
31
+ * credential state, so {@link ConnectionManager} can branch on one exhaustive
32
+ * discriminant instead of piecing the situation together from several booleans.
33
+ * It mirrors the {@link RecoveryClass} taxonomy at the connectivity layer.
37
34
  */
38
35
  export const PROBE_OUTCOMES = [
39
36
  /** Server reachable and the access credential is currently valid. */
40
37
  'reachable',
41
- /** Could not reach the server (offline / DNS / TLS / timeout). */
38
+ /** Could not reach the server (offline, DNS, TLS, or timeout). */
42
39
  'unreachable',
43
- /** Reachable, but the long-lived login is gone terminal, sign out. */
40
+ /** Reachable, but the long-lived login is gone. Terminal: sign out. */
44
41
  'session_expired',
45
- /** Reachable, but the ephemeral access key (`ek_`/`rk_`) expired → silently
46
- * re-mint a fresh key from the still-valid login and retry. NOT a sign-out. */
42
+ /** Reachable, but the ephemeral access key (`ek_`/`rk_`) expired. Silently
43
+ * re-mint a fresh key from the still-valid login and retry; not a sign-out. */
47
44
  'credential_stale',
48
- /** Reachable, but the credential TYPE/config was rejected (wrong key kind,
49
- * untrusted issuer, no org, a 403) stop; neither reconnecting nor re-auth
50
- * helps. Distinct from a sign-out. */
45
+ /** Reachable, but the credential's type or configuration was rejected (wrong
46
+ * key kind, untrusted issuer, no organization, or a 403). Stop: neither
47
+ * reconnecting nor re-authenticating helps. Distinct from a sign-out. */
51
48
  'auth_blocked',
52
49
  ];
53
50
  /** Zod enum derived from {@link PROBE_OUTCOMES}. */
@@ -62,13 +59,11 @@ const PROBE_TIMEOUT_MS = 4000;
62
59
  /**
63
60
  * Derive the probe URL from a sync-server base URL. Accepts `ws://`,
64
61
  * `wss://`, `http://`, `https://`, or a bare host — mirrors the
65
- * normalisation in `BootstrapHelper` / `createSyncEngine`.
62
+ * normalisation in `BootstrapFetcher` / `createSyncEngine`.
66
63
  */
67
64
  function resolveProbeUrl(baseUrl) {
68
65
  // No explicit baseUrl → probe the canonical hosted endpoint, matching the
69
- // `Ablo()` default. (This used to fall back to the REMOVED Go engine's
70
- // `NEXT_PUBLIC_GO_SERVER_URL` and then `http://localhost:8080`, so a probe
71
- // without a baseUrl reported a healthy production deployment as offline.)
66
+ // `Ablo()` default.
72
67
  const resolved = baseUrl ?? ABLO_DEFAULT_BASE_URL;
73
68
  // Normalize ws → http so fetch() accepts the URL. Strip any trailing slash
74
69
  // so we don't produce `//api/auth/check`.
@@ -76,25 +71,23 @@ function resolveProbeUrl(baseUrl) {
76
71
  return `${httpBase}/api/auth/check`;
77
72
  }
78
73
  /**
79
- * Probe the sync engine server with a lightweight HEAD request.
80
- *
81
- * Returns reachability AND session status in a single call, so the
82
- * ConnectionStore can make the right state transition without guessing.
74
+ * Probes the sync server with a lightweight HEAD request, returning both
75
+ * reachability and session status in a single call so {@link ConnectionManager}
76
+ * can pick the right state transition without guessing.
83
77
  *
84
- * @param input The sync-server base URL (HTTP or WS scheme accepted), or an
85
- * options bag with `authToken`. A bare string is still accepted
86
- * for backwards compatibility.
78
+ * @param input The sync-server base URL (an HTTP or WS scheme is accepted), or
79
+ * an options bag. A bare string is also accepted.
87
80
  */
88
81
  export async function probeNetwork(input) {
89
82
  const baseUrl = typeof input === 'string' ? input : input?.baseUrl;
90
83
  const getAuthToken = typeof input === 'string' ? undefined : input?.getAuthToken;
91
84
  const authToken = typeof input === 'string' ? undefined : input?.authToken;
92
85
  const url = resolveProbeUrl(baseUrl);
93
- // Fast-fail: if navigator.onLine is false, skip the probe entirely.
94
- // This is the ONE case where navigator.onLine is reliable (MDN: "false
95
- // means definitely offline"). Use `=== false` rather than `!onLine`
96
- // because Node 22+ exposes `navigator` with `onLine === undefined`,
97
- // and `!undefined === true` would short-circuit the probe server-side.
86
+ // Fast-fail: if navigator.onLine is false, skip the probe entirely. This is
87
+ // the one case where navigator.onLine is reliable (MDN: "false means
88
+ // definitely offline"). Use `=== false` rather than `!onLine` because Node
89
+ // 22+ exposes `navigator` with `onLine === undefined`, and `!undefined` is
90
+ // true, which would short-circuit the probe server-side.
98
91
  if (typeof navigator !== 'undefined' && navigator.onLine === false) {
99
92
  return { outcome: 'unreachable', latencyMs: null };
100
93
  }
@@ -110,13 +103,13 @@ export async function probeNetwork(input) {
110
103
  headers,
111
104
  });
112
105
  const latencyMs = Math.round(performance.now() - start);
113
- // The probe is a HEAD (no body), but the sync-server sets `X-Auth-Failure:
114
- // <code>` on every auth rejection. Route the code through the closed
115
- // recovery taxonomy so each failure mode gets its correct outcome — the
116
- // whole reason this taxonomy exists: an expired ephemeral key
117
- // (`access_credential_expiry`) must re-mint, NOT sign the user out the way
118
- // a genuine login expiry (`session_expiry`) does, and NOT wedge the way a
119
- // credential-type/config rejection (`auth_blocked`) does.
106
+ // The probe is a HEAD request (no body), but the server sets
107
+ // `X-Auth-Failure: <code>` on every auth rejection. Route the code through
108
+ // the recovery taxonomy so each failure mode gets its correct outcome. That
109
+ // distinction is the whole point: an expired ephemeral key
110
+ // (`access_credential_expiry`) must re-mint, not sign the user out the way a
111
+ // genuine login expiry (`session_expiry`) does, and not wedge the way a
112
+ // credential type or configuration rejection (`auth_blocked`) does.
120
113
  const authFailure = response.headers.get('x-auth-failure');
121
114
  if (authFailure) {
122
115
  const recovery = classifyRecovery(authFailure);
@@ -138,10 +131,11 @@ export async function probeNetwork(input) {
138
131
  case 'auth_blocked':
139
132
  case 'permission':
140
133
  case 'none':
141
- // A non-expiry auth rejection — wrong credential type/config, a 403,
142
- // or an auth-tagged code this SDK doesn't recognise. Re-auth re-mints
143
- // the same rejected credential and retrying won't help, so STOP
144
- // rather than reconnect-loop or sign the user out.
134
+ // A non-expiry auth rejection — wrong credential type or config, a
135
+ // 403, or an auth-tagged code this SDK does not recognise.
136
+ // Re-authenticating re-mints the same rejected credential and
137
+ // retrying will not help, so stop rather than reconnect-loop or sign
138
+ // the user out.
145
139
  getContext().logger.debug('[NetworkProbe] Reachable but auth-blocked (non-retryable, non-expiry)', {
146
140
  status: response.status,
147
141
  code: authFailure,
@@ -160,20 +154,20 @@ export async function probeNetwork(input) {
160
154
  }
161
155
  }
162
156
  else if (response.status === 401) {
163
- // Bare 401 with no READABLE structured code. This is AMBIGUOUS and must
164
- // NOT sign the user out on its own — two common causes are both
157
+ // Bare 401 with no readable structured code. This is ambiguous and must
158
+ // not sign the user out on its own — two common causes are both
165
159
  // recoverable, and only one is a real logout:
166
- // 1. The server DID send `X-Auth-Failure: apikey_expired`, but it's a
167
- // custom header on a cross-origin response and the server didn't list
168
- // it in `Access-Control-Expose-Headers`, so the browser stripped it to
169
- // null (the network-change logout bug). The access key just needs a
170
- // re-mint.
171
- // 2. A genuinely expired access key on a non-Ablo proxy / cookie path.
172
- // So route to `credential_stale`: the FSM attempts a re-mint, and the ONLY
173
- // way to actually sign out is the re-mint resolving `null` (login truly
174
- // gone). If no refresher is wired, the bounded attempt counter falls
175
- // through to `auth_blocked` (stop) still never a spurious logout. This
176
- // upholds the invariant: null is the only terminal path, never a bare 401.
160
+ // 1. The server did send `X-Auth-Failure: apikey_expired`, but it is a
161
+ // custom header on a cross-origin response the server did not list in
162
+ // `Access-Control-Expose-Headers`, so the browser stripped it to
163
+ // null. The access key just needs a re-mint.
164
+ // 2. A genuinely expired access key on a non-Ablo proxy or cookie path.
165
+ // So route to `credential_stale`: the state machine attempts a re-mint,
166
+ // and the only way to actually sign out is that re-mint resolving `null`
167
+ // (the login is truly gone). If no refresher is wired, the bounded attempt
168
+ // counter falls through to `auth_blocked` (stop) still never a spurious
169
+ // logout. The invariant holds: null is the only terminal path, never a
170
+ // bare 401.
177
171
  getContext().logger.info('[NetworkProbe] Server reachable, bare 401 — re-mint (not sign-out)', {
178
172
  latencyMs,
179
173
  });