@harperfast/harper 5.3.0-alpha.1 → 5.3.0-beta.2

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 (347) hide show
  1. package/bin/cliOperations.ts +12 -0
  2. package/bin/restart.ts +66 -6
  3. package/components/Application.ts +1134 -112
  4. package/components/DESIGN.md +421 -0
  5. package/components/OptionsWatcher.ts +368 -102
  6. package/components/Scope.ts +20 -1
  7. package/components/componentLoader.ts +43 -4
  8. package/components/deploymentOperations.ts +4 -1
  9. package/components/deploymentRecorder.ts +9 -2
  10. package/components/mcp/DESIGN.md +109 -0
  11. package/components/mcp/audit.ts +21 -17
  12. package/components/operations.js +284 -52
  13. package/components/operationsValidation.js +49 -2
  14. package/components/packageComponent.ts +25 -1
  15. package/components/requestRestart.ts +11 -0
  16. package/config/DESIGN.md +306 -0
  17. package/config/RootConfigWatcher.ts +191 -37
  18. package/config/configReadRetry.ts +62 -0
  19. package/config/configUtils.ts +78 -26
  20. package/config/parseConfigFile.ts +34 -0
  21. package/config/readConfigFileSync.ts +44 -0
  22. package/config/watcherArming.ts +59 -0
  23. package/config-root.schema.json +4 -0
  24. package/dataLayer/DESIGN.md +179 -0
  25. package/dataLayer/harperBridge/ResourceBridge.ts +28 -2
  26. package/dataLayer/restoreMarker.ts +92 -25
  27. package/dist/bin/cliOperations.js +13 -0
  28. package/dist/bin/cliOperations.js.map +1 -1
  29. package/dist/bin/restart.js +42 -6
  30. package/dist/bin/restart.js.map +1 -1
  31. package/dist/components/Application.d.ts +104 -9
  32. package/dist/components/Application.js +954 -102
  33. package/dist/components/Application.js.map +1 -1
  34. package/dist/components/OptionsWatcher.d.ts +4 -1
  35. package/dist/components/OptionsWatcher.js +378 -104
  36. package/dist/components/OptionsWatcher.js.map +1 -1
  37. package/dist/components/Scope.js +15 -1
  38. package/dist/components/Scope.js.map +1 -1
  39. package/dist/components/componentLoader.js +35 -3
  40. package/dist/components/componentLoader.js.map +1 -1
  41. package/dist/components/deploymentOperations.js +4 -1
  42. package/dist/components/deploymentOperations.js.map +1 -1
  43. package/dist/components/deploymentRecorder.d.ts +4 -2
  44. package/dist/components/deploymentRecorder.js +1 -0
  45. package/dist/components/deploymentRecorder.js.map +1 -1
  46. package/dist/components/mcp/audit.d.ts +2 -1
  47. package/dist/components/mcp/audit.js +21 -17
  48. package/dist/components/mcp/audit.js.map +1 -1
  49. package/dist/components/operations.d.ts +28 -0
  50. package/dist/components/operations.js +263 -54
  51. package/dist/components/operations.js.map +1 -1
  52. package/dist/components/operationsValidation.js +48 -2
  53. package/dist/components/operationsValidation.js.map +1 -1
  54. package/dist/components/packageComponent.js +24 -0
  55. package/dist/components/packageComponent.js.map +1 -1
  56. package/dist/components/requestRestart.d.ts +1 -0
  57. package/dist/components/requestRestart.js +7 -0
  58. package/dist/components/requestRestart.js.map +1 -1
  59. package/dist/config/RootConfigWatcher.d.ts +2 -0
  60. package/dist/config/RootConfigWatcher.js +189 -35
  61. package/dist/config/RootConfigWatcher.js.map +1 -1
  62. package/dist/config/configReadRetry.d.ts +8 -0
  63. package/dist/config/configReadRetry.js +62 -0
  64. package/dist/config/configReadRetry.js.map +1 -0
  65. package/dist/config/configUtils.d.ts +10 -9
  66. package/dist/config/configUtils.js +63 -27
  67. package/dist/config/configUtils.js.map +1 -1
  68. package/dist/config/parseConfigFile.d.ts +4 -0
  69. package/dist/config/parseConfigFile.js +35 -0
  70. package/dist/config/parseConfigFile.js.map +1 -0
  71. package/dist/config/readConfigFileSync.d.ts +1 -0
  72. package/dist/config/readConfigFileSync.js +47 -0
  73. package/dist/config/readConfigFileSync.js.map +1 -0
  74. package/dist/config/watcherArming.d.ts +15 -0
  75. package/dist/config/watcherArming.js +59 -0
  76. package/dist/config/watcherArming.js.map +1 -0
  77. package/dist/dataLayer/harperBridge/ResourceBridge.js +21 -2
  78. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  79. package/dist/dataLayer/restoreMarker.d.ts +21 -8
  80. package/dist/dataLayer/restoreMarker.js +94 -27
  81. package/dist/dataLayer/restoreMarker.js.map +1 -1
  82. package/dist/index.d.ts +2 -0
  83. package/dist/index.js +9 -1
  84. package/dist/index.js.map +1 -1
  85. package/dist/json/systemSchema.json +3 -0
  86. package/dist/resources/DatabaseTransaction.d.ts +25 -0
  87. package/dist/resources/DatabaseTransaction.js +224 -18
  88. package/dist/resources/DatabaseTransaction.js.map +1 -1
  89. package/dist/resources/LMDBTransaction.d.ts +2 -1
  90. package/dist/resources/LMDBTransaction.js +22 -3
  91. package/dist/resources/LMDBTransaction.js.map +1 -1
  92. package/dist/resources/PrimaryRocksDatabase.js +22 -6
  93. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  94. package/dist/resources/RecordEncoder.d.ts +1 -1
  95. package/dist/resources/RecordEncoder.js +46 -10
  96. package/dist/resources/RecordEncoder.js.map +1 -1
  97. package/dist/resources/Resource.js +97 -13
  98. package/dist/resources/Resource.js.map +1 -1
  99. package/dist/resources/ResourceInterface.d.ts +8 -0
  100. package/dist/resources/RocksIndexStore.js +2 -1
  101. package/dist/resources/RocksIndexStore.js.map +1 -1
  102. package/dist/resources/RocksTransactionLogStore.d.ts +10 -0
  103. package/dist/resources/RocksTransactionLogStore.js +104 -33
  104. package/dist/resources/RocksTransactionLogStore.js.map +1 -1
  105. package/dist/resources/Table.d.ts +58 -7
  106. package/dist/resources/Table.js +1143 -348
  107. package/dist/resources/Table.js.map +1 -1
  108. package/dist/resources/analytics/write.d.ts +3 -0
  109. package/dist/resources/analytics/write.js +49 -16
  110. package/dist/resources/analytics/write.js.map +1 -1
  111. package/dist/resources/auditStore.d.ts +170 -0
  112. package/dist/resources/auditStore.js +457 -11
  113. package/dist/resources/auditStore.js.map +1 -1
  114. package/dist/resources/crdt.d.ts +10 -0
  115. package/dist/resources/crdt.js +22 -0
  116. package/dist/resources/crdt.js.map +1 -1
  117. package/dist/resources/dataLoader.js +3 -4
  118. package/dist/resources/dataLoader.js.map +1 -1
  119. package/dist/resources/databases.d.ts +16 -13
  120. package/dist/resources/databases.js +625 -177
  121. package/dist/resources/databases.js.map +1 -1
  122. package/dist/resources/derivedIndexRegistry.d.ts +5 -0
  123. package/dist/resources/derivedIndexRegistry.js +68 -0
  124. package/dist/resources/derivedIndexRegistry.js.map +1 -0
  125. package/dist/resources/derivedIndexRuntime.d.ts +215 -0
  126. package/dist/resources/derivedIndexRuntime.js +2027 -0
  127. package/dist/resources/derivedIndexRuntime.js.map +1 -0
  128. package/dist/resources/graphql.js +3 -2
  129. package/dist/resources/graphql.js.map +1 -1
  130. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +102 -9
  131. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +867 -39
  132. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  133. package/dist/resources/indexes/fullTextDerivedIndex.d.ts +81 -0
  134. package/dist/resources/indexes/fullTextDerivedIndex.js +1004 -0
  135. package/dist/resources/indexes/fullTextDerivedIndex.js.map +1 -0
  136. package/dist/resources/indexes/fullTextNativeBinding.d.ts +78 -0
  137. package/dist/resources/indexes/fullTextNativeBinding.js +85 -0
  138. package/dist/resources/indexes/fullTextNativeBinding.js.map +1 -0
  139. package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
  140. package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
  141. package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
  142. package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
  143. package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
  144. package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
  145. package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.d.ts +24 -0
  146. package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js +149 -0
  147. package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js.map +1 -0
  148. package/dist/resources/nodeIdMapping.d.ts +5 -0
  149. package/dist/resources/nodeIdMapping.js +49 -0
  150. package/dist/resources/nodeIdMapping.js.map +1 -1
  151. package/dist/resources/recordLock.d.ts +47 -4
  152. package/dist/resources/recordLock.js +138 -7
  153. package/dist/resources/recordLock.js.map +1 -1
  154. package/dist/resources/recordLockCoordinator.d.ts +557 -0
  155. package/dist/resources/recordLockCoordinator.js +2597 -0
  156. package/dist/resources/recordLockCoordinator.js.map +1 -0
  157. package/dist/resources/replayLogs.js +5 -0
  158. package/dist/resources/replayLogs.js.map +1 -1
  159. package/dist/resources/replicatedApplyFailure.d.ts +16 -0
  160. package/dist/resources/replicatedApplyFailure.js +63 -0
  161. package/dist/resources/replicatedApplyFailure.js.map +1 -0
  162. package/dist/resources/scheduler/scheduler.js +3 -3
  163. package/dist/resources/scheduler/scheduler.js.map +1 -1
  164. package/dist/resources/search.d.ts +10 -4
  165. package/dist/resources/search.js +160 -40
  166. package/dist/resources/search.js.map +1 -1
  167. package/dist/resources/tracked.d.ts +5 -1
  168. package/dist/resources/tracked.js +74 -23
  169. package/dist/resources/tracked.js.map +1 -1
  170. package/dist/resources/transactionBroadcast.js +4 -6
  171. package/dist/resources/transactionBroadcast.js.map +1 -1
  172. package/dist/security/auth.js +59 -23
  173. package/dist/security/auth.js.map +1 -1
  174. package/dist/security/deferredAuthentication.d.ts +11 -0
  175. package/dist/security/deferredAuthentication.js +25 -3
  176. package/dist/security/deferredAuthentication.js.map +1 -1
  177. package/dist/security/jsLoader.js +9 -4
  178. package/dist/security/jsLoader.js.map +1 -1
  179. package/dist/server/REST.js +39 -5
  180. package/dist/server/REST.js.map +1 -1
  181. package/dist/server/http.d.ts +5 -1
  182. package/dist/server/http.js +34 -2
  183. package/dist/server/http.js.map +1 -1
  184. package/dist/server/mqtt.js +5 -1
  185. package/dist/server/mqtt.js.map +1 -1
  186. package/dist/server/serverHelpers/Headers.d.ts +2 -0
  187. package/dist/server/serverHelpers/Headers.js +6 -0
  188. package/dist/server/serverHelpers/Headers.js.map +1 -1
  189. package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
  190. package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
  191. package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
  192. package/dist/server/serverHelpers/Request.d.ts +5 -10
  193. package/dist/server/serverHelpers/Request.js +38 -136
  194. package/dist/server/serverHelpers/Request.js.map +1 -1
  195. package/dist/server/serverHelpers/contentTypes.d.ts +2 -0
  196. package/dist/server/serverHelpers/contentTypes.js +189 -15
  197. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  198. package/dist/server/serverHelpers/serverUtilities.d.ts +3 -3
  199. package/dist/server/serverHelpers/serverUtilities.js +110 -20
  200. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  201. package/dist/server/serverHelpers/uwsServer.js +4 -1
  202. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  203. package/dist/server/serverHelpers/webSocketCloseReason.d.ts +2 -0
  204. package/dist/server/serverHelpers/webSocketCloseReason.js +29 -0
  205. package/dist/server/serverHelpers/webSocketCloseReason.js.map +1 -0
  206. package/dist/server/storageReclamation.js +1 -1
  207. package/dist/server/storageReclamation.js.map +1 -1
  208. package/dist/server/threads/isolatedApplications.d.ts +47 -0
  209. package/dist/server/threads/isolatedApplications.js +171 -0
  210. package/dist/server/threads/isolatedApplications.js.map +1 -0
  211. package/dist/server/threads/logRotationTransport.d.ts +1 -0
  212. package/dist/server/threads/logRotationTransport.js +33 -0
  213. package/dist/server/threads/logRotationTransport.js.map +1 -0
  214. package/dist/server/threads/manageThreads.d.ts +64 -6
  215. package/dist/server/threads/manageThreads.js +261 -12
  216. package/dist/server/threads/manageThreads.js.map +1 -1
  217. package/dist/server/threads/socketRouter.d.ts +1 -0
  218. package/dist/server/threads/socketRouter.js +196 -13
  219. package/dist/server/threads/socketRouter.js.map +1 -1
  220. package/dist/server/threads/threadServer.js +30 -7
  221. package/dist/server/threads/threadServer.js.map +1 -1
  222. package/dist/utility/errors/hdbError.d.ts +24 -0
  223. package/dist/utility/errors/hdbError.js +58 -1
  224. package/dist/utility/errors/hdbError.js.map +1 -1
  225. package/dist/utility/hdbTerms.d.ts +2 -0
  226. package/dist/utility/hdbTerms.js +2 -0
  227. package/dist/utility/hdbTerms.js.map +1 -1
  228. package/dist/utility/logging/harper_logger.js +218 -38
  229. package/dist/utility/logging/harper_logger.js.map +1 -1
  230. package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
  231. package/dist/utility/logging/logGenerationCoordinator.js +184 -0
  232. package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
  233. package/dist/utility/logging/logRotation.d.ts +46 -0
  234. package/dist/utility/logging/logRotation.js +365 -0
  235. package/dist/utility/logging/logRotation.js.map +1 -0
  236. package/dist/utility/logging/logRotator.d.ts +1 -1
  237. package/dist/utility/logging/logRotator.js +172 -92
  238. package/dist/utility/logging/logRotator.js.map +1 -1
  239. package/dist/utility/npmUtilities.js +6 -4
  240. package/dist/utility/npmUtilities.js.map +1 -1
  241. package/dist/utility/watcherFallback.d.ts +0 -45
  242. package/dist/utility/watcherFallback.js +1 -125
  243. package/dist/utility/watcherFallback.js.map +1 -1
  244. package/dist/validation/configValidator.js +6 -3
  245. package/dist/validation/configValidator.js.map +1 -1
  246. package/index.ts +9 -0
  247. package/json/systemSchema.json +3 -0
  248. package/npm-shrinkwrap.json +177 -87
  249. package/package.json +13 -5
  250. package/resources/DESIGN.md +689 -19
  251. package/resources/DatabaseTransaction.ts +230 -17
  252. package/resources/LMDBTransaction.ts +21 -3
  253. package/resources/PrimaryRocksDatabase.ts +20 -7
  254. package/resources/RecordEncoder.ts +52 -10
  255. package/resources/Resource.ts +97 -13
  256. package/resources/ResourceInterface.ts +8 -0
  257. package/resources/RocksIndexStore.ts +2 -1
  258. package/resources/RocksTransactionLogStore.ts +111 -31
  259. package/resources/Table.ts +1273 -396
  260. package/resources/analytics/DESIGN.md +38 -0
  261. package/resources/analytics/write.ts +50 -17
  262. package/resources/auditStore.ts +460 -11
  263. package/resources/crdt.ts +22 -0
  264. package/resources/dataLoader.ts +3 -4
  265. package/resources/databases.ts +611 -147
  266. package/resources/derivedIndexRegistry.ts +56 -0
  267. package/resources/derivedIndexRuntime.ts +2292 -0
  268. package/resources/graphql.ts +3 -2
  269. package/resources/indexes/DESIGN.md +833 -0
  270. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +924 -46
  271. package/resources/indexes/fullTextDerivedIndex.ts +1165 -0
  272. package/resources/indexes/fullTextNativeBinding.ts +146 -0
  273. package/resources/indexes/hnswDerivedIndex.ts +531 -0
  274. package/resources/indexes/hnswPlaneBinding.ts +174 -0
  275. package/resources/indexes/nativeFullTextDerivedIndexLifecycle.ts +181 -0
  276. package/resources/nodeIdMapping.ts +50 -0
  277. package/resources/record-locks.md +1407 -0
  278. package/resources/recordLock.ts +173 -7
  279. package/resources/recordLockCoordinator.ts +3082 -0
  280. package/resources/replayLogs.ts +5 -0
  281. package/resources/replicatedApplyFailure.ts +77 -0
  282. package/resources/scheduler/DESIGN.md +40 -0
  283. package/resources/scheduler/scheduler.ts +4 -4
  284. package/resources/search.ts +169 -49
  285. package/resources/tracked.ts +73 -22
  286. package/resources/transactionBroadcast.ts +4 -4
  287. package/security/DESIGN.md +175 -0
  288. package/security/auth.ts +53 -24
  289. package/security/deferredAuthentication.ts +24 -2
  290. package/security/jsLoader.ts +9 -4
  291. package/server/DESIGN.md +275 -0
  292. package/server/REST.ts +42 -6
  293. package/server/http.ts +34 -2
  294. package/server/mqtt.ts +6 -4
  295. package/server/serverHelpers/Headers.ts +5 -1
  296. package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
  297. package/server/serverHelpers/Request.ts +33 -131
  298. package/server/serverHelpers/contentTypes.ts +188 -15
  299. package/server/serverHelpers/serverUtilities.ts +157 -27
  300. package/server/serverHelpers/uwsServer.ts +4 -1
  301. package/server/serverHelpers/webSocketCloseReason.ts +25 -0
  302. package/server/storageReclamation.ts +2 -2
  303. package/server/threads/isolatedApplications.ts +157 -0
  304. package/server/threads/logRotationTransport.ts +40 -0
  305. package/server/threads/manageThreads.js +254 -12
  306. package/server/threads/socketRouter.ts +217 -11
  307. package/server/threads/threadServer.js +30 -7
  308. package/studio/web/assets/{Chat-BnCBegQz.js → Chat-DADFFGe_.js} +1 -1
  309. package/studio/web/assets/{FloatingChat-CoDW1ySS.js → FloatingChat-D_mI-rZ7.js} +3 -3
  310. package/studio/web/assets/{apiToken-Bwk5BLXW.js → apiToken-c2NiSDHa.js} +1 -1
  311. package/studio/web/assets/{applications-DHxGi7JH.js → applications-DktUqh7G.js} +1 -1
  312. package/studio/web/assets/{cssMode-s0cWI-_M.js → cssMode-Cs_75Xhw.js} +1 -1
  313. package/studio/web/assets/{editor-DNcRHK54.js → editor-19b-Y1IN.js} +1 -1
  314. package/studio/web/assets/{html-Bdssedlg.js → html-DiYEQMpB.js} +1 -1
  315. package/studio/web/assets/{htmlMode-CoDlJ3fw.js → htmlMode-CmR0y7P_.js} +1 -1
  316. package/studio/web/assets/{index-D6sxmFLR.js → index-Dm0rfkJ7.js} +5 -5
  317. package/studio/web/assets/{index.lazy-tmU5BS8s.js → index.lazy-7vqt2CC3.js} +1 -1
  318. package/studio/web/assets/{javascript-B8meVSTH.js → javascript-BWtCFuOt.js} +1 -1
  319. package/studio/web/assets/{jsonMode-DpIPd35T.js → jsonMode-Buzzbv9y.js} +1 -1
  320. package/studio/web/assets/{languageServices-C_5FMJzQ.js → languageServices-SqsFWfTM.js} +1 -1
  321. package/studio/web/assets/{lspLanguageFeatures-BIzNBzPK.js → lspLanguageFeatures-EMV5cmjo.js} +1 -1
  322. package/studio/web/assets/{notifications-CvZivSbh.js → notifications-CAB-LZWT.js} +1 -1
  323. package/studio/web/assets/{notifications-CQf18QKb.js → notifications-DRzmSRxM.js} +1 -1
  324. package/studio/web/assets/{profile-DdOwtntb.js → profile-BNKAl79n.js} +1 -1
  325. package/studio/web/assets/{regions-n69fwagr.js → regions-CUow_Zw2.js} +1 -1
  326. package/studio/web/assets/{register-PfWTCXWB.js → register-Dkt3WUMp.js} +2 -2
  327. package/studio/web/assets/{setComponentFile-Bg6O7X0S.js → setComponentFile-BZRfMD0N.js} +1 -1
  328. package/studio/web/assets/{setup-CUx_aUDl.js → setup-D_yiEPO2.js} +2 -2
  329. package/studio/web/assets/{status-D7BVKqX9.js → status-DhHh1Ge-.js} +1 -1
  330. package/studio/web/assets/{toggleHighContrast-DBSyXzMr.js → toggleHighContrast-D7L1PDtV.js} +1 -1
  331. package/studio/web/assets/{tsMode-BByKCjBS.js → tsMode-CCwLk1YS.js} +1 -1
  332. package/studio/web/assets/{typescript-DDLnLpw9.js → typescript-BP1j1mjn.js} +1 -1
  333. package/studio/web/assets/{useEntityRestURL-31CHGaHk.js → useEntityRestURL-D7bnYxLw.js} +1 -1
  334. package/studio/web/assets/{workers-pR3jRY9D.js → workers-tOuCNT17.js} +1 -1
  335. package/studio/web/assets/{xml-2iRnMhQO.js → xml-BSG_3mQT.js} +1 -1
  336. package/studio/web/assets/{yaml-Bf92gJpd.js → yaml-DxiLprBB.js} +1 -1
  337. package/studio/web/index.html +1 -1
  338. package/utility/DESIGN.md +55 -0
  339. package/utility/errors/hdbError.ts +54 -0
  340. package/utility/hdbTerms.ts +2 -0
  341. package/utility/logging/harper_logger.ts +210 -30
  342. package/utility/logging/logGenerationCoordinator.ts +196 -0
  343. package/utility/logging/logRotation.ts +367 -0
  344. package/utility/logging/logRotator.ts +196 -91
  345. package/utility/npmUtilities.ts +6 -4
  346. package/utility/watcherFallback.ts +0 -122
  347. package/validation/configValidator.ts +6 -3
