@harperfast/harper 5.2.10 → 5.3.0-alpha.1

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 (554) hide show
  1. package/agent/mcpTools.ts +1 -1
  2. package/agent/session.ts +25 -14
  3. package/bin/cliOperations.ts +46 -9
  4. package/bin/copyDb.ts +282 -59
  5. package/bin/deploySetup.ts +16 -5
  6. package/bin/harper.ts +1 -1
  7. package/bin/help.ts +4 -1
  8. package/bin/lite.ts +4 -1
  9. package/bin/restart.ts +59 -3
  10. package/bin/run.ts +6 -11
  11. package/bin/upgrade.js +7 -3
  12. package/bin/workloadIdentity.ts +119 -0
  13. package/components/Application.ts +2414 -242
  14. package/components/ApplicationScope.ts +8 -0
  15. package/components/EntryHandler.ts +59 -39
  16. package/components/OptionsWatcher.ts +150 -74
  17. package/components/RuntimeModuleTracker.ts +38 -7
  18. package/components/Scope.ts +37 -15
  19. package/components/awaitRestart.ts +84 -0
  20. package/components/componentLoader.ts +340 -30
  21. package/components/componentPreparationLock.ts +16 -5
  22. package/components/mcp/adapters/harperHttp.ts +4 -0
  23. package/components/mcp/listChanged.ts +4 -0
  24. package/components/mcp/toolRegistry.ts +2 -0
  25. package/components/mcp/tools/operations.ts +9 -0
  26. package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
  27. package/components/operations.js +270 -78
  28. package/components/operationsValidation.js +49 -1
  29. package/components/status/ComponentStatusRegistry.ts +59 -0
  30. package/config/RootConfigWatcher.ts +80 -34
  31. package/config/configUtils.ts +291 -34
  32. package/config/harperConfigEnvVars.ts +170 -27
  33. package/config-root.schema.json +29 -0
  34. package/dataLayer/blobBackup.ts +160 -50
  35. package/dataLayer/delete.ts +6 -1
  36. package/dataLayer/harperBridge/ResourceBridge.ts +52 -8
  37. package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  38. package/dataLayer/hdbInfoController.ts +34 -1
  39. package/dataLayer/insert.ts +44 -1
  40. package/dataLayer/rocksdbBackup.ts +53 -10
  41. package/dataLayer/schema.ts +11 -1
  42. package/dataLayer/schemaDescribe.ts +8 -1
  43. package/dist/agent/mcpTools.js +1 -1
  44. package/dist/agent/mcpTools.js.map +1 -1
  45. package/dist/agent/session.d.ts +22 -0
  46. package/dist/agent/session.js +26 -15
  47. package/dist/agent/session.js.map +1 -1
  48. package/dist/bin/cliOperations.js +48 -9
  49. package/dist/bin/cliOperations.js.map +1 -1
  50. package/dist/bin/copyDb.d.ts +12 -1
  51. package/dist/bin/copyDb.js +248 -60
  52. package/dist/bin/copyDb.js.map +1 -1
  53. package/dist/bin/deploySetup.d.ts +2 -0
  54. package/dist/bin/deploySetup.js +11 -3
  55. package/dist/bin/deploySetup.js.map +1 -1
  56. package/dist/bin/harper.js +1 -1
  57. package/dist/bin/harper.js.map +1 -1
  58. package/dist/bin/help.js +4 -1
  59. package/dist/bin/help.js.map +1 -1
  60. package/dist/bin/lite.js +4 -1
  61. package/dist/bin/lite.js.map +1 -1
  62. package/dist/bin/restart.js +54 -5
  63. package/dist/bin/restart.js.map +1 -1
  64. package/dist/bin/run.js +4 -10
  65. package/dist/bin/run.js.map +1 -1
  66. package/dist/bin/upgrade.js +4 -3
  67. package/dist/bin/upgrade.js.map +1 -1
  68. package/dist/bin/workloadIdentity.d.ts +18 -0
  69. package/dist/bin/workloadIdentity.js +100 -0
  70. package/dist/bin/workloadIdentity.js.map +1 -0
  71. package/dist/components/Application.d.ts +139 -16
  72. package/dist/components/Application.js +2215 -267
  73. package/dist/components/Application.js.map +1 -1
  74. package/dist/components/ApplicationScope.d.ts +8 -0
  75. package/dist/components/ApplicationScope.js +7 -0
  76. package/dist/components/ApplicationScope.js.map +1 -1
  77. package/dist/components/EntryHandler.js +26 -10
  78. package/dist/components/EntryHandler.js.map +1 -1
  79. package/dist/components/OptionsWatcher.d.ts +1 -0
  80. package/dist/components/OptionsWatcher.js +141 -74
  81. package/dist/components/OptionsWatcher.js.map +1 -1
  82. package/dist/components/RuntimeModuleTracker.js +40 -6
  83. package/dist/components/RuntimeModuleTracker.js.map +1 -1
  84. package/dist/components/Scope.js +38 -13
  85. package/dist/components/Scope.js.map +1 -1
  86. package/dist/components/awaitRestart.d.ts +33 -0
  87. package/dist/components/awaitRestart.js +61 -0
  88. package/dist/components/awaitRestart.js.map +1 -0
  89. package/dist/components/componentLoader.d.ts +38 -1
  90. package/dist/components/componentLoader.js +279 -22
  91. package/dist/components/componentLoader.js.map +1 -1
  92. package/dist/components/componentPreparationLock.d.ts +5 -0
  93. package/dist/components/componentPreparationLock.js +14 -6
  94. package/dist/components/componentPreparationLock.js.map +1 -1
  95. package/dist/components/mcp/adapters/harperHttp.js +4 -0
  96. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  97. package/dist/components/mcp/listChanged.js +5 -0
  98. package/dist/components/mcp/listChanged.js.map +1 -1
  99. package/dist/components/mcp/toolRegistry.d.ts +1 -0
  100. package/dist/components/mcp/toolRegistry.js.map +1 -1
  101. package/dist/components/mcp/tools/operations.d.ts +5 -0
  102. package/dist/components/mcp/tools/operations.js +9 -0
  103. package/dist/components/mcp/tools/operations.js.map +1 -1
  104. package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
  105. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  106. package/dist/components/operations.js +231 -77
  107. package/dist/components/operations.js.map +1 -1
  108. package/dist/components/operationsValidation.js +49 -1
  109. package/dist/components/operationsValidation.js.map +1 -1
  110. package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
  111. package/dist/components/status/ComponentStatusRegistry.js +63 -0
  112. package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
  113. package/dist/config/RootConfigWatcher.d.ts +7 -1
  114. package/dist/config/RootConfigWatcher.js +64 -27
  115. package/dist/config/RootConfigWatcher.js.map +1 -1
  116. package/dist/config/configUtils.d.ts +9 -1
  117. package/dist/config/configUtils.js +254 -33
  118. package/dist/config/configUtils.js.map +1 -1
  119. package/dist/config/harperConfigEnvVars.d.ts +16 -0
  120. package/dist/config/harperConfigEnvVars.js +162 -25
  121. package/dist/config/harperConfigEnvVars.js.map +1 -1
  122. package/dist/dataLayer/blobBackup.d.ts +49 -20
  123. package/dist/dataLayer/blobBackup.js +139 -50
  124. package/dist/dataLayer/blobBackup.js.map +1 -1
  125. package/dist/dataLayer/delete.js +1 -1
  126. package/dist/dataLayer/delete.js.map +1 -1
  127. package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
  128. package/dist/dataLayer/harperBridge/ResourceBridge.js +51 -10
  129. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  130. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
  131. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  132. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
  133. package/dist/dataLayer/hdbInfoController.d.ts +10 -0
  134. package/dist/dataLayer/hdbInfoController.js +30 -1
  135. package/dist/dataLayer/hdbInfoController.js.map +1 -1
  136. package/dist/dataLayer/insert.d.ts +9 -1
  137. package/dist/dataLayer/insert.js +30 -0
  138. package/dist/dataLayer/insert.js.map +1 -1
  139. package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
  140. package/dist/dataLayer/rocksdbBackup.js +45 -8
  141. package/dist/dataLayer/rocksdbBackup.js.map +1 -1
  142. package/dist/dataLayer/schema.js +8 -0
  143. package/dist/dataLayer/schema.js.map +1 -1
  144. package/dist/dataLayer/schemaDescribe.js +8 -1
  145. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  146. package/dist/index.d.ts +1 -1
  147. package/dist/index.js.map +1 -1
  148. package/dist/json/systemSchema.json +52 -0
  149. package/dist/resources/DatabaseTransaction.d.ts +55 -3
  150. package/dist/resources/DatabaseTransaction.js +460 -134
  151. package/dist/resources/DatabaseTransaction.js.map +1 -1
  152. package/dist/resources/LMDBTransaction.js +22 -4
  153. package/dist/resources/LMDBTransaction.js.map +1 -1
  154. package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
  155. package/dist/resources/PrimaryRocksDatabase.js +30 -2
  156. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  157. package/dist/resources/RecordEncoder.d.ts +20 -0
  158. package/dist/resources/RecordEncoder.js +94 -9
  159. package/dist/resources/RecordEncoder.js.map +1 -1
  160. package/dist/resources/RequestTarget.d.ts +2 -0
  161. package/dist/resources/RequestTarget.js.map +1 -1
  162. package/dist/resources/Resource.js +20 -11
  163. package/dist/resources/Resource.js.map +1 -1
  164. package/dist/resources/ResourceInterface.d.ts +20 -1
  165. package/dist/resources/ResourceInterface.js.map +1 -1
  166. package/dist/resources/RocksIndexStore.d.ts +6 -1
  167. package/dist/resources/RocksIndexStore.js +24 -10
  168. package/dist/resources/RocksIndexStore.js.map +1 -1
  169. package/dist/resources/RocksTransactionLogStore.d.ts +14 -1
  170. package/dist/resources/RocksTransactionLogStore.js +57 -17
  171. package/dist/resources/RocksTransactionLogStore.js.map +1 -1
  172. package/dist/resources/Table.d.ts +112 -8
  173. package/dist/resources/Table.js +1071 -197
  174. package/dist/resources/Table.js.map +1 -1
  175. package/dist/resources/auditStore.d.ts +11 -2
  176. package/dist/resources/auditStore.js +183 -18
  177. package/dist/resources/auditStore.js.map +1 -1
  178. package/dist/resources/blob.d.ts +129 -9
  179. package/dist/resources/blob.js +992 -114
  180. package/dist/resources/blob.js.map +1 -1
  181. package/dist/resources/branchDatabase.d.ts +48 -0
  182. package/dist/resources/branchDatabase.js +892 -0
  183. package/dist/resources/branchDatabase.js.map +1 -0
  184. package/dist/resources/crdt.js +50 -12
  185. package/dist/resources/crdt.js.map +1 -1
  186. package/dist/resources/databases.d.ts +150 -9
  187. package/dist/resources/databases.js +1247 -525
  188. package/dist/resources/databases.js.map +1 -1
  189. package/dist/resources/defineTable.d.ts +10 -2
  190. package/dist/resources/defineTable.js +9 -1
  191. package/dist/resources/defineTable.js.map +1 -1
  192. package/dist/resources/graphql.d.ts +1 -1
  193. package/dist/resources/graphql.js +50 -15
  194. package/dist/resources/graphql.js.map +1 -1
  195. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +1 -0
  196. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +6 -2
  197. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  198. package/dist/resources/longLivedTransactions.d.ts +71 -0
  199. package/dist/resources/longLivedTransactions.js +358 -0
  200. package/dist/resources/longLivedTransactions.js.map +1 -0
  201. package/dist/resources/models/backendRegistry.d.ts +26 -0
  202. package/dist/resources/models/backendRegistry.js +60 -2
  203. package/dist/resources/models/backendRegistry.js.map +1 -1
  204. package/dist/resources/models/bootstrap.d.ts +33 -1
  205. package/dist/resources/models/bootstrap.js +416 -31
  206. package/dist/resources/models/bootstrap.js.map +1 -1
  207. package/dist/resources/recordLock.d.ts +80 -0
  208. package/dist/resources/recordLock.js +184 -0
  209. package/dist/resources/recordLock.js.map +1 -0
  210. package/dist/resources/replayLogs.d.ts +14 -1
  211. package/dist/resources/replayLogs.js +152 -24
  212. package/dist/resources/replayLogs.js.map +1 -1
  213. package/dist/resources/replayLogsGuards.d.ts +94 -7
  214. package/dist/resources/replayLogsGuards.js +111 -7
  215. package/dist/resources/replayLogsGuards.js.map +1 -1
  216. package/dist/resources/search.js +158 -32
  217. package/dist/resources/search.js.map +1 -1
  218. package/dist/resources/transactionBroadcast.d.ts +1 -1
  219. package/dist/resources/transactionBroadcast.js +2 -2
  220. package/dist/resources/transactionBroadcast.js.map +1 -1
  221. package/dist/security/auth.js +61 -30
  222. package/dist/security/auth.js.map +1 -1
  223. package/dist/security/authn/oidc/claims.d.ts +22 -0
  224. package/dist/security/authn/oidc/claims.js +71 -0
  225. package/dist/security/authn/oidc/claims.js.map +1 -0
  226. package/dist/security/authn/oidc/identityToken.d.ts +27 -0
  227. package/dist/security/authn/oidc/identityToken.js +111 -0
  228. package/dist/security/authn/oidc/identityToken.js.map +1 -0
  229. package/dist/security/authn/oidc/jwks.d.ts +25 -0
  230. package/dist/security/authn/oidc/jwks.js +261 -0
  231. package/dist/security/authn/oidc/jwks.js.map +1 -0
  232. package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
  233. package/dist/security/authn/oidc/providers/generic.js +34 -0
  234. package/dist/security/authn/oidc/providers/generic.js.map +1 -0
  235. package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
  236. package/dist/security/authn/oidc/providers/githubActions.js +129 -0
  237. package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
  238. package/dist/security/authn/oidc/providers/index.d.ts +37 -0
  239. package/dist/security/authn/oidc/providers/index.js +24 -0
  240. package/dist/security/authn/oidc/providers/index.js.map +1 -0
  241. package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
  242. package/dist/security/authn/oidc/tokenExchange.js +306 -0
  243. package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
  244. package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
  245. package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
  246. package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
  247. package/dist/security/authn/oidc/types.d.ts +38 -0
  248. package/dist/security/authn/oidc/types.js +6 -0
  249. package/dist/security/authn/oidc/types.js.map +1 -0
  250. package/dist/security/certificateVerification/index.js +40 -11
  251. package/dist/security/certificateVerification/index.js.map +1 -1
  252. package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
  253. package/dist/security/certificateVerification/trustedIssuers.js +79 -0
  254. package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
  255. package/dist/security/certificateVerification/types.d.ts +1 -0
  256. package/dist/security/credentialProvenance.d.ts +35 -0
  257. package/dist/security/credentialProvenance.js +51 -0
  258. package/dist/security/credentialProvenance.js.map +1 -0
  259. package/dist/security/credentialRejection.d.ts +4 -0
  260. package/dist/security/credentialRejection.js +24 -0
  261. package/dist/security/credentialRejection.js.map +1 -0
  262. package/dist/security/deferredAuthentication.d.ts +36 -0
  263. package/dist/security/deferredAuthentication.js +70 -0
  264. package/dist/security/deferredAuthentication.js.map +1 -0
  265. package/dist/security/impersonation.d.ts +21 -0
  266. package/dist/security/impersonation.js +108 -9
  267. package/dist/security/impersonation.js.map +1 -1
  268. package/dist/security/jsLoader.d.ts +6 -0
  269. package/dist/security/jsLoader.js +75 -15
  270. package/dist/security/jsLoader.js.map +1 -1
  271. package/dist/security/keys.js +301 -71
  272. package/dist/security/keys.js.map +1 -1
  273. package/dist/security/operationScope.d.ts +21 -0
  274. package/dist/security/operationScope.js +36 -0
  275. package/dist/security/operationScope.js.map +1 -0
  276. package/dist/security/permissionsTranslator.js +21 -0
  277. package/dist/security/permissionsTranslator.js.map +1 -1
  278. package/dist/security/tokenAuthentication.d.ts +19 -1
  279. package/dist/security/tokenAuthentication.js +191 -10
  280. package/dist/security/tokenAuthentication.js.map +1 -1
  281. package/dist/security/user.js +4 -3
  282. package/dist/security/user.js.map +1 -1
  283. package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
  284. package/dist/server/DurableSubscriptionsSession.js +67 -10
  285. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  286. package/dist/server/REST.js +73 -0
  287. package/dist/server/REST.js.map +1 -1
  288. package/dist/server/graphqlQuerying.js +4 -0
  289. package/dist/server/graphqlQuerying.js.map +1 -1
  290. package/dist/server/http.d.ts +11 -0
  291. package/dist/server/http.js +88 -15
  292. package/dist/server/http.js.map +1 -1
  293. package/dist/server/itc/serverHandlers.js +8 -1
  294. package/dist/server/itc/serverHandlers.js.map +1 -1
  295. package/dist/server/jobs/jobProcess.js +6 -2
  296. package/dist/server/jobs/jobProcess.js.map +1 -1
  297. package/dist/server/jobs/jobs.js +4 -1
  298. package/dist/server/jobs/jobs.js.map +1 -1
  299. package/dist/server/liveSubscriptionAuth.d.ts +26 -4
  300. package/dist/server/liveSubscriptionAuth.js +105 -39
  301. package/dist/server/liveSubscriptionAuth.js.map +1 -1
  302. package/dist/server/loadRootComponents.js +49 -10
  303. package/dist/server/loadRootComponents.js.map +1 -1
  304. package/dist/server/mqtt.d.ts +2 -0
  305. package/dist/server/mqtt.js +165 -30
  306. package/dist/server/mqtt.js.map +1 -1
  307. package/dist/server/nodeName.d.ts +2 -0
  308. package/dist/server/nodeName.js +107 -23
  309. package/dist/server/nodeName.js.map +1 -1
  310. package/dist/server/serverHelpers/Headers.d.ts +25 -0
  311. package/dist/server/serverHelpers/Headers.js +139 -1
  312. package/dist/server/serverHelpers/Headers.js.map +1 -1
  313. package/dist/server/serverHelpers/contentTypes.d.ts +9 -0
  314. package/dist/server/serverHelpers/contentTypes.js +32 -25
  315. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  316. package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
  317. package/dist/server/serverHelpers/deployValidationState.js +9 -19
  318. package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
  319. package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
  320. package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
  321. package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
  322. package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
  323. package/dist/server/serverHelpers/registeredOperations.js +74 -21
  324. package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
  325. package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
  326. package/dist/server/serverHelpers/requestSanitization.js +20 -0
  327. package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
  328. package/dist/server/serverHelpers/serverHandlers.js +6 -3
  329. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  330. package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
  331. package/dist/server/serverHelpers/serverUtilities.js +90 -20
  332. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  333. package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
  334. package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
  335. package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
  336. package/dist/server/serverHelpers/uwsServer.js +19 -1
  337. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  338. package/dist/server/static.js +24 -28
  339. package/dist/server/static.js.map +1 -1
  340. package/dist/server/storageReclamation.d.ts +5 -0
  341. package/dist/server/storageReclamation.js +16 -0
  342. package/dist/server/storageReclamation.js.map +1 -1
  343. package/dist/server/threads/itc.d.ts +7 -2
  344. package/dist/server/threads/itc.js +5 -1
  345. package/dist/server/threads/itc.js.map +1 -1
  346. package/dist/server/threads/manageThreads.d.ts +26 -4
  347. package/dist/server/threads/manageThreads.js +517 -72
  348. package/dist/server/threads/manageThreads.js.map +1 -1
  349. package/dist/server/threads/socketRouter.js +89 -26
  350. package/dist/server/threads/socketRouter.js.map +1 -1
  351. package/dist/server/threads/threadHeapMemory.d.ts +2 -0
  352. package/dist/server/threads/threadHeapMemory.js +31 -0
  353. package/dist/server/threads/threadHeapMemory.js.map +1 -0
  354. package/dist/server/threads/threadServer.js +46 -15
  355. package/dist/server/threads/threadServer.js.map +1 -1
  356. package/dist/sqlEngine/config.d.ts +1 -3
  357. package/dist/sqlEngine/config.js +19 -16
  358. package/dist/sqlEngine/config.js.map +1 -1
  359. package/dist/sqlTranslator/index.d.ts +1 -1
  360. package/dist/sqlTranslator/index.js +30 -7
  361. package/dist/sqlTranslator/index.js.map +1 -1
  362. package/dist/upgrade/directives/5-3-0.d.ts +7 -0
  363. package/dist/upgrade/directives/5-3-0.js +148 -0
  364. package/dist/upgrade/directives/5-3-0.js.map +1 -0
  365. package/dist/upgrade/directives/directivesController.js +2 -1
  366. package/dist/upgrade/directives/directivesController.js.map +1 -1
  367. package/dist/utility/OperationFunctionCaller.js +2 -1
  368. package/dist/utility/OperationFunctionCaller.js.map +1 -1
  369. package/dist/utility/common_utils.d.ts +16 -0
  370. package/dist/utility/common_utils.js +32 -6
  371. package/dist/utility/common_utils.js.map +1 -1
  372. package/dist/utility/componentNames.d.ts +8 -0
  373. package/dist/utility/componentNames.js +12 -1
  374. package/dist/utility/componentNames.js.map +1 -1
  375. package/dist/utility/environment/environmentManager.js +3 -6
  376. package/dist/utility/environment/environmentManager.js.map +1 -1
  377. package/dist/utility/environment/systemInformation.d.ts +1 -0
  378. package/dist/utility/environment/systemInformation.js +1 -0
  379. package/dist/utility/environment/systemInformation.js.map +1 -1
  380. package/dist/utility/errors/commonErrors.d.ts +2 -0
  381. package/dist/utility/errors/commonErrors.js +2 -0
  382. package/dist/utility/errors/commonErrors.js.map +1 -1
  383. package/dist/utility/errors/hdbError.d.ts +17 -0
  384. package/dist/utility/errors/hdbError.js +30 -1
  385. package/dist/utility/errors/hdbError.js.map +1 -1
  386. package/dist/utility/globalSchema.d.ts +18 -0
  387. package/dist/utility/hdbTerms.d.ts +16 -0
  388. package/dist/utility/hdbTerms.js +18 -2
  389. package/dist/utility/hdbTerms.js.map +1 -1
  390. package/dist/utility/logging/harper_logger.d.ts +2 -0
  391. package/dist/utility/logging/harper_logger.js +91 -14
  392. package/dist/utility/logging/harper_logger.js.map +1 -1
  393. package/dist/utility/logging/logRotator.js +76 -49
  394. package/dist/utility/logging/logRotator.js.map +1 -1
  395. package/dist/utility/nodeIdentity.d.ts +9 -0
  396. package/dist/utility/nodeIdentity.js +58 -0
  397. package/dist/utility/nodeIdentity.js.map +1 -0
  398. package/dist/utility/npmUtilities.js +9 -7
  399. package/dist/utility/npmUtilities.js.map +1 -1
  400. package/dist/utility/operationPermissions.d.ts +3 -1
  401. package/dist/utility/operationPermissions.js +16 -1
  402. package/dist/utility/operationPermissions.js.map +1 -1
  403. package/dist/utility/operation_authorization.d.ts +10 -7
  404. package/dist/utility/operation_authorization.js +212 -41
  405. package/dist/utility/operation_authorization.js.map +1 -1
  406. package/dist/utility/watchPath.d.ts +29 -0
  407. package/dist/utility/watchPath.js +68 -0
  408. package/dist/utility/watchPath.js.map +1 -0
  409. package/dist/utility/watcherFallback.d.ts +86 -0
  410. package/dist/utility/watcherFallback.js +278 -1
  411. package/dist/utility/watcherFallback.js.map +1 -1
  412. package/dist/validation/configValidator.d.ts +12 -0
  413. package/dist/validation/configValidator.js +199 -72
  414. package/dist/validation/configValidator.js.map +1 -1
  415. package/dist/validation/installValidator.js +12 -0
  416. package/dist/validation/installValidator.js.map +1 -1
  417. package/dist/validation/validationWrapper.d.ts +11 -0
  418. package/dist/validation/validationWrapper.js +16 -3
  419. package/dist/validation/validationWrapper.js.map +1 -1
  420. package/index.ts +2 -0
  421. package/json/systemSchema.json +52 -0
  422. package/npm-shrinkwrap.json +235 -233
  423. package/package.json +8 -7
  424. package/resources/DESIGN.md +114 -52
  425. package/resources/DatabaseTransaction.ts +522 -130
  426. package/resources/LMDBTransaction.ts +26 -4
  427. package/resources/PrimaryRocksDatabase.ts +29 -3
  428. package/resources/RecordEncoder.ts +103 -8
  429. package/resources/RequestTarget.ts +2 -0
  430. package/resources/Resource.ts +17 -9
  431. package/resources/ResourceInterface.ts +23 -0
  432. package/resources/RocksIndexStore.ts +30 -10
  433. package/resources/RocksTransactionLogStore.ts +78 -21
  434. package/resources/Table.ts +1107 -164
  435. package/resources/auditStore.ts +187 -21
  436. package/resources/blob.ts +1029 -110
  437. package/resources/branchDatabase.ts +941 -0
  438. package/resources/crdt.ts +76 -12
  439. package/resources/databases.ts +1354 -522
  440. package/resources/defineTable.ts +18 -2
  441. package/resources/graphql.ts +70 -16
  442. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +6 -2
  443. package/resources/longLivedTransactions.ts +360 -0
  444. package/resources/models/backendRegistry.ts +84 -2
  445. package/resources/models/bootstrap.ts +473 -28
  446. package/resources/recordLock.ts +253 -0
  447. package/resources/replayLogs.ts +147 -26
  448. package/resources/replayLogsGuards.ts +171 -8
  449. package/resources/search.ts +154 -32
  450. package/resources/transactionBroadcast.ts +3 -3
  451. package/security/auth.ts +68 -29
  452. package/security/authn/oidc/claims.ts +72 -0
  453. package/security/authn/oidc/identityToken.ts +129 -0
  454. package/security/authn/oidc/jwks.ts +260 -0
  455. package/security/authn/oidc/providers/generic.ts +40 -0
  456. package/security/authn/oidc/providers/githubActions.ts +137 -0
  457. package/security/authn/oidc/providers/index.ts +52 -0
  458. package/security/authn/oidc/tokenExchange.ts +300 -0
  459. package/security/authn/oidc/trustPolicyOperations.ts +343 -0
  460. package/security/authn/oidc/types.ts +41 -0
  461. package/security/certificateVerification/index.ts +54 -13
  462. package/security/certificateVerification/trustedIssuers.ts +76 -0
  463. package/security/certificateVerification/types.ts +1 -0
  464. package/security/credentialProvenance.ts +47 -0
  465. package/security/credentialRejection.ts +22 -0
  466. package/security/deferredAuthentication.ts +71 -0
  467. package/security/impersonation.ts +117 -12
  468. package/security/jsLoader.ts +81 -18
  469. package/security/keys.ts +298 -72
  470. package/security/operationScope.ts +33 -0
  471. package/security/permissionsTranslator.js +23 -0
  472. package/security/tokenAuthentication.ts +233 -12
  473. package/security/user.ts +4 -3
  474. package/server/DESIGN.md +183 -16
  475. package/server/DurableSubscriptionsSession.ts +71 -11
  476. package/server/REST.ts +79 -1
  477. package/server/graphqlQuerying.ts +4 -0
  478. package/server/http.ts +99 -18
  479. package/server/itc/serverHandlers.js +8 -1
  480. package/server/jobs/jobProcess.ts +8 -2
  481. package/server/jobs/jobs.ts +4 -1
  482. package/server/liveSubscriptionAuth.ts +129 -46
  483. package/server/loadRootComponents.js +50 -8
  484. package/server/mqtt.ts +179 -38
  485. package/server/nodeName.ts +103 -21
  486. package/server/serverHelpers/Headers.ts +131 -0
  487. package/server/serverHelpers/contentTypes.ts +29 -21
  488. package/server/serverHelpers/deployValidationState.ts +24 -13
  489. package/server/serverHelpers/operationAuthorizationState.ts +34 -3
  490. package/server/serverHelpers/registeredOperations.ts +79 -22
  491. package/server/serverHelpers/requestSanitization.ts +15 -0
  492. package/server/serverHelpers/serverHandlers.js +6 -3
  493. package/server/serverHelpers/serverUtilities.ts +104 -31
  494. package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
  495. package/server/serverHelpers/uwsServer.ts +17 -2
  496. package/server/static.ts +23 -29
  497. package/server/storageReclamation.ts +13 -0
  498. package/server/threads/itc.js +11 -1
  499. package/server/threads/manageThreads.js +526 -63
  500. package/server/threads/socketRouter.ts +81 -26
  501. package/server/threads/threadHeapMemory.ts +26 -0
  502. package/server/threads/threadServer.js +43 -15
  503. package/sqlTranslator/index.ts +31 -8
  504. package/studio/web/assets/{Chat-Br06zdMA.js → Chat-BnCBegQz.js} +1 -1
  505. package/studio/web/assets/{FloatingChat-BWImX5fA.js → FloatingChat-CoDW1ySS.js} +3 -3
  506. package/studio/web/assets/{apiToken-DN0nmDsq.js → apiToken-Bwk5BLXW.js} +1 -1
  507. package/studio/web/assets/{applications-kSxVoyeU.js → applications-DHxGi7JH.js} +1 -1
  508. package/studio/web/assets/{cssMode-C1JeufH5.js → cssMode-s0cWI-_M.js} +1 -1
  509. package/studio/web/assets/{editor-qoo9CrGO.js → editor-DNcRHK54.js} +1 -1
  510. package/studio/web/assets/{html-Dt4IIy04.js → html-Bdssedlg.js} +1 -1
  511. package/studio/web/assets/{htmlMode-DXgKKr4C.js → htmlMode-CoDlJ3fw.js} +1 -1
  512. package/studio/web/assets/{index-6onkYFOG.js → index-D6sxmFLR.js} +5 -5
  513. package/studio/web/assets/{index.lazy-BrCFnpNJ.js → index.lazy-tmU5BS8s.js} +1 -1
  514. package/studio/web/assets/{javascript-DNCQGUBc.js → javascript-B8meVSTH.js} +1 -1
  515. package/studio/web/assets/{jsonMode-CR6HWruP.js → jsonMode-DpIPd35T.js} +1 -1
  516. package/studio/web/assets/{languageServices-BM4fI4rS.js → languageServices-C_5FMJzQ.js} +1 -1
  517. package/studio/web/assets/{lspLanguageFeatures-DSa1ttcD.js → lspLanguageFeatures-BIzNBzPK.js} +1 -1
  518. package/studio/web/assets/{notifications-BHXLnh6x.js → notifications-CQf18QKb.js} +1 -1
  519. package/studio/web/assets/{notifications-CUmtIA6z.js → notifications-CvZivSbh.js} +1 -1
  520. package/studio/web/assets/{profile-8BeFSF3j.js → profile-DdOwtntb.js} +1 -1
  521. package/studio/web/assets/{regions-C8qR0HhD.js → regions-n69fwagr.js} +1 -1
  522. package/studio/web/assets/{register-B4n5i0SD.js → register-PfWTCXWB.js} +2 -2
  523. package/studio/web/assets/{setComponentFile-g0_B0lgX.js → setComponentFile-Bg6O7X0S.js} +1 -1
  524. package/studio/web/assets/{setup-B0CTj_Q5.js → setup-CUx_aUDl.js} +2 -2
  525. package/studio/web/assets/{status-C6Yib7-K.js → status-D7BVKqX9.js} +1 -1
  526. package/studio/web/assets/{toggleHighContrast-Dgta7bVi.js → toggleHighContrast-DBSyXzMr.js} +1 -1
  527. package/studio/web/assets/{tsMode-CH_jHvU-.js → tsMode-BByKCjBS.js} +1 -1
  528. package/studio/web/assets/{typescript-Co9LCXd5.js → typescript-DDLnLpw9.js} +1 -1
  529. package/studio/web/assets/{useEntityRestURL-wKC8NsC_.js → useEntityRestURL-31CHGaHk.js} +1 -1
  530. package/studio/web/assets/{workers-CWeLxCXA.js → workers-pR3jRY9D.js} +1 -1
  531. package/studio/web/assets/{xml-BadC-0Rk.js → xml-2iRnMhQO.js} +1 -1
  532. package/studio/web/assets/{yaml-BiUfxPbC.js → yaml-Bf92gJpd.js} +1 -1
  533. package/studio/web/index.html +1 -1
  534. package/upgrade/directives/5-3-0.ts +132 -0
  535. package/upgrade/directives/directivesController.ts +2 -1
  536. package/utility/OperationFunctionCaller.ts +2 -1
  537. package/utility/common_utils.ts +30 -5
  538. package/utility/componentNames.ts +12 -0
  539. package/utility/environment/environmentManager.ts +3 -7
  540. package/utility/environment/systemInformation.ts +7 -0
  541. package/utility/errors/commonErrors.ts +4 -0
  542. package/utility/errors/hdbError.ts +29 -0
  543. package/utility/hdbTerms.ts +17 -0
  544. package/utility/logging/harper_logger.ts +87 -14
  545. package/utility/logging/logRotator.ts +72 -45
  546. package/utility/nodeIdentity.ts +45 -0
  547. package/utility/npmUtilities.ts +10 -8
  548. package/utility/operationPermissions.ts +18 -1
  549. package/utility/operation_authorization.ts +231 -42
  550. package/utility/watchPath.ts +63 -0
  551. package/utility/watcherFallback.ts +270 -0
  552. package/validation/configValidator.ts +209 -72
  553. package/validation/installValidator.ts +15 -0
  554. package/validation/validationWrapper.ts +18 -4
