@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
@@ -20,6 +20,7 @@ import {
20
20
  } from './componentSecrets.ts';
21
21
  import type { SecretsView } from './componentSecrets.ts';
22
22
  import { deployLifecycle } from './deployLifecycle.ts';
23
+ import { thisThreadOwnsApplication } from '../server/threads/isolatedApplications.ts';
23
24
 
24
25
  export class MissingDefaultFilesOptionError extends Error {
25
26
  constructor() {
@@ -68,6 +69,7 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
68
69
  #deployEndHandler: (name: string) => void;
69
70
  #deployInFlight: boolean = false;
70
71
  #restartRequestedDuringDeploy: boolean = false;
72
+ #optionsReady: boolean = false;
71
73
  applicationScope?: ApplicationScope;
72
74
 
73
75
  options: OptionsWatcher;
@@ -241,7 +243,10 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
241
243
 
242
244
  ensureTable<TableResourceType = unknown>(options: any): TableResourceType {
243
245
  options.origin = this.#origin;
244
- return scopedTableFactory(this.applicationScope?.branches)<TableResourceType>(options);
246
+ return scopedTableFactory(
247
+ this.applicationScope?.branches,
248
+ thisThreadOwnsApplication(this.applicationScope?.name)
249
+ )<TableResourceType>(options);
245
250
  }
246
251
 
247
252
  #handleOptionsWatcherReady(): void {
@@ -251,7 +256,21 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
251
256
  // We could make the user call `await scope.ready()` in their `handleApplication` function, but that could lead to the same issue and it'd
252
257
  // be harder for the user to understand why.
253
258
 
259
+ // A second `ready` means the scope had no config of its own and now does — a config file
260
+ // that was unreadable when this worker booted, or one recreated after deletion. Re-emitting
261
+ // reaches nobody: componentLoader is long past its `await scope.ready`, so the component is
262
+ // running on the defaults until something restarts it. Same recovery as the `remove`
263
+ // listener, and the same convention — a plugin with its own `ready` handler owns it.
264
+ const started = this.#optionsReady;
265
+ this.#optionsReady = true;
266
+ const restartNeeded = started && this.listenerCount('ready') === 0;
267
+
254
268
  this.emit('ready');
269
+
270
+ if (restartNeeded) {
271
+ this.#logger.debug?.('Options arrived after the scope started, requesting restart');
272
+ this.requestRestart();
273
+ }
255
274
  }
256
275
 
257
276
  #handleError(error: unknown): void {
@@ -32,8 +32,13 @@ import { restartWorkers, getWorkerIndex } from '../server/threads/manageThreads.
32
32
  import { resetRestartNeeded, subscribeToRestartRequests } from './requestRestart.ts';
33
33
  import { trackScopeClose } from './scopeShutdown.ts';
34
34
  import { deployLifecycle } from './deployLifecycle.ts';
35
- import { assertBranchedDatabases } from './Application.ts';
35
+ import { assertBranchedDatabases, assertIsolationConfig } from './Application.ts';
36
36
  import { prepareBranches } from '../resources/branchDatabase.ts';
37
+ import {
38
+ isIsolatedApplication,
39
+ shouldLoadApplicationHere,
40
+ thisThreadOwnsApplication,
41
+ } from '../server/threads/isolatedApplications.ts';
37
42
  import { toScopeMount, nestScopeMount, type ScopeMount } from './scopeMount.ts';
38
43
  import { scopedImport } from '../security/jsLoader.ts';
39
44
  import { server } from '../server/Server.ts';
@@ -161,6 +166,32 @@ function rootConfigBranchedDatabases(appName: string): string[] | true | undefin
161
166
  return (getConfigObj()?.[appName] as any)?.branchedDatabases;
162
167
  }
163
168
 