@@ -0,0 +1,557 @@
1
+ /**
2
+ * Cluster-wide record locks (harper#483, Phase 1): amortized per-record ownership.
3
+ * Design note: `docs/record-lock-ownership.md`.
4
+ *
5
+ * `Table.lock()` acquires the node's rocksdb-js key lock first, which bounds this process to one
6
+ * outstanding acquisition per key; only then does it run the cluster step here. That step has three
7
+ * levels at three very different rates:
8
+ *
9
+ * - **The home map** — `(generation, homes[])`, published by an operator through harper-pro and
10
+ * handed to core through `transport.homeMap()`. It is immutable for the life of its generation:
11
+ * core never computes it, never advances it, and never proceeds without it.
12
+ * - **The home node** — within a generation, a key's arbiter is a rendezvous hash over `homes[]`
13
+ * (§4.4). One arbiter per key is trivially exclusive, so there is no grant state machine at all:
14
+ * no deferral queues, no `(tsR, nodeId)` tiebreak, no synthesized grants, no split votes, no
15
+ * revocation protocol between peers.
16
+ * - **The delegation** — the exclusive right to admit critical sections on one key for a bounded
17
+ * time. While one is live, `lock()`/`unlock()` are pure Phase 0: the local key lock and **zero
18
+ * cluster messages**. Releasing the application lock does not release the delegation, so a node
19
+ * writing the same record repeatedly pays one round and then nothing.
20
+ *
21
+ * Two properties carry the safety argument, and both are enforced rather than assumed:
22
+ *
23
+ * - **One delegate per key per home.** A home never has two live delegations for a key, and a
24
+ * successor delegation is issued only after the predecessor's has been recalled-and-drained or has
25
+ * provably expired on the home's own clock plus skew. Every expiry decision on both sides is made
26
+ * on that side's monotonic clock; no remote timestamp is ever compared against a local one.
27
+ * - **A delegation bounds every handle it admitted.** An admission may not outlive its delegation, so
28
+ * recall revokes capability rather than merely closing the door (§6): the commit-time lease fence
29
+ * in `DatabaseTransaction` rejects a staged write whose handle has expired, and a recall expires
30
+ * those handles before the release is acknowledged.
31
+ *
32
+ * `nodeId` here is the globally stable node NAME. It is deliberately not the audit entry's `nodeId`:
33
+ * `nodeIdMapping.ts` hands out per-node short ids (0 is always local), so the same node has different
34
+ * ids on different nodes and any ordering built on them would order the same pair differently on two
35
+ * nodes.
36
+ *
37
+ * Successor freshness follows §7: a clean release carries inherited origin-log dependencies, the
38
+ * home advances the releasing origin to that entry's own position, and the next delegate cannot
39
+ * admit until its transport has made the set visible. Missing lineage takes the weaker recovery
40
+ * barrier and fails closed if that barrier cannot be established.
41
+ */
42
+ /**
43
+ * How long a home waits before re-sending a recall that FAILED. A recall the delegate confirmed is
44
+ * never re-sent: it has stopped admitting, and the grant is then cleared by its release or by its own
45
+ * deadline. Without both rules a contender polling at 25 ms re-armed the recall on every pass and
46
+ * turned one handoff into an RPC storm lasting the rest of the delegation.
47
+ */
48
+ export declare const RECALL_RETRY_MS = 1000;
49
+ /** Margin a home adds to a delegation it issued, so the delegate always stops admitting first. */
50
+ export declare const LOCK_LEASE_SKEW_MS = 5000;
51
+ /**
52
+ * How long a delegation runs, independent of any one caller's lock lease. It MUST be longer than the
53
+ * longest lease it will admit, or the amortization does not exist: a delegation sized to the caller's
54
+ * lease has no room left for the next lock, so every repeat `lock()` renews and pays a round trip —
55
+ * exactly the cost this design is built to remove.
56
+ */
57
+ export declare const DELEGATION_LEASE_MS: number;
58
+ /**
59
+ * The two control entries. `lockRequest`/`lockGrant` belonged to the Ricart–Agrawala rule the
60
+ * design note replaces; they never shipped enabled, so their nibbles were retired rather than
61
+ * migrated (`auditStore.ts`). Delegation request/grant/recall are unicast over the transport, not
62
+ * entries — the release stays on the replicated log because it is what orders a handoff behind the
63
+ * delegate's own data writes, and the barrier is on it because being replicated is its whole
64
+ * purpose (§7.2): a member commits one on request, and its position is the point a peer must have
65
+ * applied that origin through before a recovery-mode successor may admit.
66
+ */
67
+ export type LockControlType = 'lockRelease' | 'lockBarrier';
68
+ /**
69
+ * The operator-agreed map a key's home is derived from. Supplied by harper-pro; core never computes
70
+ * it and never advances it. Immutable for the life of a generation — nothing a node observes changes
71
+ * it, which is why no agreement protocol runs here (§4).
72
+ */
73
+ export interface LockHomeMap {
74
+ /** Monotonic per database. Part of the fencing token, so it must never go backwards. */
75
+ generation: number;
76
+ /**
77
+ * Every node that participates in cluster record locks for this database — not only the ones an
78
+ * operator thinks of as arbiters. Order is irrelevant; the ring hashes each name independently.
79
+ *
80
+ * It is one set and not two because a home refuses a delegation to any node this list does not
81
+ * name (that is what keeps a decommissioned node from taking one), so a node absent from it can
82
+ * neither home a key nor lock one. Rendezvous hashing then makes every listed node the arbiter for
83
+ * its share of the ring, which is the property that costs a second list nothing.
84
+ */
85
+ homes: string[];
86
+ /**
87
+ * This node's durably persisted, monotonic incarnation counter as a home (§5.1). A random value
88
+ * makes a stale reply identifiable but not ORDERABLE: a home that restarts and re-issues counter 1
89
+ * after having issued counter 50 would let a delayed counter-50 write defeat its successor.
90
+ *
91
+ * **Per coordination incarnation, not per process.** Coordinator state — including the delegation
92
+ * counter — is per-thread, so a replacement coordinating worker starts counting from zero. If the
93
+ * incarnation did not advance with it, the new worker would re-mint tokens its predecessor already
94
+ * issued, and §4.3's incarnation-bound quiescence acknowledgements would survive a restart that
95
+ * discarded everything they attested to.
96
+ */
97
+ homeIncarnation: number;
98
+ }
99
+ /**
100
+ * A delegation's fencing token, ordered lexicographically as
101
+ * `(generation, homeIncarnation, counter)`. Comparable across homes only within a generation, which
102
+ * is all that is needed: a key has exactly one home per generation.
103
+ */
104
+ export type FencingToken = readonly [generation: number, homeIncarnation: number, counter: number];
105
+ export type LockDependency = readonly [origin: string, position: number];
106
+ export type LockDependencySet = readonly LockDependency[];
107
+ export declare function compareTokens(a: FencingToken, b: FencingToken): number;
108
+ export interface LockReleaseEntry {
109
+ type: 'lockRelease';
110
+ /** The locked record's id. Control entries carry it here, never as the audit entry's recordId. */
111
+ key: any;
112
+ /** Node that held the delegation being released. */
113
+ requester: string;
114
+ /**
115
+ * The released delegation's whole fencing token. The counter alone is NOT enough to identify it: a
116
+ * home that restarts, or whose coordinator is recreated, begins counting again, so a delayed
117
+ * release from a previous incarnation would match a live grant's counter and clear it while its
118
+ * delegate is still admitting.
119
+ */
120
+ token: FencingToken;
121
+ /**
122
+ * Inherited clean-handoff lineage. Null hands back an unused first grant without advancing it; a
123
+ * renewed grant still drops retained lineage because its earlier token may have admitted writes.
124
+ * Absent means legacy/unknown.
125
+ */
126
+ dependencies?: LockDependencySet | null;
127
+ }
128
+ /**
129
+ * The §7.2 recovery fence. It names no key and no token: it is appended after every transaction the
130
+ * writing node had committed when it was requested, so a peer that has applied that origin's log
131
+ * through this entry has applied all of them. The coordinator never acts on one.
132
+ */
133
+ export interface LockBarrierEntry {
134
+ type: 'lockBarrier';
135
+ /** Supplied by the requesting transport; distinguishes barriers an origin stamped identically across a restart. */
136
+ nonce: number;
137
+ }
138
+ export type LockControlEntry = LockReleaseEntry | LockBarrierEntry;
139
+ /**
140
+ * What a home replies to a delegation request. One shape rather than a discriminated union: the
141
+ * repo compiles with `strict: false`, where TypeScript does not narrow a union on a boolean literal,
142
+ * so a union here would type-check the denial fields as absent on every branch and then not enforce
143
+ * it. `granted` says which half is populated.
144
+ */
145
+ export interface DelegationReply {
146
+ granted: boolean;
147
+ /** Granted only. */
148
+ token?: FencingToken;
149
+ /** Granted only. How long the delegate may admit for, as a DURATION — never a remote clock reading. */
150
+ leaseMs?: number;
151
+ /** Granted only. Null selects the recovery barrier; an array is the exact clean-handoff fence. */
152
+ dependencies?: LockDependencySet | null;
153
+ /**
154
+ * Denied only. `contended` is the one reason that means another node holds the key, and so the only
155
+ * one an exhausted wait may report as 423. `generation` (the two sides hold different home maps),
156
+ * `unknown-node` (this node is not named in the map) and `quarantine` (the home is inside its §4.3
157
+ * restart interval) all describe something other than contention, so each ends as a retryable 503
158
+ * rather than telling the caller a key nobody holds is held. `timeout` is not a home answer at all
159
+ * — it is this node's own deadline ending its probe — and never classifies a wait (DESIGN.md).
160
+ */
161
+ reason?: 'contended' | 'generation' | 'unknown-node' | 'capacity' | 'not-home' | 'quarantine' | 'timeout';
162
+ /** Denied with `generation`, so a stale requester can re-derive the ring without another round trip. */
163
+ generation?: number;
164
+ retryAfterMs?: number;
165
+ }
166
+ export interface DelegationRequest {
167
+ key: any;
168
+ /** The asking node. Established by the transport, never read from an untrusted payload. */
169
+ requester: string;
170
+ generation: number;
171
+ leaseMs: number;
172
+ }
173
+ export interface DelegationRecall {
174
+ key: any;
175
+ token: FencingToken;
176
+ }
177
+ /**
178
+ * Supplied by harper-pro. Core never computes cluster topology; it only refuses to promise a
179
+ * cluster-wide lock that this contract cannot back.
180
+ */
181
+ export interface ClusterLockTransport {
182
+ /**
183
+ * The operator-agreed home map for the database, or undefined while none is available — before the
184
+ * node has the current generation, or while peers disagree about its digest. Core fails closed on
185
+ * undefined rather than guessing a ring.
186
+ *
187
+ * **One obligation core cannot check, and relies on** (§4.3): a generation change is
188
+ * operator-sequenced, so `g+1` may only be answered here once the control plane's one-shot
189
+ * activation record is active — every old home quiesced (each acknowledgement bound to the
190
+ * acknowledger's `homeIncarnation`, so a restart during the drain invalidates it) or externally
191
+ * fenced, then `DELEGATION_LEASE_MS + skew` elapsed. Core cannot observe what happened on other
192
+ * nodes; the operator can.
193
+ *
194
+ * The other interval — a restart of *this* process — is core's own, because a generation does not
195
+ * advance on a restart and there is no external event to hang it on. See `#grantableAfterMono`.
196
+ */
197
+ homeMap(database: string): LockHomeMap | undefined;
198
+ /**
199
+ * Overrides core's cold-start grant quarantine (§4.3). Set it only where a previous incarnation
200
+ * of this process provably issued nothing — a fresh database, a first start, or a test. Because the
201
+ * same attestation enables the virgin-key freshness fast path, it must prove that no earlier
202
+ * incarnation delegated any key under the current home-map generation at any time, not merely that
203
+ * its last delegation lease has elapsed. Omitted means core enforces the full
204
+ * `DELEGATION_LEASE_MS + skew` from that coordinator's construction — see `#grantableAfterMono` for
205
+ * why neither process start nor thread start is a sound anchor.
206
+ */
207
+ grantableAfterMono?: number;
208
+ /**
209
+ * Whether this worker thread owns lock coordination for the process. Coordinator state is
210
+ * per-thread while the key lock it arbitrates is process-wide, so a second thread running its own
211
+ * delegations would arbitrate against a different view. Core fails closed off the owner thread.
212
+ */
213
+ ownsCoordination(): boolean;
214
+ /** Ask `node` (the key's home) for a delegation. Unicast; rejects if the node is unreachable. */
215
+ requestDelegation(node: string, database: string, table: string, request: DelegationRequest): Promise<DelegationReply>;
216
+ /** Home → delegate. Resolves once the delegate has drained and stopped admitting. */
217
+ recallDelegation(node: string, database: string, table: string, recall: DelegationRecall): Promise<void>;
218
+ /**
219
+ * Obtain an admission from the worker thread that coordinates this database, for a `lock()` served
220
+ * on a thread that is not the owner. Present only when the transport can relay across threads
221
+ * (harper-pro#852); a transport without it makes `acquire()` fail closed off the owner thread, as
222
+ * before. The returned `LockRound` was minted by the owner's coordinator; the caller's coordinator
223
+ * records it as a REMOTE admission and installs the handle's revoker locally, so a recall on the
224
+ * owner fences a write this thread's handle staged. `mintedMono` is comparable across threads
225
+ * because `performance.now()` shares one time origin process-wide.
226
+ */
227
+ acquireOnOwner?(database: string, table: string, key: any, leaseMs: number, waitMs: number): Promise<LockRound>;
228
+ /** Release a remote admission on the owner thread (the counterpart of `acquireOnOwner`). */
229
+ releaseOnOwner?(database: string, table: string, key: any, admissionId: number): Promise<void> | void;
230
+ /**
231
+ * Establish an exact clean-handoff dependency set, or recover the strongest reachable-member
232
+ * barrier when `dependencies` is null. Recovery returns the captured positions for current lock
233
+ * participants that were made visible; it may drain additional replication peers without carrying
234
+ * them in the key's lineage. Clean waits may return void. Concurrent recovery snapshots should be
235
+ * coalesced.
236
+ *
237
+ * Recovery asks each reachable member for a `lockBarrier` entry (`writeLockBarrier`) and drains
238
+ * that member's stream through the position it returns. Core races this promise against the
239
+ * lock's own deadline but cannot cancel it; `deadlineMs` is the wait remaining at the call, so the
240
+ * transport can bound its own work to it instead of outliving the lock that asked.
241
+ */
242
+ establishLockFreshness(database: string, table: string, key: any, dependencies: LockDependencySet | null, deadlineMs: number): Promise<LockDependencySet | void>;
243
+ /** Emit a control entry and return the committed entry's local origin-log position when available. */
244
+ writeControl?(table: string, entry: LockControlEntry): Promise<number | void> | number | void;
245
+ /**
246
+ * Assigned at registration so a transport can push a received entry in directly. `author` and
247
+ * `position` come from the authenticated origin-log header, never from the payload.
248
+ */
249
+ onControlEntry?(database: string, table: string, entry: LockControlEntry, author: string, position: number): void;
250
+ /** Assigned at registration. Inbound delegation request from a peer, for a key this node homes. */
251
+ onDelegationRequest?(database: string, table: string, request: DelegationRequest): Promise<DelegationReply>;
252
+ /** Assigned at registration. Inbound recall from a key's home. */
253
+ onDelegationRecall?(database: string, table: string, recall: DelegationRecall): Promise<void>;
254
+ }
255
+ export declare function encodeLockControlPayload(entry: LockControlEntry): Uint8Array;
256
+ /**
257
+ * Decode a received control payload, or undefined when it is not one this version understands. The
258
+ * tuple length is validated exactly: a future version that grows the payload must bump the type
259
+ * rather than widen this one, since a partially-understood release would clear a delegation on terms
260
+ * the sender did not intend.
261
+ */
262
+ export declare function decodeLockControlPayload(type: unknown, value: unknown): LockControlEntry | undefined;
263
+ /**
264
+ * The string the ring hashes for a key. §4.4 scopes it by database and table: hashing the record id
265
+ * alone would home id `42` in every table of every database on the same node, concentrating unrelated
266
+ * hot keys on one arbiter. The separator cannot appear in a name or a stringified key id.
267
+ */
268
+ export declare function ringKeyFor(database: string, table: string, keyId: unknown): string;
269
+ export declare function homeFor(keyId: string, homes: string[]): string | undefined;
270
+ export interface LockRound {
271
+ /** The identity to stamp the holder's writes with. */
272
+ tsR: number;
273
+ /** The monotonic reading the admission's lease is measured from. */
274
+ mintedMono: number;
275
+ /**
276
+ * Identifies THIS admission for the life of the handle it produced. The caller hands it back on
277
+ * registration and on release. An id rather than the delegation's token, because the token changes
278
+ * on renewal while the admission does not: addressing by token made a renewed delegation lose
279
+ * track of handles it was still responsible for.
280
+ */
281
+ admissionId: number;
282
+ }
283
+ export interface LockCoordinatorOptions {
284
+ database: string;
285
+ table: string;
286
+ /** This node's globally stable name. */
287
+ nodeId: string;
288
+ transport: ClusterLockTransport;
289
+ /**
290
+ * Emit one control entry. Core passes the transport's own `writeControl` when it has one and its
291
+ * transaction-log writer otherwise, since writing to the local log IS the send.
292
+ */
293
+ writeControl: (entry: LockControlEntry) => Promise<number | void> | number | void;
294
+ /** Stable map key for a record id; core passes `writeKeyId`. */
295
+ keyIdOf: (key: any) => unknown;
296
+ /** Mints the holder's stamp; core passes the primary store's monotonic timestamp. */
297
+ nextTimestamp: () => number;
298
+ /** Monotonic clock. Every expiry decision is made on this. */
299
+ monotonic?: () => number;
300
+ skewMs?: number;
301
+ /**
302
+ * The coordinator this one replaces, when a transport is re-registered (a component reload is
303
+ * enough). Its live authority is MOVED here rather than discarded: the transport changed, but the
304
+ * handles it admitted did not, and a successor that started with an empty grant table could hand
305
+ * the same key to another node with no lease time elapsed. That is the "a home may never forget a
306
+ * grant before its expiry" rule (§8) applied across the swap rather than only within one
307
+ * coordinator's life. §11 of the design note calls for exactly this — carry live authority across,
308
+ * or fence and settle every outstanding handle before granting; carrying it across costs nothing.
309
+ */
310
+ adopt?: LockCoordinator;
311
+ /**
312
+ * Overrides the cold-start grant quarantine (§4.3). Pass `-Infinity` only where a previous
313
+ * incarnation provably issued nothing — a fresh database, or a test. Required when `monotonic` is
314
+ * an injected clock the test drives itself. See `#grantableAfterMono`.
315
+ */
316
+ grantableAfterMono?: number;
317
+ /** False in tests, which drive `tick()` themselves. */
318
+ autoTick?: boolean;
319
+ }
320
+ export declare class LockCoordinator {
321
+ #private;
322
+ readonly database: string;
323
+ readonly table: string;
324
+ readonly nodeId: string;
325
+ readonly transport: ClusterLockTransport;
326
+ /**
327
+ * How long until this coordinator can rule out authority issued before it took over — 0 when it
328
+ * already can. Non-mutating, unlike `#ownershipHorizon`, which records ownership as a side effect.
329
+ *
330
+ * The waiver is deliberately NOT consulted. `grantableAfterMono` attests that no previous
331
+ * INCARNATION OF THIS PROCESS delegated; it says nothing about a sibling thread that was
332
+ * coordinating until this instant, and a coordinator built while already owning keeps the waiver
333
+ * without ever observing that handoff — which let a takeover worker in a first-incarnation process
334
+ * report a clean drain while the previous owner's delegates were still admitting.
335
+ *
336
+ * The cost is that a freshly built coordinator cannot prove quiescence for a full lease. That falls
337
+ * only on the case that does not need the proof: a node with no delegations yet is bootstrapping
338
+ * generation 1, where there is nothing to drain and no interval to skip.
339
+ */
340
+ unprovenOwnershipMs(): number;
341
+ constructor(options: LockCoordinatorOptions);
342
+ /**
343
+ * Move this coordinator's live authority to the coordinator replacing it. Called from the
344
+ * successor's constructor, before `close()`, so nothing is dropped in between. Both coordinators
345
+ * run in the same thread for the same node, so the delegations and grants are still this node's —
346
+ * only the transport object underneath them changed.
347
+ */
348
+ handOffTo(successor: LockCoordinator): void;
349
+ /**
350
+ * For `cluster_status`: delegations held, delegations issued, live admissions, admissions still
351
+ * revocable, misrouted calls. `revocable` counts the ones an unlock left fenceable as well, so the
352
+ * retention `#pruneAdmissions` bounds is visible rather than inferred from `admitted`.
353
+ */
354
+ get stats(): {
355
+ delegations: number;
356
+ granted: number;
357
+ admitted: number;
358
+ revocable: number;
359
+ droppedOffOwner: number;
360
+ relayedAdmissions: number;
361
+ };
362
+ /**
363
+ * Admit a critical section for a key whose native lock this thread already holds. Resolves with
364
+ * the stamp and the monotonic reading the lease runs from; rejects 423 when no delegation could be
365
+ * obtained in time, or 503 when the guarantee cannot be established at all.
366
+ *
367
+ * The amortization is the first branch: a live, un-recalled delegation with enough time left costs
368
+ * zero cluster messages.
369
+ */
370
+ acquire(key: any, leaseMs: number, waitMs: number): Promise<LockRound>;
371
+ /**
372
+ * Fence a remote admission's handle on this thread, driven by a recall or surrender on the owner
373
+ * (harper-pro#852), addressed by the OWNER's admission id. Resolves once the handle is provably
374
+ * fenced — its `revokeLease` has run — so the owner's ack cannot claim a fence the handle has not
375
+ * taken; the owner waits on this before writing the release. A revoke that arrives before the handle
376
+ * is installed, or before `registerAdmission` supplies the real revoker, resolves only when the fence
377
+ * finally lands. A revoker that throws, or whose promise rejects, fails this promise rather than
378
+ * resolving it, so the owner falls back to its own lease bound rather than being told the fence
379
+ * succeeded.
380
+ */
381
+ revokeRemoteAdmission(ownerAdmissionId: number): Promise<void>;
382
+ /**
383
+ * Fence every relayed handle this thread holds, fail-closed (harper-pro#852). Called when the owner
384
+ * worker that granted them is gone (its thread exited, so its delegation table died with it): the
385
+ * handles are no longer backed by any delegation, so nothing here may keep committing. There is no
386
+ * owner left to tell, so this only revokes locally and drops the entries; a still-pending ack is
387
+ * resolved, since the handle can no longer commit.
388
+ */
389
+ fenceAllRemoteAdmissions(): void;
390
+ /**
391
+ * End this node's admission for a key. The delegation is deliberately KEPT: that is the
392
+ * amortization, and the next `lock()` on this node costs nothing. Returns the durable release
393
+ * write only when the delegation is actually being given up.
394
+ */
395
+ release(key: any, admissionId: number): Promise<void> | void;
396
+ /**
397
+ * Apply a control entry from a peer (or a replayed one). Idempotent by `(requester, tsR)`: a
398
+ * release that arrives twice, or is replayed from the log long after its producer is gone, must be
399
+ * harmless.
400
+ *
401
+ * `author` is the node the entry was actually written by, taken from the audit header rather than
402
+ * the payload — without it a peer could write a release naming any other node and clear a
403
+ * delegation it does not hold.
404
+ */
405
+ applyEntry(entry: LockControlEntry, author: string, position?: number): void;
406
+ /**
407
+ * Inbound delegation request from a peer, for a key this node homes. The home is the single
408
+ * arbiter, so this is the whole of the exclusion argument: one live grant per key, and a successor
409
+ * only after the predecessor is recalled-and-drained or provably expired here.
410
+ */
411
+ onDelegationRequest(request: DelegationRequest): Promise<DelegationReply>;
412
+ /**
413
+ * Inbound recall from a key's home. Revokes capability rather than closing the door: no new
414
+ * admission may start, live ones are drained, and the release is written only once the delegation
415
+ * can no longer admit or commit. A recall for a token we no longer hold is a no-op, not an error.
416
+ */
417
+ onDelegationRecall(recall: DelegationRecall): Promise<void>;
418
+ /** Expire delegations and grants whose deadlines have passed. Bounded work per call. */
419
+ tick(): void;
420
+ /**
421
+ * One table's half of `quiesceDelegations`. Never throws for a single grant: a transition needs to
422
+ * know exactly what is still live, and one unreachable delegate must not hide the rest.
423
+ */
424
+ quiesce(result: QuiesceResult, remaining: () => number): Promise<void>;
425
+ /**
426
+ * Stop this coordinator and invalidate what it issued. Expiring every delegation is the part that
427
+ * matters: `close()` runs when a transport is replaced (a component reload is enough), and a
428
+ * successor coordinator must not be able to grant a key whose predecessor handles are still live.
429
+ */
430
+ close(): void;
431
+ /**
432
+ * Register how to revoke the handle an admission produced. `Table.lock()` calls this once the
433
+ * handle has joined the round, so a recall can fence a write that was staged and then unlocked.
434
+ */
435
+ registerAdmission(admissionId: number, revoke: () => void | Promise<void>): void;
436
+ }
437
+ type CoordinatorResolver = (database: string, table: string) => LockCoordinator | undefined;
438
+ type ControlWriter = (entry: LockControlEntry) => Promise<number | void> | number | void;
439
+ type ControlWriterResolver = (database: string, table: string) => ControlWriter | undefined;
440
+ /** Installed by Table.ts so a transport can push received entries in without importing Table. */
441
+ export declare function setLockCoordinatorResolver(resolve: CoordinatorResolver, resolveAdmitting?: CoordinatorResolver, resolveControlWriter?: ControlWriterResolver): void;
442
+ export interface QuiesceOutstanding {
443
+ table: string;
444
+ key: unknown;
445
+ /** Present when this node was the HOME and the delegate did not confirm. */
446
+ delegate?: string;
447
+ reason: string;
448
+ }
449
+ export interface QuiesceResult {
450
+ /**
451
+ * Whether this result is a PROOF of quiescence, or merely a report of what was swept.
452
+ *
453
+ * A sweep can only visit coordinators that exist on this thread, and they are built lazily — a
454
+ * table nothing has touched since a restart has none, so an empty `outstanding` would otherwise
455
+ * read as "nothing is live" when the previous incarnation's delegations are still running
456
+ * elsewhere. An orchestrator must require `complete && outstanding.length === 0`; anything else
457
+ * means fall back to the drain interval for this node.
458
+ */
459
+ complete: boolean;
460
+ /** Delegations this node held and gave up, so it can no longer admit under them. */
461
+ surrendered: number;
462
+ /** Grants this node issued whose delegate confirmed it stopped admitting. */
463
+ recalled: number;
464
+ /**
465
+ * What is still live, or unprovable. Empty means this node is provably quiesced for the database —
466
+ * and ONLY then, which is why a coordinator still inside its restart quarantine contributes an
467
+ * entry here rather than reporting a clean sweep it cannot back.
468
+ */
469
+ outstanding: QuiesceOutstanding[];
470
+ }
471
+ /**
472
+ * Stop this node admitting under the current generation for `database`, and say whether it is
473
+ * provably done (harper-pro#856).
474
+ *
475
+ * A membership change otherwise has to wait out `DELEGATION_LEASE_MS + LOCK_LEASE_SKEW_MS` before the
476
+ * next generation may activate, because authority already issued under the old one has to expire —
477
+ * roughly six minutes during which the staged nodes serve no cluster locks at all. That interval is a
478
+ * TIMER, chosen because you cannot recall what you cannot reach. In a planned transition every
479
+ * participant is reachable, so the same guarantee can be *established* instead of waited out: this
480
+ * drains both directions and reports what, if anything, is left.
481
+ *
482
+ * - **As a delegate** it surrenders every delegation it holds. This is the load-bearing half: a
483
+ * delegate is what admits, and `onDelegationRecall` is the existing path whose resolution means
484
+ * "nothing on this node can admit on that token" — live critical sections are drained, not cut.
485
+ * - **As a home** it recalls every grant it issued, so delegates elsewhere stop too. Redundant when
486
+ * every node is quiescing at once, and the reason this still terminates when one is not.
487
+ *
488
+ * `outstanding` empty on every node in `homes(g) ∪ homes(g+1)` is the operator's evidence that the
489
+ * next generation may be activated immediately. Anything left is a node to fall back to the timer for,
490
+ * or to fence externally — this never claims a drain it did not get, and never throws for one grant.
491
+ */
492
+ export declare function quiesceDelegations(database: string, budgetMs: number): Promise<QuiesceResult>;
493
+ /**
494
+ * Commit a `lockBarrier` entry for the table and resolve to its transaction-log position — the §7.2
495
+ * recovery fence, for a transport answering a peer's recovery probe. The entry is appended after
496
+ * every transaction this node had committed when the call was made, so a peer that has applied this
497
+ * origin's log through the returned position has applied all of them.
498
+ *
499
+ * The transport supplies the nonce it will match the entry on, since a position alone is not an
500
+ * identity: a restart after the wall clock moved backwards can reissue a log key, and a drain that
501
+ * matched the earlier entry at that key would declare this origin drained with its post-restart
502
+ * commits unapplied.
503
+ *
504
+ * Strictly this node's own commit, never the transport's `writeControl`: the fence is a position in
505
+ * THIS origin's log, and the caller is the transport itself — a relaying hook would answer with a
506
+ * position that is not local, or re-enter the operation that called here. A write that commits
507
+ * without a position rejects rather than resolve, since a barrier nobody can wait on is not a fence.
508
+ */
509
+ export declare function writeLockBarrier(database: string, table: string, nonce: number): Promise<number>;
510
+ /**
511
+ * Register harper-pro's transport for a database, on THIS thread.
512
+ *
513
+ * **It must be registered on every worker that can serve a `lock()`, not only the coordinating one**
514
+ * — including a dedicated application worker (harper#2524) — and core cannot check that. `clusterRequiredDatabases` is module state, so a worker that never registers never latches —
515
+ * and a default-scoped `lock()` there takes the Phase 0 node lock alone while a peer runs the cluster
516
+ * protocol, which is two nodes admitting one key. The `ownsCoordination()` fail-closed path only
517
+ * reaches a worker that has a transport. Registering everywhere also makes that path the one a
518
+ * non-owner worker takes, which is what it exists for.
519
+ */
520
+ export declare function registerClusterLockTransport(database: string, transport: ClusterLockTransport): void;
521
+ export declare function unregisterClusterLockTransport(database: string, standalone?: boolean): void;
522
+ /** True once a transport has been registered for this database and no standalone claim has cleared it. */
523
+ export declare function isClusterLockRequired(database: string): boolean;
524
+ /**
525
+ * The registered transport, if any. The `size` check keeps the Phase 0 path free of a map lookup on
526
+ * every `lock()` in a build where no transport is ever registered.
527
+ */
528
+ export declare function getClusterLockTransport(database: string): ClusterLockTransport | undefined;
529
+ export declare function hasClusterLockTransports(): boolean;
530
+ export declare function deliverLockControlEntry(database: string, table: string, entry: LockControlEntry, author: string, position: number): void;
531
+ export declare function deliverDelegationRequest(database: string, table: string, request: DelegationRequest): Promise<DelegationReply>;
532
+ export declare function deliverDelegationRecall(database: string, table: string, recall: DelegationRecall): Promise<void>;
533
+ /**
534
+ * The owner thread's end of `acquireOnOwner`: mint an admission for a `lock()` served on another
535
+ * worker thread, and register `revoke` as the way to fence that thread's handle. `revoke` is what the
536
+ * transport wires to a cross-thread message; a recall or surrender here calls it and waits for the
537
+ * fence before writing the release. Runs on the coordinating thread, resolved through the transport-
538
+ * gated resolver so it fails when this thread does not coordinate the database — the same shape as
539
+ * `deliverDelegationRequest`. Returns the round the calling worker installs as a remote admission.
540
+ */
541
+ export declare function acquireForRelay(database: string, table: string, key: any, leaseMs: number, waitMs: number, makeRevoke: (round: LockRound) => () => void | Promise<void>): Promise<LockRound>;
542
+ /** The owner thread's end of `releaseOnOwner`: end a relayed admission the owner minted. */
543
+ export declare function releaseForRelay(database: string, table: string, key: any, admissionId: number): Promise<void> | void;
544
+ /**
545
+ * The calling thread's end of an owner `revoke`: fence the handle for a relayed admission this thread
546
+ * holds. Resolves once the handle's `revokeLease` has run, so the owner may wait for the fence before
547
+ * it writes the release. The admitting resolver answers the coordinator that adopted the admission
548
+ * across a transport swap.
549
+ */
550
+ export declare function revokeRelayedAdmission(database: string, table: string, admissionId: number): Promise<void>;
551
+ /**
552
+ * Fail-closed fence for every relayed handle a table's coordinator holds (harper-pro#852), for when
553
+ * the owner worker that granted them has exited and its delegations are gone. Harper-pro calls this
554
+ * per table when it learns the coordinating thread for a database changed.
555
+ */
556
+ export declare function fenceRelayedAdmissions(database: string, table: string): void;
557
+ export {};