@abloatai/ablo 0.25.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/AGENTS.md +5 -3
  2. package/CHANGELOG.md +34 -0
  3. package/README.md +104 -88
  4. package/dist/BaseSyncedStore.d.ts +140 -266
  5. package/dist/BaseSyncedStore.js +338 -739
  6. package/dist/Database.d.ts +62 -77
  7. package/dist/Database.js +106 -127
  8. package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
  9. package/dist/{ObjectPool.js → InstanceCache.js} +91 -83
  10. package/dist/LazyReferenceCollection.d.ts +11 -15
  11. package/dist/LazyReferenceCollection.js +16 -15
  12. package/dist/Model.d.ts +37 -52
  13. package/dist/Model.js +52 -69
  14. package/dist/ModelRegistry.d.ts +46 -25
  15. package/dist/ModelRegistry.js +32 -30
  16. package/dist/NetworkMonitor.d.ts +5 -6
  17. package/dist/NetworkMonitor.js +6 -7
  18. package/dist/SyncClient.d.ts +119 -109
  19. package/dist/SyncClient.js +303 -224
  20. package/dist/SyncEngineContext.d.ts +1 -3
  21. package/dist/SyncEngineContext.js +1 -2
  22. package/dist/adapters/alwaysOnline.d.ts +6 -8
  23. package/dist/adapters/alwaysOnline.js +6 -8
  24. package/dist/adapters/inMemoryStorage.d.ts +9 -9
  25. package/dist/adapters/inMemoryStorage.js +9 -9
  26. package/dist/agent/Agent.d.ts +39 -31
  27. package/dist/agent/Agent.js +35 -23
  28. package/dist/agent/index.d.ts +4 -4
  29. package/dist/agent/index.js +5 -5
  30. package/dist/agent/session.d.ts +47 -44
  31. package/dist/agent/session.js +37 -48
  32. package/dist/agent/types.d.ts +26 -31
  33. package/dist/agent/types.js +6 -7
  34. package/dist/ai-sdk/coordinatedTool.d.ts +108 -0
  35. package/dist/ai-sdk/{coordinated-tool.js → coordinatedTool.js} +44 -38
  36. package/dist/ai-sdk/coordinationContext.d.ts +46 -0
  37. package/dist/ai-sdk/{coordination-context.js → coordinationContext.js} +30 -31
  38. package/dist/ai-sdk/index.d.ts +25 -22
  39. package/dist/ai-sdk/index.js +25 -22
  40. package/dist/ai-sdk/wrap.d.ts +7 -8
  41. package/dist/ai-sdk/wrap.js +2 -2
  42. package/dist/auth/credentialPolicy.d.ts +74 -71
  43. package/dist/auth/credentialPolicy.js +51 -56
  44. package/dist/auth/credentialSource.d.ts +7 -18
  45. package/dist/auth/credentialSource.js +10 -18
  46. package/dist/auth/index.d.ts +59 -58
  47. package/dist/auth/index.js +34 -40
  48. package/dist/auth/schemas.d.ts +5 -4
  49. package/dist/auth/schemas.js +5 -4
  50. package/dist/batching/index.d.ts +19 -21
  51. package/dist/batching/index.js +14 -17
  52. package/dist/cli.cjs +483 -369
  53. package/dist/client/Ablo.d.ts +107 -836
  54. package/dist/client/Ablo.js +174 -833
  55. package/dist/client/ApiClient.d.ts +44 -20
  56. package/dist/client/ApiClient.js +193 -44
  57. package/dist/client/auth.d.ts +51 -60
  58. package/dist/client/auth.js +137 -110
  59. package/dist/client/claimHeartbeatLoop.d.ts +50 -0
  60. package/dist/client/claimHeartbeatLoop.js +88 -0
  61. package/dist/client/consoleLogger.d.ts +35 -0
  62. package/dist/client/consoleLogger.js +44 -0
  63. package/dist/client/createInternalComponents.d.ts +14 -17
  64. package/dist/client/createInternalComponents.js +26 -31
  65. package/dist/client/createModelProxy.d.ts +130 -120
  66. package/dist/client/createModelProxy.js +158 -124
  67. package/dist/client/credentialEndpoint.d.ts +61 -0
  68. package/dist/client/credentialEndpoint.js +86 -0
  69. package/dist/client/functionalUpdate.d.ts +29 -27
  70. package/dist/client/functionalUpdate.js +21 -21
  71. package/dist/client/hostedEndpoints.d.ts +21 -0
  72. package/dist/client/hostedEndpoints.js +21 -0
  73. package/dist/client/httpClient.d.ts +58 -54
  74. package/dist/client/httpClient.js +29 -31
  75. package/dist/client/identity.d.ts +15 -20
  76. package/dist/client/identity.js +49 -59
  77. package/dist/client/modelRegistration.d.ts +10 -0
  78. package/dist/client/modelRegistration.js +301 -0
  79. package/dist/client/options.d.ts +373 -0
  80. package/dist/client/options.js +6 -0
  81. package/dist/client/registerDataSource.d.ts +9 -9
  82. package/dist/client/registerDataSource.js +15 -16
  83. package/dist/client/resourceTypes.d.ts +333 -0
  84. package/dist/client/resourceTypes.js +7 -0
  85. package/dist/client/schemaConfig.d.ts +44 -0
  86. package/dist/client/schemaConfig.js +176 -0
  87. package/dist/client/sessionMint.d.ts +17 -13
  88. package/dist/client/sessionMint.js +26 -31
  89. package/dist/client/validateAbloOptions.d.ts +12 -14
  90. package/dist/client/validateAbloOptions.js +9 -10
  91. package/dist/client/writeOptionsSchema.d.ts +18 -16
  92. package/dist/client/writeOptionsSchema.js +23 -20
  93. package/dist/client/wsMutationExecutor.d.ts +28 -0
  94. package/dist/client/wsMutationExecutor.js +71 -0
  95. package/dist/context.d.ts +6 -4
  96. package/dist/context.js +6 -7
  97. package/dist/coordination/index.d.ts +13 -4
  98. package/dist/coordination/index.js +29 -4
  99. package/dist/coordination/schema.d.ts +176 -128
  100. package/dist/coordination/schema.js +197 -133
  101. package/dist/coordination/trace.d.ts +9 -11
  102. package/dist/coordination/trace.js +13 -15
  103. package/dist/core/DatabaseManager.d.ts +5 -8
  104. package/dist/core/DatabaseManager.js +38 -40
  105. package/dist/core/QueryProcessor.d.ts +7 -9
  106. package/dist/core/QueryProcessor.js +27 -34
  107. package/dist/core/QueryView.d.ts +17 -5
  108. package/dist/core/QueryView.js +6 -7
  109. package/dist/core/StoreManager.d.ts +14 -16
  110. package/dist/core/StoreManager.js +26 -25
  111. package/dist/core/ViewRegistry.d.ts +5 -5
  112. package/dist/core/ViewRegistry.js +4 -4
  113. package/dist/core/index.d.ts +18 -13
  114. package/dist/core/index.js +32 -26
  115. package/dist/core/openIDBWithTimeout.d.ts +38 -36
  116. package/dist/core/openIDBWithTimeout.js +57 -54
  117. package/dist/core/queryUtils.d.ts +45 -0
  118. package/dist/core/queryUtils.js +69 -0
  119. package/dist/core/storeContract.d.ts +145 -0
  120. package/dist/core/storeContract.js +12 -0
  121. package/dist/environment.d.ts +28 -0
  122. package/dist/environment.js +21 -0
  123. package/dist/errorCodes.d.ts +118 -101
  124. package/dist/errorCodes.js +277 -260
  125. package/dist/errors.d.ts +170 -165
  126. package/dist/errors.js +161 -151
  127. package/dist/index.d.ts +30 -27
  128. package/dist/index.js +90 -82
  129. package/dist/interfaces/index.d.ts +108 -133
  130. package/dist/interfaces/index.js +5 -4
  131. package/dist/keys/index.d.ts +27 -29
  132. package/dist/keys/index.js +59 -49
  133. package/dist/mutators/RecordingTransaction.d.ts +16 -16
  134. package/dist/mutators/RecordingTransaction.js +31 -37
  135. package/dist/mutators/Transaction.d.ts +18 -26
  136. package/dist/mutators/Transaction.js +14 -20
  137. package/dist/mutators/UndoManager.d.ts +122 -131
  138. package/dist/mutators/UndoManager.js +149 -155
  139. package/dist/mutators/defineMutators.d.ts +24 -37
  140. package/dist/mutators/defineMutators.js +14 -20
  141. package/dist/mutators/inverseOp.d.ts +12 -15
  142. package/dist/mutators/inverseOp.js +12 -15
  143. package/dist/mutators/mutateActions.d.ts +10 -9
  144. package/dist/mutators/mutateActions.js +1 -1
  145. package/dist/mutators/readerActions.d.ts +9 -8
  146. package/dist/mutators/readerActions.js +2 -2
  147. package/dist/mutators/undoApply.d.ts +31 -27
  148. package/dist/mutators/undoApply.js +26 -24
  149. package/dist/policy/index.d.ts +5 -3
  150. package/dist/policy/index.js +5 -3
  151. package/dist/policy/types.d.ts +105 -101
  152. package/dist/policy/types.js +67 -66
  153. package/dist/query/client.d.ts +32 -16
  154. package/dist/query/client.js +103 -72
  155. package/dist/query/types.d.ts +37 -60
  156. package/dist/query/types.js +13 -33
  157. package/dist/react/AbloProvider.d.ts +7 -11
  158. package/dist/react/AbloProvider.js +24 -17
  159. package/dist/react/context.d.ts +27 -146
  160. package/dist/react/context.js +9 -10
  161. package/dist/react/index.d.ts +41 -42
  162. package/dist/react/index.js +37 -38
  163. package/dist/react/internalContext.d.ts +17 -19
  164. package/dist/react/useAblo.d.ts +23 -22
  165. package/dist/react/useAblo.js +17 -15
  166. package/dist/react/useCurrentUserId.d.ts +8 -7
  167. package/dist/react/useCurrentUserId.js +8 -7
  168. package/dist/react/useErrorListener.d.ts +7 -7
  169. package/dist/react/useErrorListener.js +11 -12
  170. package/dist/react/useMutationFailureListener.d.ts +8 -8
  171. package/dist/react/useMutationFailureListener.js +9 -9
  172. package/dist/react/useMutators.d.ts +11 -11
  173. package/dist/react/useMutators.js +10 -4
  174. package/dist/react/useReactive.js +2 -3
  175. package/dist/react/useSyncStatus.d.ts +4 -6
  176. package/dist/react/useUndoScope.d.ts +7 -9
  177. package/dist/react/useUndoScope.js +3 -3
  178. package/dist/schema/coordination.d.ts +21 -25
  179. package/dist/schema/coordination.js +21 -25
  180. package/dist/schema/ddl.d.ts +43 -39
  181. package/dist/schema/ddl.js +75 -68
  182. package/dist/schema/ddlLock.d.ts +35 -0
  183. package/dist/schema/ddlLock.js +46 -0
  184. package/dist/schema/diff.d.ts +99 -61
  185. package/dist/schema/diff.js +43 -34
  186. package/dist/schema/field.d.ts +37 -42
  187. package/dist/schema/field.js +36 -49
  188. package/dist/schema/generate.d.ts +12 -12
  189. package/dist/schema/generate.js +12 -12
  190. package/dist/schema/index.d.ts +5 -4
  191. package/dist/schema/index.js +29 -21
  192. package/dist/schema/model.d.ts +121 -146
  193. package/dist/schema/model.js +24 -35
  194. package/dist/schema/openapi.d.ts +10 -9
  195. package/dist/schema/openapi.js +7 -1
  196. package/dist/schema/queries.d.ts +30 -32
  197. package/dist/schema/queries.js +24 -25
  198. package/dist/schema/relation.d.ts +89 -99
  199. package/dist/schema/relation.js +13 -13
  200. package/dist/schema/residency.d.ts +38 -0
  201. package/dist/schema/residency.js +30 -0
  202. package/dist/schema/roles.d.ts +45 -27
  203. package/dist/schema/roles.js +52 -21
  204. package/dist/schema/schema.d.ts +36 -45
  205. package/dist/schema/schema.js +42 -39
  206. package/dist/schema/select.d.ts +13 -13
  207. package/dist/schema/select.js +13 -13
  208. package/dist/schema/serialize.d.ts +36 -39
  209. package/dist/schema/serialize.js +27 -31
  210. package/dist/schema/sugar.d.ts +17 -32
  211. package/dist/schema/sugar.js +14 -29
  212. package/dist/schema/{sync-delta-row.d.ts → syncDeltaRow.d.ts} +27 -50
  213. package/dist/schema/syncDeltaRow.js +89 -0
  214. package/dist/schema/tenancy.d.ts +44 -46
  215. package/dist/schema/tenancy.js +46 -48
  216. package/dist/server/adapter.d.ts +58 -58
  217. package/dist/server/adapter.js +13 -14
  218. package/dist/server/commit.d.ts +60 -64
  219. package/dist/server/index.d.ts +9 -10
  220. package/dist/server/index.js +1 -1
  221. package/dist/server/readConfig.d.ts +70 -0
  222. package/dist/server/readConfig.js +8 -0
  223. package/dist/server/storageMode.d.ts +23 -0
  224. package/dist/server/storageMode.js +17 -0
  225. package/dist/source/adapter.d.ts +31 -26
  226. package/dist/source/adapter.js +10 -10
  227. package/dist/source/adapters/drizzle.d.ts +28 -23
  228. package/dist/source/adapters/drizzle.js +34 -28
  229. package/dist/source/adapters/kysely.d.ts +27 -25
  230. package/dist/source/adapters/kysely.js +28 -26
  231. package/dist/source/adapters/memory.d.ts +8 -7
  232. package/dist/source/adapters/memory.js +10 -9
  233. package/dist/source/adapters/prisma.d.ts +13 -12
  234. package/dist/source/adapters/prisma.js +27 -29
  235. package/dist/source/conformance.d.ts +18 -11
  236. package/dist/source/conformance.js +27 -19
  237. package/dist/source/connector.d.ts +31 -32
  238. package/dist/source/connector.js +30 -28
  239. package/dist/source/connectorProtocol.d.ts +160 -0
  240. package/dist/source/connectorProtocol.js +162 -0
  241. package/dist/source/contract.d.ts +26 -27
  242. package/dist/source/contract.js +28 -29
  243. package/dist/source/factory.d.ts +94 -0
  244. package/dist/source/factory.js +268 -0
  245. package/dist/source/index.d.ts +10 -462
  246. package/dist/source/index.js +17 -421
  247. package/dist/source/migrations.d.ts +9 -9
  248. package/dist/source/migrations.js +9 -9
  249. package/dist/source/next.d.ts +10 -11
  250. package/dist/source/next.js +7 -8
  251. package/dist/source/pushQueue.d.ts +70 -48
  252. package/dist/source/pushQueue.js +36 -29
  253. package/dist/source/signing.d.ts +88 -0
  254. package/dist/source/signing.js +159 -0
  255. package/dist/source/types.d.ts +351 -0
  256. package/dist/source/types.js +43 -0
  257. package/dist/stores/ObjectStore.d.ts +11 -12
  258. package/dist/stores/ObjectStore.js +34 -35
  259. package/dist/stores/ObjectStoreContract.d.ts +12 -15
  260. package/dist/stores/SyncActionStore.d.ts +8 -12
  261. package/dist/stores/SyncActionStore.js +77 -46
  262. package/dist/surface.d.ts +28 -21
  263. package/dist/surface.js +28 -20
  264. package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +37 -45
  265. package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +101 -80
  266. package/dist/sync/ConnectionManager.d.ts +47 -50
  267. package/dist/sync/ConnectionManager.js +74 -70
  268. package/dist/sync/NetworkProbe.d.ts +27 -31
  269. package/dist/sync/NetworkProbe.js +67 -72
  270. package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +49 -36
  271. package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +79 -54
  272. package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +45 -59
  273. package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +44 -52
  274. package/dist/sync/SyncWebSocket.d.ts +175 -250
  275. package/dist/sync/SyncWebSocket.js +431 -769
  276. package/dist/sync/awaitClaimGrant.d.ts +18 -18
  277. package/dist/sync/awaitClaimGrant.js +38 -30
  278. package/dist/sync/bootstrapApply.d.ts +70 -0
  279. package/dist/sync/bootstrapApply.js +73 -0
  280. package/dist/sync/commitFrames.d.ts +44 -0
  281. package/dist/sync/commitFrames.js +94 -0
  282. package/dist/sync/createClaimStream.d.ts +23 -22
  283. package/dist/sync/createClaimStream.js +108 -25
  284. package/dist/sync/createPresenceStream.d.ts +19 -18
  285. package/dist/sync/createPresenceStream.js +25 -26
  286. package/dist/sync/createSnapshot.d.ts +13 -17
  287. package/dist/sync/createSnapshot.js +20 -26
  288. package/dist/sync/credentialLifecycle.d.ts +175 -0
  289. package/dist/sync/credentialLifecycle.js +322 -0
  290. package/dist/sync/deltaPipeline.d.ts +113 -0
  291. package/dist/sync/deltaPipeline.js +261 -0
  292. package/dist/sync/groupChange.d.ts +113 -0
  293. package/dist/sync/groupChange.js +242 -0
  294. package/dist/sync/heartbeat.d.ts +63 -0
  295. package/dist/sync/heartbeat.js +91 -0
  296. package/dist/sync/participants.d.ts +27 -27
  297. package/dist/sync/schemas.d.ts +3 -2
  298. package/dist/sync/schemas.js +14 -10
  299. package/dist/sync/syncCursor.d.ts +40 -0
  300. package/dist/sync/syncCursor.js +55 -0
  301. package/dist/sync/syncPlan.d.ts +54 -0
  302. package/dist/sync/syncPlan.js +50 -0
  303. package/dist/sync/syncPosition.d.ts +54 -49
  304. package/dist/sync/syncPosition.js +57 -52
  305. package/dist/sync/wsFrameHandlers.d.ts +116 -0
  306. package/dist/sync/wsFrameHandlers.js +374 -0
  307. package/dist/testing/fixtures/bootstrap.d.ts +21 -17
  308. package/dist/testing/fixtures/bootstrap.js +12 -6
  309. package/dist/testing/fixtures/deltas.d.ts +31 -34
  310. package/dist/testing/fixtures/deltas.js +30 -33
  311. package/dist/testing/fixtures/models.d.ts +11 -10
  312. package/dist/testing/fixtures/models.js +12 -10
  313. package/dist/testing/helpers/{react-wrapper.d.ts → reactWrapper.d.ts} +13 -10
  314. package/dist/testing/helpers/{react-wrapper.js → reactWrapper.js} +15 -12
  315. package/dist/testing/helpers/{sync-engine-harness.d.ts → syncEngineHarness.d.ts} +17 -18
  316. package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +14 -11
  317. package/dist/testing/helpers/wait.d.ts +13 -8
  318. package/dist/testing/helpers/wait.js +13 -8
  319. package/dist/testing/index.d.ts +4 -4
  320. package/dist/testing/index.js +3 -3
  321. package/dist/testing/mocks/MockMutationExecutor.d.ts +18 -17
  322. package/dist/testing/mocks/MockMutationExecutor.js +15 -14
  323. package/dist/testing/mocks/MockNetworkMonitor.d.ts +8 -8
  324. package/dist/testing/mocks/MockNetworkMonitor.js +8 -8
  325. package/dist/testing/mocks/MockSyncContext.d.ts +21 -34
  326. package/dist/testing/mocks/MockSyncContext.js +16 -45
  327. package/dist/testing/mocks/MockSyncStore.d.ts +14 -14
  328. package/dist/testing/mocks/MockSyncStore.js +11 -11
  329. package/dist/testing/mocks/MockWebSocket.d.ts +28 -24
  330. package/dist/testing/mocks/MockWebSocket.js +22 -21
  331. package/dist/transactions/TransactionQueue.d.ts +190 -221
  332. package/dist/transactions/TransactionQueue.js +424 -822
  333. package/dist/transactions/TransactionStore.d.ts +20 -0
  334. package/dist/transactions/TransactionStore.js +53 -0
  335. package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
  336. package/dist/transactions/UnconfirmedWrites.js +104 -0
  337. package/dist/transactions/coalesceRules.d.ts +58 -0
  338. package/dist/transactions/coalesceRules.js +140 -0
  339. package/dist/transactions/commitPayload.d.ts +130 -0
  340. package/dist/transactions/commitPayload.js +143 -0
  341. package/dist/transactions/deltaConfirmation.d.ts +58 -0
  342. package/dist/transactions/deltaConfirmation.js +215 -0
  343. package/dist/transactions/optimisticApply.d.ts +49 -0
  344. package/dist/transactions/optimisticApply.js +65 -0
  345. package/dist/transactions/replayValidation.d.ts +99 -0
  346. package/dist/transactions/replayValidation.js +111 -0
  347. package/dist/types/global.d.ts +46 -41
  348. package/dist/types/global.js +20 -19
  349. package/dist/types/index.d.ts +74 -80
  350. package/dist/types/index.js +22 -27
  351. package/dist/types/modelData.d.ts +10 -0
  352. package/dist/types/modelData.js +9 -0
  353. package/dist/types/participant.d.ts +20 -0
  354. package/dist/types/participant.js +10 -0
  355. package/dist/types/streams.d.ts +216 -209
  356. package/dist/types/streams.js +7 -7
  357. package/dist/utils/asyncIterator.d.ts +25 -32
  358. package/dist/utils/asyncIterator.js +25 -32
  359. package/dist/utils/duration.d.ts +12 -15
  360. package/dist/utils/duration.js +12 -15
  361. package/dist/utils/mobxSetup.d.ts +53 -0
  362. package/dist/utils/{mobx-setup.js → mobxSetup.js} +44 -100
  363. package/dist/webhooks/events.d.ts +21 -16
  364. package/dist/webhooks/events.js +10 -8
  365. package/dist/webhooks/index.d.ts +5 -7
  366. package/dist/webhooks/index.js +5 -7
  367. package/dist/{schema/sync-delta-wire.d.ts → wire/delta.d.ts} +58 -41
  368. package/dist/wire/delta.js +114 -0
  369. package/dist/wire/errorEnvelope.d.ts +35 -27
  370. package/dist/wire/errorEnvelope.js +38 -32
  371. package/dist/wire/frames.d.ts +150 -67
  372. package/dist/wire/frames.js +48 -1
  373. package/dist/wire/index.d.ts +18 -13
  374. package/dist/wire/index.js +36 -13
  375. package/dist/wire/listEnvelope.d.ts +16 -23
  376. package/dist/wire/listEnvelope.js +7 -6
  377. package/dist/wire/protocol.d.ts +38 -0
  378. package/dist/wire/protocol.js +38 -0
  379. package/dist/wire/protocolVersion.d.ts +60 -0
  380. package/dist/wire/protocolVersion.js +67 -0
  381. package/docs/api-keys.md +4 -3
  382. package/docs/coordination.md +59 -0
  383. package/docs/examples/existing-python-backend.md +3 -3
  384. package/docs/identity.md +4 -4
  385. package/docs/integration-guide.md +1 -1
  386. package/docs/react.md +1 -1
  387. package/docs/sessions.md +5 -7
  388. package/package.json +24 -21
  389. package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
  390. package/dist/ai-sdk/coordination-context.d.ts +0 -52
  391. package/dist/client/index.d.ts +0 -36
  392. package/dist/client/index.js +0 -33
  393. package/dist/config/index.d.ts +0 -10
  394. package/dist/config/index.js +0 -12
  395. package/dist/core/query-utils.d.ts +0 -34
  396. package/dist/core/query-utils.js +0 -59
  397. package/dist/interfaces/headless.d.ts +0 -95
  398. package/dist/interfaces/headless.js +0 -41
  399. package/dist/query/index.d.ts +0 -6
  400. package/dist/query/index.js +0 -5
  401. package/dist/realtime/index.d.ts +0 -10
  402. package/dist/realtime/index.js +0 -9
  403. package/dist/schema/plane.d.ts +0 -23
  404. package/dist/schema/plane.js +0 -19
  405. package/dist/schema/sync-delta-row.js +0 -103
  406. package/dist/schema/sync-delta-wire.js +0 -102
  407. package/dist/server/next.d.ts +0 -51
  408. package/dist/server/next.js +0 -47
  409. package/dist/server/read-config.d.ts +0 -67
  410. package/dist/server/read-config.js +0 -8
  411. package/dist/server/storage-mode.d.ts +0 -1
  412. package/dist/server/storage-mode.js +0 -18
  413. package/dist/source/connector-protocol.d.ts +0 -159
  414. package/dist/source/connector-protocol.js +0 -161
  415. package/dist/sync/OfflineFlush.d.ts +0 -9
  416. package/dist/sync/OfflineFlush.js +0 -22
  417. package/dist/sync/OfflineTransactionStore.d.ts +0 -37
  418. package/dist/sync/OfflineTransactionStore.js +0 -263
  419. package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
  420. package/dist/transactions/OptimisticEchoTracker.js +0 -104
  421. package/dist/transactions/index.d.ts +0 -16
  422. package/dist/transactions/index.js +0 -7
  423. package/dist/transactions/mutation-error-handler.d.ts +0 -5
  424. package/dist/transactions/mutation-error-handler.js +0 -39
  425. package/dist/utils/mobx-setup.d.ts +0 -42
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Application-level heartbeat for the sync WebSocket.
3
+ *
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
+ */
14
+ import { getContext } from '../context.js';
15
+ import { PING_INTERVAL_MS } from '../wire/protocol.js';
16
+ /**
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.
21
+ */
22
+ export const HEARTBEAT_INTERVAL_MS = PING_INTERVAL_MS;
23
+ export const HEARTBEAT_TIMEOUT_MS = 10_000;
24
+ /**
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.
32
+ *
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
+ */
40
+ export class HeartbeatController {
41
+ transport;
42
+ heartbeatTimer = null;
43
+ heartbeatTimeoutTimer = null;
44
+ constructor(transport) {
45
+ this.transport = transport;
46
+ }
47
+ start() {
48
+ this.stop();
49
+ this.heartbeatTimer = setInterval(() => {
50
+ if (!this.transport.isSocketOpen())
51
+ return;
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
+ try {
55
+ this.transport.sendPing();
56
+ }
57
+ catch (err) {
58
+ getContext().observability.captureWebSocketError({
59
+ context: 'heartbeat-send-failed',
60
+ error: err instanceof Error ? err.message : String(err),
61
+ });
62
+ this.transport.forceClose('heartbeat-send-failed');
63
+ return;
64
+ }
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
+ if (this.heartbeatTimeoutTimer)
69
+ clearTimeout(this.heartbeatTimeoutTimer);
70
+ this.heartbeatTimeoutTimer = setTimeout(() => {
71
+ getContext().observability.captureWebSocketError({
72
+ context: 'heartbeat-timeout',
73
+ });
74
+ this.transport.forceClose('heartbeat-timeout');
75
+ }, HEARTBEAT_TIMEOUT_MS);
76
+ }, HEARTBEAT_INTERVAL_MS);
77
+ }
78
+ stop() {
79
+ if (this.heartbeatTimer) {
80
+ clearInterval(this.heartbeatTimer);
81
+ this.heartbeatTimer = null;
82
+ }
83
+ this.clearHeartbeatTimeout();
84
+ }
85
+ clearHeartbeatTimeout() {
86
+ if (this.heartbeatTimeoutTimer) {
87
+ clearTimeout(this.heartbeatTimeoutTimer);
88
+ this.heartbeatTimeoutTimer = null;
89
+ }
90
+ }
91
+ }
@@ -1,11 +1,11 @@
1
1
  import type { SyncWebSocket } from './SyncWebSocket.js';