package/server/http.ts CHANGED
@@ -18,7 +18,13 @@ import { createServer as createSecureServerHttp1 } from 'node:https';
18
18
  import { createServer, IncomingMessage, validateHeaderName, validateHeaderValue } from 'node:http';
19
19
  import { createServer as createNetServer } from 'node:net';
20
20
  import { Request, BunRequest, UwsRequest, isBun } from './serverHelpers/Request.ts';
21
- import { appendHeader, Headers, toWriteHeadHeaders } from './serverHelpers/Headers.ts';
21
+ import {
22
+ appendHeader,
23
+ bridgeChainHeadersToNodeResponse,
24
+ Headers,
25
+ mergeChainHeadersIntoFallback,
26
+ toWriteHeadHeaders,
27
+ } from './serverHelpers/Headers.ts';
22
28
  import {
23
29
  decodeProxyHeader,
24
30
  applyProxyHeader,
@@ -34,7 +40,7 @@ import { server, type ServerOptions, type HttpOptions, type UpgradeOptions, Upgr
34
40
  import { setPortServerMap, SERVERS, socketOptionDefaults } from './serverRegistry.ts';
35
41
  import { getComponentName } from '../components/componentLoader.ts';
36
42
  import { throttle } from './throttle.ts';
37
- import { makeCallbackChain as buildCallbackChain, describeChains } from './middlewareChain.ts';
43
+ import { makeCallbackChain as buildCallbackChain, describeChains, type HttpEntry } from './middlewareChain.ts';
38
44
  import { WebSocketServer } from 'ws';
39
45
 
40
46
  const { errorToString, errorForLog } = harperLogger;
@@ -490,7 +496,7 @@ export function httpServer(listener, options) {
490
496
  httpResponders[options?.runFirst ? 'unshift' : 'push'](entry);
491
497
  } else if (isBun) {
492
498
  // On Bun, store non-function listeners (e.g. Fastify's http.Server) for fallback delegation
493
- fallbackServers[port] = listener;
499
+ registerFallbackServer(port, listener);
494
500
  } else if ((httpServers[port] as any)?.uws) {
495
501
  // uWS HTTP path (#914, HARPER_UWS_HTTP): the port is backed by uWebSockets.js, not a Node
496
502
  // http server, so a raw non-function listener (e.g. Fastify's http.Server via
@@ -499,12 +505,12 @@ export function httpServer(listener, options) {
499
505
  // port. Divert it to the fallback map like the Bun path; makeUwsHandler delegates unhandled
500
506
  // requests to it via inject(). The { uws: true } marker is guaranteed present here: the
501
507
  // getServer(port) call above (same loop iteration) sets it before this branch runs.
502
- fallbackServers[port] = listener;
508
+ registerFallbackServer(port, listener);
503
509
  } else {
504
510
  listener.isSecure = secure;
505
511
  registerServer(listener, port, false);
506
512
  }
507
- httpChain[port] = makeCallbackChain(httpResponders, port);
513
+ buildChains(httpChain, httpResponders, port);
508
514
  }
509
515
 
510
516
  return servers;
@@ -543,10 +549,47 @@ export function pipeBodyToResponse(
543
549
  } else {
544
550
  recordAction(performance.now() - endTime, 'transfer', handlerPath, method);
545
551
  recordAction(bytesSent, 'bytes-sent', handlerPath, method);
552
+ endConnectionIfClientExpectsClose(nodeResponse);
546
553
  }
547
554
  });