169
+ /**
170
+ * Whether this thread is the one that loads `appName` (harper#642, tier 2). An isolated application
171
+ * is loaded by its dedicated worker and by nothing else; every other thread loads only the
172
+ * applications that are not isolated. Decided before any of the application's modules are imported.
173
+ * With no worker threads at all there is nowhere for an isolated application to run, so it fails
174
+ * closed here rather than silently sharing the only thread.
175
+ */
176
+ function placedOnThisThread(appName: string): boolean {
177
+ if (Object.hasOwn(TRUSTED_RESOURCE_PLUGINS, appName)) return true;
178
+ try {
179
+ assertIsolationConfig(appName, (getConfigObj()?.[appName] as any)?.isolated);
180
+ } catch (error) {
181
+ componentLifecycle.failed(appName, error, `Component '${appName}' failed to load`);
182
+ return false;
183
+ }
184
+ if (isIsolatedApplication(appName) && isMainThread && getWorkerIndex() === 0) {
185
+ componentLifecycle.failed(
186
+ appName,
187
+ new Error(`Application '${appName}' is isolated, which needs a worker thread of its own, but threads.count is 0`),
188
+ `Component '${appName}' failed to load`
189
+ );
190
+ return false;
191
+ }
192
+ return shouldLoadApplicationHere(appName);
193
+ }
194
+
164
195
  /**
165
196
  * Resolves the mount for `appName`, or reports the failure and returns `undefined` if the
166
197
  * configured `host`/`urlPath` is invalid. The caller must skip loading the application in that
@@ -268,6 +299,7 @@ export async function loadComponentDirectories(
268
299
  }
269
300
  return;
270
301
  }
302
+ if (!placedOnThisThread(appName)) return;
271
303
  const mountResult = tryRootConfigMount(appName);
272
304
  if (!mountResult.ok) return;
273
305
  const loadedModules = new Set<any>();
@@ -316,6 +348,7 @@ export async function loadComponentDirectories(
316
348
  );
317
349
  continue;
318
350
  }
351
+ if (!placedOnThisThread(appName)) continue;
319
352
  const appFolder = join(CF_ROUTES_DIR, appName);
320
353
  const mountResult = tryRootConfigMount(appName);
321
354
  if (!mountResult.ok) continue;
@@ -347,7 +380,7 @@ export async function loadComponentDirectories(
347
380
  if (hdbAppFolder) {
348
381
  if (getWorkerIndex() === 0) harperLogger.info?.('Loading application from ' + hdbAppFolder);
349
382
  const mountResult = tryRootConfigMount(basename(hdbAppFolder));
350
- if (mountResult.ok) {
383
+ if (mountResult.ok && placedOnThisThread(basename(hdbAppFolder))) {
351
384
  cfsLoaded.push(
352
385
  serializeComponentLoad(hdbAppFolder, () =>
353
386
  loadComponent(hdbAppFolder, cycleResources, hdbAppFolder, {
@@ -849,6 +882,9 @@ export async function loadComponent(
849
882
  if (!componentConfig) continue;
850
883
 
851
884
  // Initialize loading status for all components (applications and extensions)
885
+ // A root-config application (`package:`) is placed like a directory one: an isolated application
886
+ // loads only in its dedicated worker, and that worker loads no other application.
887
+ if (isRoot && componentConfig.package && !placedOnThisThread(componentName)) continue;
852
888
  componentLifecycle.loading(componentStatusName);
853
889
 
854
890
  const subApplicationScope = isRoot
@@ -940,7 +976,10 @@ export async function loadComponent(
940
976
  // our own trusted modules can be directly retrieved from our map, otherwise use the (configurable) secure module loader
941
977
  const ensureTable = (options: any) => {
942
978
  options.origin = origin;
943
- return scopedTableFactory(applicationScope.branches)(options);
979
+ return scopedTableFactory(
980
+ applicationScope.branches,
981
+ thisThreadOwnsApplication(applicationScope.name)
982
+ )(options);
944
983
  };
945
984
  // call the main start hook
946
985
  const network =
@@ -1160,7 +1199,7 @@ export async function loadComponent(
1160
1199
  // — not abort the loop and discard a pending follow-up, like the save that fixes it.
1161
1200
  try {
1162
1201
  await loadComponentDirectories();
1163
- await restartWorkers();
1202
+ await restartWorkers(undefined, undefined, true, null, '*');
1164
1203
  } catch (error) {
1165
1204
  harperLogger.error('Error during component reload', error);
1166
1205
  }
@@ -13,7 +13,10 @@ import type { ProgressEmitter } from '../server/serverHelpers/progressEmitter.ts
13
13
  const { HTTP_STATUS_CODES } = hdbErrors;
14
14
 
15
15
  const DEPLOYMENT_TABLE = terms.SYSTEM_TABLE_NAMES.DEPLOYMENT_TABLE_NAME;
16
- const TERMINAL_STATUSES = new Set(['success', 'failed', 'rolled_back']);
16
+ // `staged` belongs here: the build is complete on disk and nothing further happens to this deployment, so
17
+ // an SSE tail must finish and `delete_deployment_payload` must not refuse a tarball nobody is installing
18
+ // from any more. Activating the artifact is a separate deployment with a row of its own.
19
+ const TERMINAL_STATUSES = new Set(['success', 'failed', 'rolled_back', 'staged']);
17
20
 
18
21
  interface ListRequest {
19
22
  project?: string;
@@ -55,7 +55,11 @@ type DeploymentStatus =
55
55
  | 'restarting'
56
56
  | 'success'
57
57
  | 'failed'
58
- | 'rolled_back';
58
+ | 'rolled_back'
59
+ // Terminal: the build is complete and certified on disk but was not activated. Terminal rather than
60
+ // in-flight because nothing further happens to THIS deployment — activating its artifact is a separate
61
+ // operator action with a row of its own.
62
+ | 'staged';
59
63
 
60
64
  interface CreateOptions {
61
65
  project?: string;
@@ -63,6 +67,8 @@ interface CreateOptions {
63
67
  user?: string;
64
68
  restart_mode?: 'immediate' | 'rolling' | null;
65
69
  rollback_of?: string | null;
70
+ /** The staged deployment whose artifact this deployment activated. */
71
+ activated_from?: string | null;
66
72
  // Deploy credentials in reference form (`{ registry, secret, scope? }` / `{ host, secret,