2
- import type { Schema, SchemaRecord } from '../schema/schema.js';
2
+ 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;
@@ -46,7 +47,7 @@ export interface ParticipantJoinOptions {
46
47
  export interface ScopedPresence {
47
48
  readonly self: Peer;
48
49
  readonly focus: ClaimTarget | null;
49
- readonly others: ReadonlyArray<Peer>;
50
+ readonly others: readonly Peer[];
50
51
  update(activity: Activity): void;
51
52
  reading(detail?: string): void;
52
53
  reading(target: PresenceTarget, detail?: string): void;
@@ -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
- readonly others: ReadonlyArray<Claim>;
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,15 +84,15 @@ 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;
94
- readonly peers: ReadonlyArray<Peer>;
95
- readonly activeClaims: ReadonlyArray<Claim>;
94
+ readonly peers: readonly Peer[];
95
+ readonly activeClaims: readonly Claim[];
96
96
  focus(target: PresenceTarget, options?: ParticipantFocusOptions): JoinedParticipant;
97
97
  leave(): void;
98
98
  [Symbol.asyncDispose](): Promise<void>;
@@ -106,10 +106,10 @@ export interface ParticipantManagerConfig {
106
106
  readonly getTransport: () => SyncWebSocket | null;
107
107
  readonly presence: PresenceStream;
108
108
  readonly claims: AttachableClaimStream;
109
- readonly schema?: Schema<SchemaRecord>;
109
+ readonly schema?: Schema;
110
110
  }
111
111
  export declare function createParticipantManager(config: ParticipantManagerConfig): ParticipantManager;
112
- export declare function resolveParticipantSyncGroups(scope: ParticipantScope | undefined, schema?: Schema<SchemaRecord>): string[];
113
- export declare function syncGroupFromEntityRef(ref: ClaimTarget, schema?: Schema<SchemaRecord>): string;
112
+ export declare function resolveParticipantSyncGroups(scope: ParticipantScope | undefined, schema?: Schema): string[];
113
+ export declare function syncGroupFromEntityRef(ref: ClaimTarget, schema?: Schema): string;
114
114
  export declare function parseParticipantTtlSeconds(value: number | string | null | undefined): number | undefined;
115
115
  export declare function createParticipantClaimId(): string;
@@ -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);
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Holds the resume state of one WebSocket sync session: the `lastSyncId`
3
+ * watermark, which marks the highest delta the client has seen, and an opaque
4
+ * server cursor used for incremental sync. The transport carries both across
5
+ * reconnects so the session can resume where it left off.
6
+ *
7
+ * The watermark advances under a strict rule — it moves forward only on an
8
+ * acknowledgement that is gated on durable persistence — which the transport
9
+ * enforces at its acknowledgement and delta-handling call sites.
10
+ */
11
+ export declare class SyncCursor {
12
+ lastSyncId: number;
13
+ syncCursor: string | null;
14
+ constructor(lastSyncId: number);
15
+ /**
16
+ * Advances the watermark in response to an acknowledgement. This becomes the
17
+ * value the next incremental-sync request and the connect handshake send, and
18
+ * the value {@link SyncCursor.getLastSyncId} reports when persisting on a
19
+ * clean shutdown. The move is monotonic: a stale, lower acknowledgement never
20
+ * pulls the watermark backward.
21
+ */
22
+ ackAdvance(syncId: number): void;
23
+ /**
24
+ * Sets the watermark outright, used when restoring persisted state.
25
+ */
26
+ setLastSyncId(syncId: number): void;
27
+ /**
28
+ * Sets the opaque server cursor used for incremental sync.
29
+ */
30
+ setSyncCursor(cursor: string | null): void;
31
+ /**
32
+ * Returns the current opaque server cursor, or null if none is set.
33
+ */
34
+ getSyncCursor(): string | null;
35
+ /**
36
+ * Returns the highest delta id seen this session, for persistence on a clean
37
+ * shutdown.
38
+ */
39
+ getLastSyncId(): number;
40
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Holds the resume state of one WebSocket sync session: the `lastSyncId`
3
+ * watermark, which marks the highest delta the client has seen, and an opaque
4
+ * server cursor used for incremental sync. The transport carries both across
5
+ * reconnects so the session can resume where it left off.
6
+ *
7
+ * The watermark advances under a strict rule — it moves forward only on an
8
+ * acknowledgement that is gated on durable persistence — which the transport
9
+ * enforces at its acknowledgement and delta-handling call sites.
10
+ */
11
+ export class SyncCursor {
12
+ lastSyncId;
13
+ syncCursor;
14
+ constructor(lastSyncId) {
15
+ this.lastSyncId = lastSyncId;
16
+ this.syncCursor = null;
17
+ }
18
+ /**
19
+ * Advances the watermark in response to an acknowledgement. This becomes the
20
+ * value the next incremental-sync request and the connect handshake send, and
21
+ * the value {@link SyncCursor.getLastSyncId} reports when persisting on a
22
+ * clean shutdown. The move is monotonic: a stale, lower acknowledgement never
23
+ * pulls the watermark backward.
24
+ */
25
+ ackAdvance(syncId) {
26
+ if (syncId > this.lastSyncId) {
27
+ this.lastSyncId = syncId;
28
+ }
29
+ }
30
+ /**
31
+ * Sets the watermark outright, used when restoring persisted state.
32
+ */
33
+ setLastSyncId(syncId) {
34
+ this.lastSyncId = syncId;
35
+ }
36
+ /**
37
+ * Sets the opaque server cursor used for incremental sync.
38
+ */
39
+ setSyncCursor(cursor) {
40
+ this.syncCursor = cursor;
41
+ }
42
+ /**
43
+ * Returns the current opaque server cursor, or null if none is set.
44
+ */
45
+ getSyncCursor() {
46
+ return this.syncCursor;
47
+ }
48
+ /**
49
+ * Returns the highest delta id seen this session, for persistence on a clean
50
+ * shutdown.
51
+ */
52
+ getLastSyncId() {
53
+ return this.lastSyncId || 0;
54
+ }
55
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Derives a client's sync plan from its {@link Schema}. Walking the schema's
3
+ * models and relations, it produces two declarative arrays consumed when the
4
+ * store is constructed: the foreign-key indexes to register on the in-memory
5
+ * object pool, and the enrichment rules that attach related parents to
6
+ * incoming rows. See {@link deriveSyncPlanFromSchema}.
7
+ */
8
+ import type { Schema } from '../schema/schema.js';
9
+ /** A foreign-key index to register on the in-memory object pool when the store is constructed. */
10
+ export interface ForeignKeyIndexSpec {
11
+ /**
12
+ * The name of the child model, where the foreign-key field lives, and the
13
+ * name the object pool indexes by. Use the wire type-name casing (for
14
+ * example `'SlideLayer'`, not `'slideLayer'`), since that is the value
15
+ * stamped onto reconstructed models and the key the pool looks up.
16
+ */
17
+ readonly modelName: string;
18
+ /** The foreign-key field name on the child model, for example `'slideId'`. */
19
+ readonly fieldName: string;
20
+ }
21
+ /**
22
+ * A declarative rule for enriching an incoming row with its related parent.
23
+ *
24
+ * When a delta for `modelName` arrives and its row has been constructed, the
25
+ * store reads the row's `foreignKey` value, looks up the matching parent in
26
+ * the object pool, and attaches it under `relationKey`. Enrichment is
27
+ * best-effort: if the parent is not in the pool yet — for example, it arrives
28
+ * later in the same bootstrap batch — the step is skipped without error.
29
+ */
30
+ export interface EnrichmentPlanEntry {
31
+ /** The child model whose incoming deltas should be enriched. */
32
+ readonly modelName: string;
33
+ /** The foreign-key field on the child that points at the parent's id. */
34
+ readonly foreignKey: string;
35
+ /** The property name under which to attach the parent model. */
36
+ readonly relationKey: string;
37
+ }
38
+ /**
39
+ * Walks a schema and derives the two sync-plan arrays used when the store is
40
+ * constructed: the foreign-key indexes to register on the object pool and the
41
+ * enrichment plan. See {@link ForeignKeyIndexSpec} and
42
+ * {@link EnrichmentPlanEntry}.
43
+ *
44
+ * Both are drawn from each `belongsTo` relation that sets `options.index` or
45
+ * `options.enrich`; relations without those options are skipped. Enabling them
46
+ * is opt-in, so adding a `belongsTo` relation never silently changes how deltas
47
+ * apply or how lookups resolve. A `hasMany` or `hasOne` relation registers its
48
+ * index on the target model, since that is where the foreign key lives. The
49
+ * function has no side effects and is called once at construction.
50
+ */
51
+ export declare function deriveSyncPlanFromSchema(schema: Schema): {
52
+ enrichmentPlan: EnrichmentPlanEntry[];
53
+ foreignKeyIndexes: ForeignKeyIndexSpec[];
54
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Derives a client's sync plan from its {@link Schema}. Walking the schema's
3
+ * models and relations, it produces two declarative arrays consumed when the
4
+ * store is constructed: the foreign-key indexes to register on the in-memory
5
+ * object pool, and the enrichment rules that attach related parents to
6
+ * incoming rows. See {@link deriveSyncPlanFromSchema}.
7
+ */
8
+ /**
9
+ * Walks a schema and derives the two sync-plan arrays used when the store is
10
+ * constructed: the foreign-key indexes to register on the object pool and the
11
+ * enrichment plan. See {@link ForeignKeyIndexSpec} and
12
+ * {@link EnrichmentPlanEntry}.
13
+ *
14
+ * Both are drawn from each `belongsTo` relation that sets `options.index` or
15
+ * `options.enrich`; relations without those options are skipped. Enabling them
16
+ * is opt-in, so adding a `belongsTo` relation never silently changes how deltas
17
+ * apply or how lookups resolve. A `hasMany` or `hasOne` relation registers its
18
+ * index on the target model, since that is where the foreign key lives. The
19
+ * function has no side effects and is called once at construction.
20
+ */
21
+ export function deriveSyncPlanFromSchema(schema) {
22
+ const enrichmentPlan = [];
23
+ const foreignKeyIndexes = [];
24
+ for (const [modelName, def] of Object.entries(schema.models)) {
25
+ const typename = def.typename ?? modelName;
26
+ for (const [relationKey, rel] of Object.entries(def.relations)) {
27
+ if (rel.type === 'belongsTo') {
28
+ if (rel.options?.index) {
29
+ foreignKeyIndexes.push({ modelName: typename, fieldName: rel.foreignKey });
30
+ }
31
+ if (rel.options?.enrich) {
32
+ enrichmentPlan.push({
33
+ modelName: typename,
34
+ foreignKey: rel.foreignKey,
35
+ relationKey,
36
+ });
37
+ }
38
+ }
39
+ else if (rel.type === 'hasMany' || rel.type === 'hasOne') {
40
+ // For hasMany and hasOne, the foreign key lives on the target model,
41
+ // not the current one, so register the index on the target. Its wire
42
+ // type name is resolved from the schema here.
43
+ const targetDef = schema.models[rel.target];
44
+ const targetTypename = targetDef?.typename ?? rel.target;
45
+ foreignKeyIndexes.push({ modelName: targetTypename, fieldName: rel.foreignKey });
46
+ }
47
+ }
48
+ }
49
+ return { enrichmentPlan, foreignKeyIndexes };
50
+ }
@@ -1,40 +1,39 @@
1
1
  /**
2
- * THE sync-position structure one typed object for "where is this client
3
- * in the global delta order", replacing five scattered private counters
4
- * (`lastSeenSyncId` on the queue, `highestProcessedSyncId` + `lastAckedId`
5
- * on the store, ad-hoc acked watermarks, `max()` calls at snapshot sites).
2
+ * Records where this client stands in the global delta order. It is a single
3
+ * typed object holding three related but distinct positions, each with its own
4
+ * rule for when it may advance. Keeping them separate is deliberate: collapsing
5
+ * them into one counter is a classic source of sync bugs.
6
6
  *
7
- * Three facts with DIFFERENT advance disciplines flattening them was the
8
- * historical bug source, so the structure models them explicitly:
7
+ * - `persisted` the resume cursor. It advances only after deltas have
8
+ * committed to durable local storage. This is the value reconnect catch-up
9
+ * sends to the server, so it must never run ahead of what actually landed
10
+ * on disk; otherwise the server would skip deltas the client never stored.
9
11
  *
10
- * - `persisted` — the resume/ack cursor. Advances ONLY after deltas have
11
- * committed to IndexedDB (the Replicache "lastMutationID read in the
12
- * same transaction as the client view" rule see SyncWebSocket.sendAck).
13
- * This is what reconnect catch-up sends; it must never run ahead of
14
- * durable state or the server skips deltas that never landed.
12
+ * - `applied` — the in-memory cursor: the last delta applied to the object
13
+ * pool. It drives the guards that deduplicate and reject replayed deltas.
14
+ * It may run ahead of `persisted`, because the pool is updated before the
15
+ * flush to disk, and behind what has merely been received, because
16
+ * bootstrap-queued deltas arrive before they are applied.
15
17
  *
16
- * - `applied` — the in-memory cursor: the last delta APPLIED to the
17
- * object pool. Drives delta dedup/replay guards. May run ahead of
18
- * `persisted` (pool applies before the IDB flush) and behind receipt
19
- * (bootstrap-queued deltas are received but not yet applied).
18
+ * - `acked` — the highest server position acknowledged for this client's own
19
+ * commits. An acknowledgement at N means the server applied our write at N;
20
+ * the optimistic pool already reflects it, so for the entities we wrote we
21
+ * have effectively read through N even before the echo returns on the
22
+ * stream.
20
23
  *
21
- * - `acked` the highest server watermark ACKED to this client's OWN
22
- * commits. An ack at N means the server applied our write at N; the
23
- * optimistic pool already reflects it, so for entities we wrote we have
24
- * logically read through N even before the stream echo arrives.
24
+ * One value is derived: `readFloor` is the greater of `applied` and `acked`,
25
+ * and is the only position a snapshot or claim should stamp as its read point.
26
+ * Using the raw stream cursor alone would make a claim taken right after a
27
+ * confirmed write look stale against that write's own delta; using the raw
28
+ * acknowledgement alone would be wrong for read-only clients. The maximum is
29
+ * correct per entity, because a competing change to an entity we just wrote
30
+ * necessarily lands above our acknowledgement and still rejects as stale.
25
31
  *
26
- * One derived read: `readFloor` = max(applied, acked) the ONLY value
27
- * snapshots/claims may stamp as `readAt`. The bare stream cursor made a
28
- * claim taken right after an ack-confirmed write stale against that write's
29
- * own delta; the bare ack would be wrong for read-only clients. Per-entity
30
- * correct: a foreign change to an entity we just wrote necessarily lands
31
- * ABOVE our ack and still stale-rejects.
32
- *
33
- * The Zod schema IS the state shape — the class holds exactly one
34
- * `SyncPositionSnapshot` and applies monotonic merges to it, so
35
- * snapshot/restore are identity-shaped and the schema is the single gate
36
- * for anything loaded from disk (`parseSyncPosition`; a corrupted stored
37
- * cursor "ahead of reality" is an existing, known failure mode).
32
+ * The validation schema is the state shape: the class holds exactly one
33
+ * {@link SyncPositionSnapshot} and merges monotonically into it, so snapshot
34
+ * and restore share that shape and {@link parseSyncPosition} is the single gate
35
+ * for anything loaded from disk a corrupted cursor stored "ahead of reality"
36
+ * being a known failure mode.
38
37
  */
39
38
  import { z } from 'zod';
40
39
  export declare const syncPositionSchema: z.ZodObject<{
@@ -44,35 +43,41 @@ export declare const syncPositionSchema: z.ZodObject<{
44
43
  }, z.core.$strip>;
45
44
  export type SyncPositionSnapshot = z.infer<typeof syncPositionSchema>;
46
45
  /**
47
- * PERSISTENCE DESIGN: only the `persisted` cursor is stored durably (as
48
- * `WorkspaceMetadata.lastSyncId`, written by Database after each IDB delta
49
- * commit and gated on load through `syncPositionSchema.shape.persisted` in
50
- * `Database.requiredBootstrap`). Persisting `applied`/`acked` would be
51
- * meaningless: on resume the pool is rebuilt FROM the persisted state, so
52
- * the correct restore is exactly `advancePersisted(storedCursor)` — which
53
- * implies `applied`, while `acked` starts at 0 (a dead session's acks carry
54
- * no read authority; the offline queue re-acks its own replays).
46
+ * Only the `persisted` cursor is stored durably; `applied` and `acked` are
47
+ * not. On resume the object pool is rebuilt from the persisted state, so the
48
+ * correct restore is simply to advance `persisted` to the stored value, which
49
+ * also implies `applied`. `acked` starts at zero, because a past session's
50
+ * acknowledgements carry no read authority and the offline queue
51
+ * re-acknowledges its own replays.
52
+ */
53
+ /**
54
+ * Validates an untrusted value, such as one loaded from disk, into a position
55
+ * snapshot, or returns null when it does not match the schema.
55
56
  */
56
- /** Validate a persisted/foreign value into a position snapshot. */
57
57
  export declare function parseSyncPosition(value: unknown): SyncPositionSnapshot | null;
58
- /** The live position. One instance per client (owned by SyncClient); the
59
- * three producers advance their own fact, consumers read. */
58
+ /**
59
+ * The live sync position: one instance per client. Three producers each
60
+ * advance their own cursor, and consumers read the result.
61
+ */
60
62
  export declare class SyncPosition {
61
63
  #private;
62
- /** Current state the schema shape, frozen-by-copy. */
64
+ /** Returns a copy of the current state in the schema's shape. */
63
65
  snapshot(): SyncPositionSnapshot;
64
66
  get persisted(): number;
65
67
  get applied(): number;
66
68
  get acked(): number;
67
- /** THE value snapshots/claims stamp as `readAt`. */
69
+ /** The position a snapshot or claim stamps as its read point: the greater of `applied` and `acked`. */
68
70
  get readFloor(): number;
69
- /** Deltas through `syncId` have COMMITTED to IndexedDB. Persisting
70
- * implies applied the flush path applies before/with persisting. */
71
+ /**
72
+ * Records that deltas through `syncId` have committed to durable local
73
+ * storage. This also advances `applied`, since the flush applies each delta
74
+ * before or as it persists.
75
+ */
71
76
  advancePersisted(syncId: number): void;
72
- /** A delta was APPLIED to the in-memory pool. */
77
+ /** Records that a delta was applied to the in-memory object pool. */
73
78
  advanceApplied(syncId: number): void;
74
- /** The server acked one of OUR commits at this watermark. */
79
+ /** Records that the server acknowledged one of this client's commits at the given position. */
75
80
  noteAck(lastSyncId: number | undefined): void;
76
- /** Restore from a VALIDATED snapshot (e.g. IDB resume). Monotonic. */
81
+ /** Restores from an already-validated snapshot, for example on resume from disk. The merge is monotonic. */
77
82
  restore(snapshot: SyncPositionSnapshot): void;
78
83
  }