548
555
  }
549
556
 
557
+ /**
558
+ * Bun's node:http never derives keep-alive from the request, so a stream-ended response delivers its
559
+ * full body and then leaves the client attached until its own timeout (#2210). Ending the *request's*
560
+ * socket is the only remedy that works there, and it is graceful, so nothing is truncated (measured
561
+ * on plain TCP and TLS). DESIGN.md carries the measurements and the alternatives that don't work.
562
+ */
563
+ function endConnectionIfClientExpectsClose(nodeResponse: any) {
564
+ if (!isBun) return;
565
+ const nodeRequest = nodeResponse.req;
566
+ if (nodeRequest?.httpVersionMajor !== 1) return;
567
+ const socket = nodeRequest.socket;
568
+ // a peer that RSTs right after the terminal chunk can leave this callback holding a destroyed
569
+ // socket; end()ing it would throw from inside stream internals, i.e. an uncaughtException
570
+ if (!socket || socket.destroyed) return;
571
+ const connection = nodeRequest.headers?.connection;
572
+ const tokens = typeof connection === 'string' ? connection.split(',') : [];
573
+ if (hasConnectionToken(tokens, 'close')) {
574
+ socket.end();
575
+ return;
576
+ }
577
+ // HTTP/1.0 persistence needs an explicit keep-alive AND a length to read to, so a 1.0 response
578
+ // that got no Content-Length is delimited by the close — the same line Node draws
579
+ if (
580
+ nodeRequest.httpVersionMinor === 0 &&
581
+ !(hasConnectionToken(tokens, 'keep-alive') && nodeResponse.hasHeader?.('content-length'))
582
+ )
583
+ socket.end();
584
+ }
585
+
586
+ function hasConnectionToken(tokens: string[], wanted: string) {
587
+ for (const token of tokens) {
588
+ if (token.trim().toLowerCase() === wanted) return true;
589
+ }
590
+ return false;
591
+ }
592
+
550
593
  function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