67
73
  // username? }`) — never a literal token. Kept so a rollback can re-resolve the credential from
68
74
  // hdb_secret without the operator re-supplying it. Null when the deploy used no credentials or a
@@ -141,6 +147,7 @@ export class DeploymentRecorder {
141
147
  completed_at: null,
142
148
  user: options.user ?? null,
143
149
  rollback_of: options.rollback_of ?? null,
150
+ activated_from: options.activated_from ?? null,
144
151
  credentials: options.credentials ?? null,
145
152
  error: null,
146
153
  };
@@ -465,7 +472,7 @@ export class DeploymentRecorder {
465
472
  this.sealed = true;
466
473
  }
467
474
 
468
- async finish(status: 'success' | 'failed' | 'rolled_back', error?: unknown): Promise<void> {
475
+ async finish(status: 'success' | 'failed' | 'rolled_back' | 'staged', error?: unknown): Promise<void> {
469
476
  if (this.finished) return;
470
477
  // Send a terminal sentinel through the emitter (if any) BEFORE we unsubscribe and
471
478
  // remove it from the registry, so any SSE tail subscribers can resolve their wait
@@ -0,0 +1,109 @@
1
+ # components/mcp/ — Design notes
2
+
3
+ The MCP Streamable-HTTP surface.
4
+
5
+ **Read this when:** adding or changing an MCP method, tool, prompt or profile.
6
+
7
+ Index of every design note: [DESIGN.md](../../DESIGN.md).
8
+
9
+ ---
10
+
11
+ ## MCP protocol surface (`components/mcp/`)
12
+
13
+ The MCP Streamable-HTTP transport (spec `2025-06-18`) is served at `/mcp` under **two profiles**: an
14
+ _operations_ profile (mounted on the Fastify operations server) and an _application_ profile (mounted on
15
+ the Harper application HTTP server). Both share `transport.ts` (the JSON-RPC dispatcher) but differ in what
16
+ they expose — operations surfaces management operations as tools; application surfaces exported
17
+ Resources/tables. Profile gating runs throughout (`completeResourceArgument`, prompt visibility, tool
18
+ `visibleTo`), so when adding a method, decide which profile(s) it belongs to rather than assuming both.
19
+
20
+ A handful of design points are non-obvious and easy to break:
21
+
22
+ - **Per-call POST SSE streaming has a close-before-subscribe race.** A `tools/call` that opts into streaming
23
+ (`Accept: text/event-stream`) gets an `IterableEventQueue` whose frames the adapter consumes via the event
24
+ API (`on('data')` / `once('close')`), **not** `for await` — the async iterator does not terminate on
25
+ `'close'`. The streaming tool handler is therefore dispatched inside a `setImmediate` (a _detached_,
26
+ deferred task) so the adapter's consumer attaches **before** any frame is produced. Without the defer, a
27
+ fast handler emits its final frame + `close` synchronously; the queue buffers `'data'` but not `'close'`,
28
+ and the stream hangs. Any handler on this path must check `signal.aborted` first (cancellation can land
29
+ before the deferred task runs).
30
+
31
+ - **Server→client requests are correlated across workers.** `serverRequests.ts` lets a streaming `tools/call`
32
+ call _back_ into the client (`sampling/createMessage`, `elicitation/create`, `roots/list`) and await the
33
+ reply. The request frame rides **the call's POST SSE stream**; the client's response is a _fresh POST_ that
34
+ can land on **any worker**. The pending-promise registry is per-worker, so a response with no local match
35
+ is fanned out over ITC (`MCP_CLIENT_RESPONSE`) and the worker holding the promise resolves it (mirrors
36
+ `components/status/crossThread.ts`). Request ids are `srv-${randomUUID()}` — **not** a per-worker counter,
37
+ which would collide on `(sessionId, id)` across workers and misroute responses. Methods are capability-gated
38
+ (`METHOD_CAPABILITY`) against the client capabilities captured at `initialize`; the registry is bounded
39
+ (timeout + high-water-mark) so a non-responding client can't leak promises.
40
+
41
+ - **Application tools must be rebuilt after JS resources register, not just on schema changes.** The
42
+ application-profile tool scan (`registerApplicationTools`) runs at MCP component boot and on schema-change
43
+ ITC events — both of which fire while the `@table` classes register, **before** the `jsResource` plugin
44
+ registers the component's exported `class X extends tables.X` subclass. That subclass is the object the
45
+ registry ends up holding (REST routes to it) and the only place author opt-ins (`static mcpTools`/
46
+ `mcpPrompts`) live, so a scan that ran earlier sees only the base table class and misses them (#1448). The
47
+ fix: `jsResource` fires `signalResourcesRegistered()` (a deliberately **local-only**, non-ITC signal in
48
+ `utility/signalling.ts`, backed by `resourceHandler` in `server/itc/serverHandlers.js` — each worker
49
+ registers its own JS resources, so the rebuild belongs in that worker) after registration; `listChanged`
50
+ subscribes and re-runs the scan. Consequence: the verb tools (`create_*` etc.) now bind to the subclass and
51
+ honor its `post`/`patch` overrides, matching REST — previously they bound to the base table class and
52
+ silently bypassed those overrides. Advertised CRUD output schemas are still table-derived, so an overridden
53
+ write verb whose return diverges from `{ id }`/`{ ok }`/`{ deleted }` advertises a subset shape (the in-use
54
+ SDK tolerates supersets; tightening per-override envelopes is sibling-issue work — see the `derive.ts`
55
+ envelope note).
56
+
57
+ - **Resource subscriptions are row-backed via the audit log.** `resources/subscribe` resolves the URI to a
58
+ Resource and drives `Table.subscribe` off the audit-store `'committed'` path (same machinery as the
59
+ "Audit-store `'committed'` notification batching" section above). The targeting is the subtle part:
60
+ `getMatch` returns the matched Resource plus the remaining path on `relativeURL`, and `subscribeToResource`
61
+ sets **both** `request.id` (the record key, or `undefined`) **and** `request.isCollection` from it. A record
62
+ URI (`…/WorkItem/42`) watches that record; a collection URI (`…/WorkItem`, what `resources/list` advertises)
63
+ watches the whole table. `new RequestTarget(path)` parses an id out of the path on its own, so _both_ fields
64
+ must be overridden — otherwise a collection URI silently watches a phantom record named after the resource
65
+ and receives nothing. `harper://*` pseudo-resources are **list-changed-only** (not row-backed). Subscriptions
66
+ use `omitCurrent` (notify on change, not a retained snapshot — the notification just says "re-read this").
67
+
68
+ - **Subscribe requires a live GET stream; teardown is asymmetric.** `resources/subscribe` rejects (`-32602`)
69
+ if no GET SSE stream has registered the session — the audit-log iterator has nowhere to deliver, and there'd
70
+ be no `RegisteredSession` close hook to stop it. The GET `'close'` handler drops **subscriptions only**
71
+ (`dropSessionSubscriptions`), _not_ pending server requests: those ride the per-call POST stream, so a normal
72
+ GET reconnect must not reject an in-flight `ctx.serverRequest`. A `DELETE` (explicit session teardown) drops
73
+ **both**, because it may arrive with no open GET stream.
74
+
75
+ - **SSE resumability (`Last-Event-ID`).** Every GET-channel frame goes through `pushSessionFrame`, which
76
+ assigns a monotonic event id and appends to a bounded per-session `replayBuffer`. On reconnect with a
77
+ `Last-Event-ID` header, `replaySince` re-sends only the frames after that id. The event-id sequence **and**
78
+ the buffer carry across a supersede (a fresh GET replacing the old one for the same session id), so ids stay
79
+ monotonic and no frame is lost across a reconnect.
80
+
81
+ - **Test seams avoid loading thread/audit machinery in unit tests.** `_setSubscribeImplForTest`
82
+ (`resources.ts`) and `_setItcForTest` (`serverRequests.ts`) inject fakes so the unit suite needn't spin up
83
+ the audit log or ITC. Consequence: the subscribe **targeting** logic (`id`/`isCollection` derivation) is
84
+ _bypassed_ by the seam and is therefore covered at the **integration** level (`sse-listchanged.test.ts` N3
85
+ record / N4 collection), not in unit tests.
86
+
87
+ Two related traps: the create/schema-update path's exclusive `update-attributes` lock is a
88
+ synchronous bounded wait (`acquireUpdateAttributesLock` in `Table.ts`: brief hot spin, then
89
+ `Atomics.wait` backoff, retryable `ServerError` after the 10s `UPDATE_ATTRIBUTES_LOCK_TIMEOUT` — harper#2251; it used to be
90
+ an unbounded `while (!tryLock()) {}` spin that pinned a worker core forever if the holder never
91
+ released). Release is structural — `table()` releases in a single `finally` and `dropTable` uses
92
+ `withUpdateAttributesLock` — so a throw inside the locked window cannot leak the lock (regression
93
+ suite: `unitTests/resources/updateAttributesLock.test.js`). Because the acquire can now throw,
94
+ `table()` takes the RocksDB lock _before_ it mutates the live `Table` (attributes, class metadata,
95
+ index handles): losing the race then leaves this worker's in-memory schema exactly as it found it,
96
+ and moving any mutation above that acquire reintroduces schema drift the catalog never saw. LMDB
97
+ keeps the lazy acquire — its `exclusiveLock()` is an environment-wide write transaction that cannot
98
+ time out, so taking it eagerly would stall every write to the database on an unchanged reload. A
99
+ successful acquire that waited past `UPDATE_ATTRIBUTES_LOCK_SLOW_WAIT` (1s) warns once, since
100
+ contention is otherwise invisible until it becomes a timeout. The locked
101
+ sections MUST stay synchronous: the wait blocks the event loop, so an awaited operation inside
102
+ one would stall a concurrent acquirer to its deadline. And dropping then recreating a
103
+ same-named table within one process requires @harperfast/rocksdb-js >= the column-family
104
+ eviction fix (1.4.3 / rocksdb-js#<main PR>): older bindings keep the dropped column family's
105
+ by-name registry entry alive whenever other worker threads hold handles, so the recreate
106
+ silently reuses a dangling handle and every write fails with "Invalid column family specified
107
+ in write batch", poisoning the whole database env until restart. The regression suite for all
108
+ of this is `unitTests/resources/dropTableGhost.test.js` (it fails by design on pre-fix
109
+ bindings).
@@ -6,7 +6,7 @@
6
6
  * (session id, profile, tool name).
7
7
  *
8
8
  * Argument summarization runs through a redaction step that drops anything
9
- * that looks like a credential (key/secret/password). Operators who need
9
+ * that looks like a credential (secret/password/token). Operators who need
10
10
  * stricter PII handling configure `mcp.audit.argumentRedactor` to a custom
11
11
  * function via a future component-author hook (v1.1).
12
12
  */
@@ -25,17 +25,17 @@ export interface AuditEntry {
25
25
  }
26
26
 
27
27
  const REDACTION_PATTERN = /(secret|password|token|api[-_]?key|credentials?|auth)/i;
28
- // Exact field names that carry secret material without a credential-looking name: `value`/`values`
29
- // (set_secret plaintext, set_env_value .env secrets) and `envelope` (set_secret ciphertext). These
30
- // mirror the fields processLocalTransaction strips from the REST operations log — the MCP audit
31
- // path must not become a bypass. Exact-match so e.g. `search_value` stays auditable.
32
- const REDACTION_EXACT_FIELDS = /^(value|values|envelope)$/i;
33
- // ...but only for the operations that actually put secrets in those generically-named fields.
34
- // `value`/`values` are common, non-secret params on many other operations (record data, config
35
- // values), so blanket-redacting them would gut the audit trail for unrelated tools. The global
36
- // REDACTION_PATTERN still applies to every tool; this just narrows the exact-field masking to the
37
- // secret-bearing ops. NOTE: extend this set if a new op ever carries a secret in a plain field.
38
- const EXACT_FIELD_TOOLS = new Set(['set_secret', 'set_env_value']);
28
+ // Both secret tools carry all three fields, not just the ones each declares: MCP forwards arguments
29
+ // as-is and audits them after the handler, so a field the tool rejects is still logged. Mirrors what
30
+ // the REST operations log strips unconditionally (UNLOGGABLE_OPERATION_FIELDS).
31
+ const SECRET_VALUE_FIELDS: ReadonlySet<string> = new Set(['value', 'values', 'envelope']);
32
+ // Per tool, not global: `value`/`values` are ordinary params on many other operations.
33
+ const REDACTION_FIELDS_BY_TOOL = new Map<string, ReadonlySet<string>>([
34
+ ['set_secret', SECRET_VALUE_FIELDS],
35
+ ['set_env_value', SECRET_VALUE_FIELDS],
36
+ ['add_ssh_key', new Set(['key'])],
37
+ ['update_ssh_key', new Set(['key'])],
38
+ ]);
39
39
  const REDACTION_PLACEHOLDER = '[redacted]';
40
40
  const MAX_REDACTION_DEPTH = 10;
41
41
 
@@ -46,23 +46,27 @@ const MAX_REDACTION_DEPTH = 10;
46
46
  * a credential buried below the depth limit cannot leak. Returns a shallow
47
47
  * clone — the caller's payload is never mutated.
48
48
  */
49
- export function redactArgs(value: unknown, depth = 0, redactExactFields = false): unknown {
49
+ export function redactArgs(value: unknown, depth = 0, redactionFields?: ReadonlySet<string>): unknown {
50
50
  if (value === null || typeof value !== 'object') return value;
51
51
  if (depth > MAX_REDACTION_DEPTH) return REDACTION_PLACEHOLDER;
52
52
  if (Array.isArray(value)) {
53
- return value.map((v) => redactArgs(v, depth + 1, redactExactFields));
53
+ return value.map((v) => redactArgs(v, depth + 1, redactionFields));
54
54
  }
55
55
  const out: Record<string, unknown> = {};
56
56
  for (const [k, v] of Object.entries(value)) {
57
- if (REDACTION_PATTERN.test(k) || (redactExactFields && REDACTION_EXACT_FIELDS.test(k))) {
57
+ if (REDACTION_PATTERN.test(k) || redactionFields?.has(k.toLowerCase())) {
58
58
  out[k] = REDACTION_PLACEHOLDER;
59
59
  } else {
60
- out[k] = redactArgs(v, depth + 1, redactExactFields);
60
+ out[k] = redactArgs(v, depth + 1, redactionFields);
61
61
  }
62
62
  }
63
63
  return out;
64
64
  }
65
65
 
66
+ export function redactArgsForTool(value: unknown, tool: string): unknown {
67
+ return redactArgs(value, 0, REDACTION_FIELDS_BY_TOOL.get(tool));
68
+ }
69
+
66
70
  /** Mask a session id for logging — first 8 chars, suffix elided. */
67
71
  export function maskSessionId(id: string): string {
68
72
  if (typeof id !== 'string' || id.length <= 8) return id;
@@ -79,7 +83,7 @@ export function emitAuditEntry(entry: AuditEntry): void {
79
83
  const masked = {
80
84
  ...entry,
81
85
  sessionId: maskSessionId(entry.sessionId),
82
- args: redactArgs(entry.args, 0, EXACT_FIELD_TOOLS.has(entry.tool)),
86
+ args: redactArgsForTool(entry.args, entry.tool),
83
87
  };
84
88
  harperLogger.info({ category: 'mcp.audit', ...masked });
85
89
  } catch (err) {