551
594
  const { mtls: isMtls, usageType } = options || {};
552
595
  const isOperationsServer = usageType === 'operations-api';
@@ -642,9 +685,7 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
642
685
  // This means the HDB stack didn't handle the request, and we can then cascade the request
643
686
  // to the server-level handler, forming the bridge to the slower legacy fastify framework that expects
644
687
  // to interact with a node HTTP server object.
645
- for (const headerPair of response.headers || []) {
646
- nodeResponse.setHeader(headerPair[0], headerPair[1]);
647
- }
688
+ bridgeChainHeadersToNodeResponse(response.headers, nodeResponse);
648
689
  nodeRequest.baseRequest = request;
649
690
  nodeResponse.baseResponse = response;
650
691
  return httpServers[port].emit('unhandled', nodeRequest, nodeResponse);
@@ -947,7 +988,7 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
947
988
  * and a Fastify fallback is registered for the port, it delegates via inject() (see injectToFastify),
948
989
  * mirroring the Bun path — so legacy Fastify routes work behind uWS too.
949
990
  */
950
- function makeUwsHandler(port: number | string, isOperationsServer: boolean, requestQueueLimit?: number) {
991
+ export function makeUwsHandler(port: number | string, isOperationsServer: boolean, requestQueueLimit?: number) {
951
992
  // Build a fresh response descriptor rather than mutating what the chain returned: a handler may
952
993
  // return a WHATWG `Response` (read-only `status`/`body` accessors), which the Bun path also never
953
994
  // mutates. `headers` is normalized in place the same way the Bun path does.
@@ -983,6 +1024,7 @@ function makeUwsHandler(port: number | string, isOperationsServer: boolean, requ
983
1024
  if (Array.isArray(v)) respHeaders.set(k, k.toLowerCase() === 'set-cookie' ? v : v.join(', '));
984
1025
  else respHeaders.set(k, String(v));
985
1026
  }
1027
+ mergeChainHeadersIntoFallback(headers, respHeaders);
986
1028
  if (universalHeaders.length > 0) applyUniversalHeaders(respHeaders);
987
1029
  logHttpRequest(request, injectResult.statusCode, requestId, performance.now() - startTime);
988
1030
  const responseStream = injectResult.stream();
@@ -1006,6 +1048,7 @@ function makeUwsHandler(port: number | string, isOperationsServer: boolean, requ
1006
1048
  }
1007
1049
  logHttpRequest(request, 404, requestId, performance.now() - startTime);
1008
1050
  const notFoundHeaders = new Headers({ 'content-type': 'text/plain' });
1051
+ mergeChainHeadersIntoFallback(headers, notFoundHeaders);
1009
1052
  if (universalHeaders.length > 0) applyUniversalHeaders(notFoundHeaders);
1010
1053
  return { status: 404, headers: notFoundHeaders, body: 'Not found\n' };
1011
1054
  }
@@ -1180,10 +1223,11 @@ function getBunHTTPServer(port: number, secure: boolean, options: ServerOptions)
1180
1223
  // Delegate to the fallback server (e.g. Fastify) via node:http compatibility.
1181
1224
  // We create a Node-compatible IncomingMessage/ServerResponse and emit 'request'
1182
1225
  // on the fallback server, then capture the response.
1183
- return await bunDelegateToNodeServer(fallbackServer, webRequest, request);
1226
+ return await bunDelegateToNodeServer(fallbackServer, webRequest, request, response.headers);
1184
1227
  }
1185
1228
  logHttpRequest(request, 404, requestId, performance.now() - startTime);
1186
1229
  const notFoundHeaders = new globalThis.Headers();
1230
+ mergeChainHeadersIntoFallback(response.headers, notFoundHeaders);
1187
1231
  if (universalHeaders.length > 0) applyUniversalHeaders(notFoundHeaders);
1188
1232
  return new Response('Not found\n', { status: 404, headers: notFoundHeaders });
1189
1233
  }
@@ -1367,6 +1411,10 @@ let fastifyInstances: Record<string | number, any> = {};
1367
1411
  export function registerFastifyInstance(port: string | number, instance: any) {
1368
1412
  fastifyInstances[port] = instance;
1369
1413
  }
1414
+
1415
+ export function registerFallbackServer(port: string | number, listener: any) {
1416
+ fallbackServers[port] = listener;
1417
+ }
1370
1418
  const INTERNAL_USER_HEADER = 'x-harper-internal-pre-auth-user';
1371
1419
 
1372
1420
  /**
@@ -1397,10 +1445,11 @@ function injectToFastify(
1397
1445
  return fastify.inject({ method: req.method, url: req.url, headers, payload: req.body, payloadAsStream: true });
1398
1446
  }
1399
1447
 
1400
- async function bunDelegateToNodeServer(
1448
+ export async function bunDelegateToNodeServer(
1401
1449
  nodeServer: any,
1402
1450
  webRequest: globalThis.Request,
1403
- bunRequest?: any
1451
+ bunRequest?: any,
1452
+ chainHeaders?: any
1404
1453
  ): Promise<Response> {
1405
1454
  // Check if there's a Fastify instance registered for this port (preferred path)
1406
1455
  for (const port in fallbackServers) {
@@ -1421,13 +1470,19 @@ async function bunDelegateToNodeServer(
1421
1470
  });
1422
1471
  const webHeaders = new globalThis.Headers();
1423
1472
  for (const [k, v] of Object.entries(injectResult.headers)) {
1424
- if (v != null) webHeaders.set(k, Array.isArray(v) ? v.join(', ') : String(v));
1473
+ if (v == null) continue;
1474
+ if (Array.isArray(v)) {
1475
+ if (k.toLowerCase() === 'set-cookie') {
1476
+ for (const single of v) webHeaders.append(k, String(single));
1477
+ } else webHeaders.set(k, v.join(', '));
1478
+ } else webHeaders.set(k, String(v));
1425
1479
  }
1426
1480
  // Propagate Connection: close so Bun closes the TCP connection after this response,
1427
1481
  // preventing stale keep-alive sockets from causing silent hangs on subsequent requests.
1428
1482
  if (webRequest.headers.get('connection')?.toLowerCase() === 'close') {
1429
1483
  webHeaders.set('connection', 'close');
1430
1484
  }
1485
+ mergeChainHeadersIntoFallback(chainHeaders, webHeaders);
1431
1486
  if (universalHeaders.length > 0) applyUniversalHeaders(webHeaders);
1432
1487
  const responseStream = injectResult.stream();
1433
1488
  // Event-stream responses (MCP SSE) must reach the client incrementally — return
@@ -1462,6 +1517,7 @@ async function bunDelegateToNodeServer(
1462
1517
  }
1463
1518
  // No Fastify instance found — return 404
1464
1519
  const notFoundHeaders = new globalThis.Headers();
1520
+ mergeChainHeadersIntoFallback(chainHeaders, notFoundHeaders);
1465
1521
  if (universalHeaders.length > 0) applyUniversalHeaders(notFoundHeaders);
1466
1522
  return new Response('Not found\n', { status: 404, headers: notFoundHeaders });
1467
1523
  }
@@ -1470,14 +1526,39 @@ type SerializedRoute = { host?: string; urlPath?: string; order: string[] };
1470
1526
  // Resolved order captured at chain-build time, keyed identically to httpChain/upgradeChains/
1471
1527
  // websocketChains (kind → port → routes). Reporting the stored build-time order rather than
1472
1528
  // recomputing from current responders guarantees get_status matches the callback chain actually
1473
- // serving that port — including cases where a late `port: 'all'` registration rebuilds only the
1474
- // 'all' chain and leaves a concrete port's chain (and this description) unchanged (#1573).
1529
+ // serving that port: buildChains() writes both in the same pass, so a description can never
1530
+ // describe an order the port isn't running (#1573).
1475
1531
  const resolvedChainDescriptions: Record<string, Record<string, SerializedRoute[]>> = {
1476
1532
  http: {},
1477
1533
  upgrade: {},
1478
1534
  websocket: {},
1479
1535
  };
1480
1536
 
1537
+ // Preserve the original port type because route selection uses strict equality.
1538
+ const builtChainPorts: Record<string, Map<string, number | string>> = {
1539
+ http: new Map(),
1540
+ upgrade: new Map(),
1541
+ websocket: new Map(),
1542
+ };
1543
+
1544
+ // A late registration on 'all' must rebuild every already-bound concrete port (#2418).
1545
+ function buildChains(
1546
+ chains: Record<string, Function>,
1547
+ listeners: HttpEntry[],
1548
+ port: number | string,
1549
+ requestArgIndex: number = 0,
1550
+ kind: string = 'http'
1551
+ ) {
1552
+ builtChainPorts[kind].set(String(port), port);
1553
+ if (port !== 'all') {
1554
+ chains[port] = makeCallbackChain(listeners, port, requestArgIndex, kind);
1555
+ return;
1556
+ }
1557
+ for (const builtPort of builtChainPorts[kind].values()) {
1558
+ chains[builtPort] = makeCallbackChain(listeners, builtPort, requestArgIndex, kind);
1559
+ }
1560
+ }
1561
+
1481
1562
  function makeCallbackChain(
1482
1563
  responders: typeof httpResponders,
1483
1564
  portNum: number | string,
@@ -1603,7 +1684,7 @@ function onUpgrade(listener: UpgradeListener, options: UpgradeOptions) {
1603
1684
  host: options?.host || undefined,
1604
1685
  };
1605
1686
  upgradeListeners[options?.runFirst ? 'unshift' : 'push'](entry);
1606
- upgradeChains[port] = makeCallbackChain(upgradeListeners, port, 0, 'upgrade');
1687
+ buildChains(upgradeChains, upgradeListeners, port, 0, 'upgrade');
1607
1688
  }
1608
1689
  }
1609
1690
 
@@ -1742,10 +1823,10 @@ function onWebSocket(listener: (ws: WebSocket) => void, options: OnWebSocketOpti
1742
1823
  host: options?.host || undefined,
1743
1824
  };
1744
1825
  websocketListeners[options?.runFirst ? 'unshift' : 'push'](wsEntry);
1745
- websocketChains[port] = makeCallbackChain(websocketListeners, port, 1, 'websocket');
1826
+ buildChains(websocketChains, websocketListeners, port, 1, 'websocket');
1746
1827
 
1747
1828
  // mqtt doesn't invoke the http handler so this needs to be here to load up the http chains.
1748
- httpChain[port] = makeCallbackChain(httpResponders, port);
1829
+ buildChains(httpChain, httpResponders, port);
1749
1830
  }
1750
1831
 
1751
1832
  return servers;
@@ -12,7 +12,7 @@ const harperBridge =
12
12
  require('../../dataLayer/harperBridge/harperBridge.ts');
13
13
  const process = require('process');
14
14
  const { isMainThread, threadId, workerData } = require('node:worker_threads');
15
- const { resetDatabases, closeDatabase } = require('../../resources/databases.ts');
15
+ const { resetDatabases, closeDatabase, reloadBranchAt } = require('../../resources/databases.ts');
16
16
 
17
17
  /**
18
18
  * This object/functions are passed to the ITC client instance and dynamically added as event handlers.
@@ -76,6 +76,13 @@ schemaHandler.addListener = function (listener) {
76
76
  */
77
77
  async function syncSchemaMetadata(msg) {
78
78
  try {
79
+ // A change to a scope-private branch is not a change to any database in the global map, so the
80
+ // rescan below has nothing to find; a thread holding that branch open reloads it instead.
81
+ if (msg.branchPath) {
82
+ // No write barrier here: the symbol-keyed put below has never been one (harper#2522).
83
+ reloadBranchAt(msg.branchPath);
84
+ return;
85
+ }
79
86
  // TODO: Eventually should indicate which database/table changed so we don't have to scan everything
80
87
  let databases = resetDatabases();
81
88
  if (msg.table && msg.database)
@@ -9,6 +9,8 @@ import harperLogger from '../../utility/logging/harper_logger.ts';
9
9
  import * as globalSchema from '../../utility/globalSchema.ts';
10
10
  import * as user from '../../security/user.ts';
11
11
  import * as serverUtils from '../serverHelpers/serverUtilities.ts';
12
+ import { runWithDispatchedOperation } from '../serverHelpers/operationAuthorizationState.ts';
13
+ import { stripSuppliedParsedSqlObject } from '../serverHelpers/requestSanitization.ts';
12
14
  import moment from 'moment';
13
15
  import * as jobs from './jobs.ts';
14
16
  import { cloneDeep } from 'lodash';
@@ -54,12 +56,16 @@ const JOB_ID = JOB_NAME.substring(4);
54
56
  throw new Error('Did not find job request in hdb_job table, unable to proceed');
55
57
  }
56
58
  request = cloneDeep(request);
59
+ // The worker re-enters from the persisted row rather than re-dispatching, so a row queued before
60
+ // the dispatch-time strip existed — or written directly — is sanitized here.
61
+ stripSuppliedParsedSqlObject(request);
57
62
 
58
63
  const operation = serverUtils.getOperationFunction(request);
59
64
  harperLogger.trace('Running operation:', request.operation, 'for job', JOB_ID);
60
65
 
61
- // Run the job operation.
62
- const results = await operation.job_operation_function(request);
66
+ const results = await runWithDispatchedOperation(request.operation, () =>
67
+ operation.job_operation_function(request)
68
+ );
63
69
  harperLogger.trace('Result from job:', JOB_ID, results);
64
70
 
65
71
  jobObj.status = hdbTerms.JOB_STATUS_ENUM.COMPLETE;
@@ -11,6 +11,7 @@ import * as search from '../../dataLayer/search.ts';
11
11
  import Search_Object from '../../dataLayer/SearchObject.ts';
12
12
  import searchByHashObj from '../../dataLayer/SearchByHashObject.ts';
13
13
  import SQL_Search_Object from '../../dataLayer/SqlSearchObject.ts';
14
+ import { runWithOperationAuthorizationBypass } from '../serverHelpers/operationAuthorizationState.ts';
14
15
  import * as hdbTerms from '../../utility/hdbTerms.ts';
15
16
  import JobObject from './JobObject.ts';
16
17
  import UpdateObject from '../../dataLayer/UpdateObject.ts';
@@ -249,7 +250,9 @@ export async function getJobsInDateRange(jsonBody: any) {
249
250
  const hdbSql = require('../../sqlTranslator/index');
250
251
  pSqlEvaluate = promisify(hdbSql.evaluateSQL);
251
252
  }
252
- return await pSqlEvaluate(sqlSearchObj);
253
+ // Harper's own statement, not the caller's. A carrier is not equivalent here — see DESIGN.md.
254
+ // Wraps only this statement: a later caller-dependent one must not inherit the bypass.
255
+ return await runWithOperationAuthorizationBypass(true, () => pSqlEvaluate(sqlSearchObj));
253
256
  } catch (e) {
254
257
  log.error(
255
258
  `there was a problem searching for jobs from date ${jsonBody.from_date} to date ${jsonBody.to_date} ${e}`
@@ -23,8 +23,24 @@ interface LiveSubscription {
23
23
  authExpiresAt?: number;
24
24
  /** Returns true if the principal is still authorized for this subscription. */
25
25
  recheck: () => Promise<boolean>;
26
- /** Stop delivery and tear down the subscription. */
27
- terminate: () => void;
26
+ /** Stop delivery and tear down. May be async (e.g. a shared-feed refcount release). */
27
+ terminate: () => void | Promise<void>;
28
+ }
29
+
30
+ function errorMessage(error: unknown): string {
31
+ try {
32
+ return error instanceof Error ? error.message : String(error);
33
+ } catch {
34
+ return '<error message unavailable>';
35
+ }
36
+ }
37
+
38
+ function safeLog(log: ((message: string) => void) | undefined, message: string): void {
39
+ try {
40
+ log?.(message);
41
+ } catch {
42
+ /* a broken logger must not turn a contained failure into a new one */
43
+ }
28
44
  }
29
45
 
30
46
  const registry = new Set<LiveSubscription>();
@@ -32,9 +48,15 @@ let sweepTimer: any = null;
32
48
  let itcListenerInstalled = false;
33
49
  let sweeping = false;
34
50
 
51
+ const NOOP_HANDLE = { unregister: () => {} };
52
+
53
+ function triggerSweep(): void {
54
+ void sweep().catch((error) => safeLog(hdbLogger.error, `liveSubscriptionAuth: sweep failed: ${errorMessage(error)}`));
55
+ }
56
+
35
57
  function ensureStarted(): void {
36
58
  if (!sweepTimer) {
37
- sweepTimer = setInterval(() => void sweep(), RECHECK_INTERVAL_MS);
59
+ sweepTimer = setInterval(triggerSweep, RECHECK_INTERVAL_MS);
38
60
  // don't keep the worker alive solely for the recheck timer
39
61
  sweepTimer.unref?.();
40
62
  }
@@ -44,7 +66,7 @@ function ensureStarted(): void {
44
66
  // user/role cache before invoking listeners, so recheck() observes the new permissions.
45
67
  const handlers = require('./itc/serverHandlers.js');
46
68
  if (handlers?.userHandler?.addListener) {
47
- handlers.userHandler.addListener(() => void sweep());
69
+ handlers.userHandler.addListener(triggerSweep);
48
70
  itcListenerInstalled = true;
49
71
  }
50
72
  } catch (error) {
@@ -61,32 +83,49 @@ function stopIfIdle(): void {
61
83
  }
62
84
 
63
85
  /**
64
- * Register a live subscription for continuous re-authorization. The returned subscription's normal
65
- * teardown (end()/close) automatically unregisters it, so callers don't need to.
86
+ * Register a live subscription for continuous re-authorization.
87
+ *
88
+ * Without `revoke`, teardown is end()/close()/emit('close') on `subscription`, and the
89
+ * subscription's own teardown unregisters the entry so callers need not.
90
+ *
91
+ * With `revoke`, that becomes the teardown and `subscription` is never touched — no `end` wrapping,
92
+ * no 'close' listener — because a feed shared by many subscribers must stay revocable per subscriber,
93
+ * so the registry can neither own the shared object nor let every registrant mutate it. A `revoke`
94
+ * caller owns the entry's lifetime: nothing detects a leaked registration, and a forgotten
95
+ * `unregister()` degrades sweep latency for every other tracked subscriber. It also owns teardown
96
+ * recovery — the entry is untracked before `revoke` runs and `revoke` is invoked exactly once, so one
97
+ * that throws, rejects or never settles is logged and never retried. A `recheck` shared across
98
+ * subscribers must not mutate state shared across them: `registerLiveSubscriptionForContext` in
99
+ * resources/Resource.ts mutates `context.user`, which is safe only while each context has exactly
100
+ * one subscriber.
66
101
  */
67
- export function registerLiveSubscription(opts: {
68
- subscription: any;
69
- username: string;
70
- authExpiresAt?: number;
71
- recheck: () => Promise<boolean>;
72
- }): void {
73
- const { subscription, username, authExpiresAt, recheck } = opts;
74
- if (!subscription || typeof subscription !== 'object' || subscription.closed) return;
102
+ export function registerLiveSubscription(
103
+ opts: {
104
+ username: string;
105
+ authExpiresAt?: number;
106
+ recheck: () => Promise<boolean>;
107
+ } & (
108
+ | { subscription: any; revoke?: undefined }
109
+ // requiring one of the two modes stops a caller that supplies neither from type-checking
110
+ // into a silent no-op registration; a revoke-only registrant may own no subscription object
111
+ | { subscription?: any; revoke: () => void | Promise<void> }
112
+ )
113
+ ): { unregister: () => void } {
114
+ const { subscription, username, authExpiresAt, recheck, revoke } = opts;
115
+ if (!revoke && (!subscription || typeof subscription !== 'object' || subscription.closed)) return NOOP_HANDLE;
75
116
 
76
117
  const entry: LiveSubscription = {
77
118
  username,
78
119
  authExpiresAt,
79
120
  recheck,
80
- terminate: () => {
81
- // end() removes the subscription from the broadcast loop and closes its iterable queue.
82
- try {
121
+ terminate:
122
+ revoke ??
123
+ (() => {
124
+ // end() removes the subscription from the broadcast loop and closes its iterable queue.
83
125
  if (subscription.end) subscription.end();
84
126
  else if (subscription.close) subscription.close();
85
127
  else subscription.emit?.('close');
86
- } catch {
87
- /* ignore */
88
- }
89
- },
128
+ }),
90
129
  };
91
130
  registry.add(entry);
92
131
 
@@ -94,51 +133,90 @@ export function registerLiveSubscription(opts: {
94
133
  registry.delete(entry);
95
134
  stopIfIdle();
96
135
  };
97
- // Both transports ultimately call end() on normal teardown (MQTT unsubscribe/disconnect; SSE close
98
- // is wired to end()); wrap it so a closed stream never leaks a registry entry. Also listen for
99
- // 'close' to cover any iterable that closes without an end().
100
- const originalEnd = typeof subscription.end === 'function' ? subscription.end.bind(subscription) : null;
101
- if (originalEnd) {
102
- subscription.end = function (...args: any[]) {
103
- unregister();
104
- return originalEnd(...args);
105
- };
136
+
137
+ if (!revoke) {
138
+ // Both transports ultimately call end() on normal teardown (MQTT unsubscribe/disconnect; SSE close
139
+ // is wired to end()); wrap it so a closed stream never leaks a registry entry. Also listen for
140
+ // 'close' to cover any iterable that closes without an end(). Skipped when `revoke` is supplied:
141
+ // the caller owns unregistration, and a subscription shared by many subscribers must not be
142
+ // mutated once per registrant.
143
+ const originalEnd = typeof subscription.end === 'function' ? subscription.end.bind(subscription) : null;
144
+ if (originalEnd) {
145
+ subscription.end = function (...args: any[]) {
146
+ unregister();
147
+ return originalEnd(...args);
148
+ };
149
+ }
150
+ subscription.on?.('close', unregister);
106
151
  }
107
- subscription.on?.('close', unregister);
108
152
 
109
153
  ensureStarted();
154
+ return { unregister };
155
+ }
156
+
157
+ /** Untrack first: a `terminate` that hangs or fails must not wedge the sweep or be re-entered by a later one. */
158
+ function terminateEntry(
159
+ entry: LiveSubscription,
160
+ reason: string,
161
+ notice: ((message: string) => void) | undefined = hdbLogger.info
162
+ ): void {
163
+ registry.delete(entry);
164
+ safeLog(notice, `liveSubscriptionAuth: revoking subscription for ${entry.username} (${reason})`);
165
+ const failed = (error: unknown) =>
166
+ safeLog(
167
+ hdbLogger.error,
168
+ `liveSubscriptionAuth: terminate failed for ${entry.username} (${reason}): ${errorMessage(error)}`
169
+ );
170
+ try {
171
+ // an async terminate's rejection would otherwise surface as an unhandled rejection on the timer's stack
172
+ Promise.resolve(entry.terminate()).catch(failed);
173
+ } catch (error) {
174
+ failed(error);
175
+ }
110
176
  }
111
177
 
112
178
  async function sweep(): Promise<void> {
113
179
  if (sweeping) return; // a slow recheck must not overlap with the next tick/event
114
180
  sweeping = true;
181
+ // the per-subscriber lines are info, which the shipped default (logging.level: warn) drops; one
182
+ // aggregate keeps the pass visible without making a mass role change a warn per subscriber
183
+ const revokedByReason = new Map<string, number>();
184
+ const countRevocation = (reason: string) => revokedByReason.set(reason, (revokedByReason.get(reason) ?? 0) + 1);
115
185
  try {
116
- const now = Date.now();
117
- for (const entry of registry) {
186
+ // snapshot bounds the pass to entries present at its start; the has() guards cover the rest
187
+ for (const entry of Array.from(registry)) {
188
+ if (!registry.has(entry)) continue;
118
189
  try {
119
- const expired = entry.authExpiresAt != null && now >= entry.authExpiresAt * 1000;
190
+ const expired = entry.authExpiresAt != null && Date.now() >= entry.authExpiresAt * 1000;
120
191
  const stillAuthorized = expired ? false : await entry.recheck();
121
- const revoke = expired || !stillAuthorized;
122
- if (revoke) {
123
- registry.delete(entry);
124
- entry.terminate();
192
+ if (!registry.has(entry)) continue;
193
+ if (expired || !stillAuthorized) {
194
+ const reason = expired ? 'token expired' : 'no longer authorized';
195
+ terminateEntry(entry, reason);
196
+ countRevocation(reason);
125
197
  }
126
198
  } catch (error) {
127
199
  // fail closed: if authorization can't be confirmed, revoke
128
- registry.delete(entry);
129
- try {
130
- entry.terminate();
131
- } catch {
132
- /* ignore */
200
+ if (registry.has(entry)) {
201
+ terminateEntry(entry, `recheck error: ${errorMessage(error)}`, hdbLogger.warn);
202
+ countRevocation('recheck error');
133
203
  }
134
- hdbLogger.warn?.(
135
- `liveSubscriptionAuth: revoked subscription for ${entry.username} after recheck error: ${(error as Error).message}`
136
- );
137
204
  }
138
205
  }
139
206
  } finally {
140
207
  sweeping = false;
141
208
  stopIfIdle();
209
+ if (revokedByReason.size > 0) {
210
+ let total = 0;
211
+ for (const count of revokedByReason.values()) total += count;
212
+ const breakdown = Array.from(revokedByReason, ([reason, count]) => `${reason}: ${count}`).join(', ');
213
+ safeLog(
214
+ hdbLogger.warn,
215
+ // "revoking", like the per-subscriber line: teardown is dispatched, not awaited, and a
216
+ // failure surfaces on its own error line
217
+ `liveSubscriptionAuth: revoking ${total} live subscription${total === 1 ? '' : 's'} (${breakdown})`
218
+ );
219
+ }
142
220
  }
143
221
  }
144
222
 
@@ -146,3 +224,8 @@ async function sweep(): Promise<void> {
146
224
  export function _liveSubscriptionCount(): number {
147
225
  return registry.size;
148
226
  }
227
+
228
+ /** Test-only: run a sweep synchronously, bypassing the interval/ITC triggers. */
229
+ export function _sweepNow(): Promise<void> {
230
+ return sweep();
231
+ }
@@ -1,19 +1,62 @@
1
1
  const { isMainThread } = require('worker_threads');
2
2
  const { getTables } = require('../resources/databases.ts');
3
- const { loadComponentDirectories, loadComponent } = require('../components/componentLoader.ts');
3
+ const { loadComponentDirectories, loadComponent, readyComponentModules } = require('../components/componentLoader.ts');
4
4
  const { resetResources } = require('../resources/Resources.ts');
5
5
  const configUtils = require('../config/configUtils.ts');
6
6
  const { dirname } = require('path');
7
7
  const { loadCertificates } = require('../security/keys.ts');
8
- const { installApplications } = require('../components/Application.ts');
8
+ const { installApplications, recoverInterruptedActivations } = require('../components/Application.ts');
9
9
  const { errorForLog } = require('../utility/logging/harper_logger.ts');
10
+ const { CONFIG_PARAMS } = require('../utility/hdbTerms.ts');
10
11
 
11
12
  let loadedComponents = new Map();
13
+
14
+ /**
15
+ * Map every component directory to the reason activation recovery could not run. Used only when the scan
16
+ * fails globally, where the safe answer is "none of these are known-good" rather than "all of these are".
17
+ */
18
+ async function failEveryComponentClosed(cause) {
19
+ const failures = new Map();
20
+ const reason = cause instanceof Error ? cause : new Error(`Activation recovery could not run: ${String(cause)}`);
21
+ try {
22
+ const { readdir } = require('node:fs/promises');
23
+ const componentsRoot = configUtils.getConfigPath(CONFIG_PARAMS.COMPONENTSROOT);
24
+ for (const entry of await readdir(componentsRoot, { withFileTypes: true })) {
25
+ if (!entry.name.startsWith('.') && (entry.isDirectory() || entry.isSymbolicLink())) {
26
+ failures.set(entry.name, reason);
27
+ }
28
+ }
29
+ } catch (error) {
30
+ console.error(errorForLog(error));
31
+ }
32
+ return failures;
33
+ }
12
34
  /**
13
35
  * This is main entry point for loading the main set of global server modules that power Harper.
14
36
  * @returns {Promise<void>}
15
37
  */
16
38
  async function loadRootComponents(isWorkerThread = false) {
39
+ // Interrupted activations are settled FIRST, before installApplications() — not merely before the
40
+ // component scan. installApplications() installs whatever the root config names, so a candidate left
41
+ // half-swapped by a crash has to be resolved before that runs, or boot reinstalls over it. Failures are
42
+ // per component and returned, not thrown: the loader fails those components closed and loads the rest.
43
+ // Undefined, not an empty Map: a worker skips this branch entirely, and handing the loader an empty map
44
+ // would assert "nothing is unreconciled" on a thread that never checked. The loader treats undefined as
45
+ // "no verdict from boot" instead.
46
+ let interruptedActivationFailures;
47
+ try {
48
+ if (isMainThread && !process.env.HARPER_SAFE_MODE) {
49
+ interruptedActivationFailures = await recoverInterruptedActivations(
50
+ configUtils.getConfigPath(CONFIG_PARAMS.COMPONENTSROOT)
51
+ );
52
+ }
53
+ } catch (error) {
54
+ // The scan itself failed, so WHICH components are unsettled is unknown — loading them all would
55
+ // defeat the fail-closed contract this pass exists for. Every component present is failed closed
56
+ // instead, and a later reload cycle retries once the cause is gone.
57
+ console.error(errorForLog(error));
58
+ interruptedActivationFailures = await failEveryComponentClosed(error);
59
+ }
17
60
  try {
18
61
  if (isMainThread && !process.env.HARPER_SAFE_MODE) await installApplications();
19
62
  } catch (error) {
@@ -33,13 +76,12 @@ async function loadRootComponents(isWorkerThread = false) {
33
76
  });
34
77
  if (!process.env.HARPER_SAFE_MODE) {
35
78
  // once the global plugins are loaded, we now load all the CF and run applications (and their components)
36
- await loadComponentDirectories(loadedComponents, resources);
37
- }
38
- let allReady = [];
39
- for (let [serverModule] of loadedComponents) {
40
- if (serverModule.ready) allReady.push(serverModule.ready());
79
+ const readyComponentPromises = new WeakMap();
80
+ await loadComponentDirectories(loadedComponents, resources, readyComponentPromises, interruptedActivationFailures);
81
+ await readyComponentModules(loadedComponents.keys(), readyComponentPromises);
82
+ return;
41
83
  }
42
- if (allReady.length > 0) await Promise.all(allReady);
84
+ await readyComponentModules(loadedComponents.keys());
43
85
  }
44
86
 
45
87
  module.exports.loadRootComponents = loadRootComponents;