harper 5.1.20 → 5.2.0-alpha.5

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 (395) hide show
  1. package/agent/operations.ts +13 -16
  2. package/components/Application.ts +188 -7
  3. package/components/ApplicationScope.ts +3 -0
  4. package/components/Scope.ts +12 -0
  5. package/components/anthropic/index.ts +9 -2
  6. package/components/bedrock/index.ts +25 -2
  7. package/components/componentLoader.ts +56 -7
  8. package/components/componentSecrets.ts +451 -0
  9. package/components/deploymentRecorder.ts +7 -1
  10. package/components/mcp/adapters/fastify.ts +3 -0
  11. package/components/mcp/adapters/harperHttp.ts +3 -0
  12. package/components/mcp/audit.ts +21 -6
  13. package/components/mcp/customResourceRegistry.ts +17 -38
  14. package/components/mcp/listChanged.ts +37 -20
  15. package/components/mcp/quota.ts +145 -0
  16. package/components/mcp/rateLimit.ts +152 -14
  17. package/components/mcp/resources.ts +2 -4
  18. package/components/mcp/toolRegistry.ts +73 -4
  19. package/components/mcp/tools/application.ts +77 -8
  20. package/components/mcp/tools/operations.ts +106 -36
  21. package/components/mcp/tools/schemas/operationDescriptions.ts +4 -4
  22. package/components/mcp/tools/schemas/operations.ts +5 -0
  23. package/components/mcp/transport.ts +55 -4
  24. package/components/ollama/index.ts +6 -2
  25. package/components/openai/index.ts +9 -2
  26. package/components/operations.js +204 -10
  27. package/components/operationsValidation.js +173 -0
  28. package/components/secretOperations.ts +498 -0
  29. package/config/configUtils.ts +25 -1
  30. package/config-app.schema.json +81 -0
  31. package/config-root.schema.json +8 -1
  32. package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
  33. package/dataLayer/readAuditLog.ts +14 -0
  34. package/dataLayer/schemaDescribe.ts +1 -0
  35. package/dist/agent/operations.d.ts +7 -4
  36. package/dist/agent/operations.js +13 -15
  37. package/dist/agent/operations.js.map +1 -1
  38. package/dist/components/Application.d.ts +25 -2
  39. package/dist/components/Application.js +166 -7
  40. package/dist/components/Application.js.map +1 -1
  41. package/dist/components/ApplicationScope.d.ts +2 -0
  42. package/dist/components/ApplicationScope.js +3 -0
  43. package/dist/components/ApplicationScope.js.map +1 -1
  44. package/dist/components/Scope.d.ts +8 -0
  45. package/dist/components/Scope.js +11 -0
  46. package/dist/components/Scope.js.map +1 -1
  47. package/dist/components/anthropic/index.d.ts +4 -1
  48. package/dist/components/anthropic/index.js +4 -2
  49. package/dist/components/anthropic/index.js.map +1 -1
  50. package/dist/components/bedrock/index.d.ts +4 -1
  51. package/dist/components/bedrock/index.js +22 -2
  52. package/dist/components/bedrock/index.js.map +1 -1
  53. package/dist/components/componentLoader.d.ts +2 -0
  54. package/dist/components/componentLoader.js +52 -10
  55. package/dist/components/componentLoader.js.map +1 -1
  56. package/dist/components/componentSecrets.d.ts +63 -0
  57. package/dist/components/componentSecrets.js +405 -0
  58. package/dist/components/componentSecrets.js.map +1 -0
  59. package/dist/components/deploymentRecorder.d.ts +3 -0
  60. package/dist/components/deploymentRecorder.js +4 -3
  61. package/dist/components/deploymentRecorder.js.map +1 -1
  62. package/dist/components/mcp/adapters/fastify.d.ts +2 -0
  63. package/dist/components/mcp/adapters/fastify.js +1 -0
  64. package/dist/components/mcp/adapters/fastify.js.map +1 -1
  65. package/dist/components/mcp/adapters/harperHttp.d.ts +2 -0
  66. package/dist/components/mcp/adapters/harperHttp.js +1 -0
  67. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  68. package/dist/components/mcp/audit.d.ts +2 -2
  69. package/dist/components/mcp/audit.js +20 -5
  70. package/dist/components/mcp/audit.js.map +1 -1
  71. package/dist/components/mcp/customResourceRegistry.d.ts +5 -12
  72. package/dist/components/mcp/customResourceRegistry.js +15 -30
  73. package/dist/components/mcp/customResourceRegistry.js.map +1 -1
  74. package/dist/components/mcp/listChanged.d.ts +7 -2
  75. package/dist/components/mcp/listChanged.js +28 -0
  76. package/dist/components/mcp/listChanged.js.map +1 -1
  77. package/dist/components/mcp/quota.d.ts +31 -0
  78. package/dist/components/mcp/quota.js +155 -0
  79. package/dist/components/mcp/quota.js.map +1 -0
  80. package/dist/components/mcp/rateLimit.d.ts +19 -3
  81. package/dist/components/mcp/rateLimit.js +129 -13
  82. package/dist/components/mcp/rateLimit.js.map +1 -1
  83. package/dist/components/mcp/resources.js.map +1 -1
  84. package/dist/components/mcp/toolRegistry.d.ts +39 -1
  85. package/dist/components/mcp/toolRegistry.js +60 -5
  86. package/dist/components/mcp/toolRegistry.js.map +1 -1
  87. package/dist/components/mcp/tools/application.d.ts +8 -1
  88. package/dist/components/mcp/tools/application.js +65 -5
  89. package/dist/components/mcp/tools/application.js.map +1 -1
  90. package/dist/components/mcp/tools/operations.d.ts +15 -3
  91. package/dist/components/mcp/tools/operations.js +99 -38
  92. package/dist/components/mcp/tools/operations.js.map +1 -1
  93. package/dist/components/mcp/tools/schemas/operationDescriptions.js +4 -4
  94. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  95. package/dist/components/mcp/tools/schemas/operations.js +4 -0
  96. package/dist/components/mcp/tools/schemas/operations.js.map +1 -1
  97. package/dist/components/mcp/transport.d.ts +6 -0
  98. package/dist/components/mcp/transport.js +47 -3
  99. package/dist/components/mcp/transport.js.map +1 -1
  100. package/dist/components/ollama/index.d.ts +4 -1
  101. package/dist/components/ollama/index.js +4 -2
  102. package/dist/components/ollama/index.js.map +1 -1
  103. package/dist/components/openai/index.d.ts +4 -1
  104. package/dist/components/openai/index.js +4 -2
  105. package/dist/components/openai/index.js.map +1 -1
  106. package/dist/components/operations.d.ts +39 -0
  107. package/dist/components/operations.js +194 -2
  108. package/dist/components/operations.js.map +1 -1
  109. package/dist/components/operationsValidation.d.ts +38 -0
  110. package/dist/components/operationsValidation.js +155 -0
  111. package/dist/components/operationsValidation.js.map +1 -1
  112. package/dist/components/secretOperations.d.ts +113 -0
  113. package/dist/components/secretOperations.js +475 -0
  114. package/dist/components/secretOperations.js.map +1 -0
  115. package/dist/config/configUtils.d.ts +4 -1
  116. package/dist/config/configUtils.js +18 -1
  117. package/dist/config/configUtils.js.map +1 -1
  118. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
  119. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
  120. package/dist/dataLayer/readAuditLog.js +11 -0
  121. package/dist/dataLayer/readAuditLog.js.map +1 -1
  122. package/dist/dataLayer/schemaDescribe.js +2 -0
  123. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  124. package/dist/globals.d.ts +1 -1
  125. package/dist/globals.js +1 -0
  126. package/dist/globals.js.map +1 -1
  127. package/dist/index.d.ts +3 -0
  128. package/dist/index.js +1 -0
  129. package/dist/index.js.map +1 -1
  130. package/dist/json/systemSchema.json +38 -0
  131. package/dist/resources/DatabaseTransaction.d.ts +31 -3
  132. package/dist/resources/DatabaseTransaction.js +190 -29
  133. package/dist/resources/DatabaseTransaction.js.map +1 -1
  134. package/dist/resources/LMDBTransaction.d.ts +1 -1
  135. package/dist/resources/LMDBTransaction.js +43 -5
  136. package/dist/resources/LMDBTransaction.js.map +1 -1
  137. package/dist/resources/PrimaryRocksDatabase.d.ts +51 -0
  138. package/dist/resources/PrimaryRocksDatabase.js +194 -0
  139. package/dist/resources/PrimaryRocksDatabase.js.map +1 -0
  140. package/dist/resources/RecordEncoder.d.ts +1 -0
  141. package/dist/resources/RecordEncoder.js +81 -75
  142. package/dist/resources/RecordEncoder.js.map +1 -1
  143. package/dist/resources/RequestTarget.d.ts +19 -0
  144. package/dist/resources/RequestTarget.js.map +1 -1
  145. package/dist/resources/Resource.d.ts +0 -1
  146. package/dist/resources/Resource.js +103 -16
  147. package/dist/resources/Resource.js.map +1 -1
  148. package/dist/resources/ResourceInterface.d.ts +6 -2
  149. package/dist/resources/ResourceInterface.js.map +1 -1
  150. package/dist/resources/Resources.js.map +1 -1
  151. package/dist/resources/Table.d.ts +6 -4
  152. package/dist/resources/Table.js +247 -24
  153. package/dist/resources/Table.js.map +1 -1
  154. package/dist/resources/analytics/read.js +25 -28
  155. package/dist/resources/analytics/read.js.map +1 -1
  156. package/dist/resources/blob.js +165 -22
  157. package/dist/resources/blob.js.map +1 -1
  158. package/dist/resources/crdt.d.ts +2 -0
  159. package/dist/resources/crdt.js +45 -36
  160. package/dist/resources/crdt.js.map +1 -1
  161. package/dist/resources/databases.d.ts +1 -0
  162. package/dist/resources/databases.js +134 -7
  163. package/dist/resources/databases.js.map +1 -1
  164. package/dist/resources/graphql.js +8 -0
  165. package/dist/resources/graphql.js.map +1 -1
  166. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +35 -5
  167. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +239 -14
  168. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  169. package/dist/resources/jsResource.js +6 -0
  170. package/dist/resources/jsResource.js.map +1 -1
  171. package/dist/resources/loadEnv.js +30 -3
  172. package/dist/resources/loadEnv.js.map +1 -1
  173. package/dist/resources/login.js +2 -2
  174. package/dist/resources/login.js.map +1 -1
  175. package/dist/resources/models/Models.js +14 -3
  176. package/dist/resources/models/Models.js.map +1 -1
  177. package/dist/resources/models/embedHook.js +10 -2
  178. package/dist/resources/models/embedHook.js.map +1 -1
  179. package/dist/resources/models/openaiStream.d.ts +56 -0
  180. package/dist/resources/models/openaiStream.js +94 -0
  181. package/dist/resources/models/openaiStream.js.map +1 -0
  182. package/dist/resources/replayLogs.js +4 -3
  183. package/dist/resources/replayLogs.js.map +1 -1
  184. package/dist/resources/search.d.ts +1 -1
  185. package/dist/resources/search.js +104 -11
  186. package/dist/resources/search.js.map +1 -1
  187. package/dist/resources/secretDecryptor.d.ts +54 -0
  188. package/dist/resources/secretDecryptor.js +131 -0
  189. package/dist/resources/secretDecryptor.js.map +1 -0
  190. package/dist/resources/tracked.js +15 -5
  191. package/dist/resources/tracked.js.map +1 -1
  192. package/dist/security/auth.js +71 -8
  193. package/dist/security/auth.js.map +1 -1
  194. package/dist/security/jsLoader.js +11 -0
  195. package/dist/security/jsLoader.js.map +1 -1
  196. package/dist/security/tokenAuthentication.d.ts +9 -1
  197. package/dist/security/tokenAuthentication.js +31 -1
  198. package/dist/security/tokenAuthentication.js.map +1 -1
  199. package/dist/server/DurableSubscriptionsSession.d.ts +1 -1
  200. package/dist/server/DurableSubscriptionsSession.js +8 -4
  201. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  202. package/dist/server/REST.js +28 -9
  203. package/dist/server/REST.js.map +1 -1
  204. package/dist/server/fastifyRoutes.js +11 -1
  205. package/dist/server/fastifyRoutes.js.map +1 -1
  206. package/dist/server/graphqlQuerying.js +4 -3
  207. package/dist/server/graphqlQuerying.js.map +1 -1
  208. package/dist/server/http.d.ts +40 -2
  209. package/dist/server/http.js +560 -77
  210. package/dist/server/http.js.map +1 -1
  211. package/dist/server/itc/serverHandlers.js +64 -0
  212. package/dist/server/itc/serverHandlers.js.map +1 -1
  213. package/dist/server/liveSubscriptionAuth.d.ts +12 -0
  214. package/dist/server/liveSubscriptionAuth.js +140 -0
  215. package/dist/server/liveSubscriptionAuth.js.map +1 -0
  216. package/dist/server/loadRootComponents.js +2 -1
  217. package/dist/server/loadRootComponents.js.map +1 -1
  218. package/dist/server/middlewareChain.d.ts +44 -1
  219. package/dist/server/middlewareChain.js +86 -10
  220. package/dist/server/middlewareChain.js.map +1 -1
  221. package/dist/server/mqtt.js +2 -1
  222. package/dist/server/mqtt.js.map +1 -1
  223. package/dist/server/operationsServer.js +1 -1
  224. package/dist/server/operationsServer.js.map +1 -1
  225. package/dist/server/serverHelpers/Headers.d.ts +9 -0
  226. package/dist/server/serverHelpers/Headers.js +22 -0
  227. package/dist/server/serverHelpers/Headers.js.map +1 -1
  228. package/dist/server/serverHelpers/JSONStream.js +3 -3
  229. package/dist/server/serverHelpers/JSONStream.js.map +1 -1
  230. package/dist/server/serverHelpers/Request.d.ts +52 -1
  231. package/dist/server/serverHelpers/Request.js +122 -1
  232. package/dist/server/serverHelpers/Request.js.map +1 -1
  233. package/dist/server/serverHelpers/contentTypes.js +3 -8
  234. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  235. package/dist/server/serverHelpers/progressEmitter.d.ts +19 -0
  236. package/dist/server/serverHelpers/progressEmitter.js +52 -4
  237. package/dist/server/serverHelpers/progressEmitter.js.map +1 -1
  238. package/dist/server/serverHelpers/registeredOperations.d.ts +40 -0
  239. package/dist/server/serverHelpers/registeredOperations.js +286 -0
  240. package/dist/server/serverHelpers/registeredOperations.js.map +1 -0
  241. package/dist/server/serverHelpers/serverHandlers.js +11 -5
  242. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  243. package/dist/server/serverHelpers/serverUtilities.d.ts +1 -0
  244. package/dist/server/serverHelpers/serverUtilities.js +67 -5
  245. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  246. package/dist/server/serverHelpers/uwsServer.d.ts +80 -0
  247. package/dist/server/serverHelpers/uwsServer.js +433 -0
  248. package/dist/server/serverHelpers/uwsServer.js.map +1 -0
  249. package/dist/server/static.d.ts +0 -15
  250. package/dist/server/static.js +161 -2
  251. package/dist/server/static.js.map +1 -1
  252. package/dist/server/status/index.d.ts +4 -1
  253. package/dist/server/status/index.js +64 -4
  254. package/dist/server/status/index.js.map +1 -1
  255. package/dist/server/threads/manageThreads.d.ts +10 -0
  256. package/dist/server/threads/manageThreads.js +107 -4
  257. package/dist/server/threads/manageThreads.js.map +1 -1
  258. package/dist/server/threads/socketRouter.js +5 -1
  259. package/dist/server/threads/socketRouter.js.map +1 -1
  260. package/dist/server/threads/threadServer.js +92 -14
  261. package/dist/server/threads/threadServer.js.map +1 -1
  262. package/dist/upgrade/directives/5-2-0.d.ts +7 -0
  263. package/dist/upgrade/directives/5-2-0.js +108 -0
  264. package/dist/upgrade/directives/5-2-0.js.map +1 -0
  265. package/dist/upgrade/directives/directivesController.js +2 -1
  266. package/dist/upgrade/directives/directivesController.js.map +1 -1
  267. package/dist/utility/common_utils.js +5 -0
  268. package/dist/utility/common_utils.js.map +1 -1
  269. package/dist/utility/envFile.d.ts +41 -0
  270. package/dist/utility/envFile.js +221 -0
  271. package/dist/utility/envFile.js.map +1 -0
  272. package/dist/utility/globalSchema.d.ts +9 -0
  273. package/dist/utility/hdbTerms.d.ts +25 -0
  274. package/dist/utility/hdbTerms.js +31 -0
  275. package/dist/utility/hdbTerms.js.map +1 -1
  276. package/dist/utility/logging/harper_logger.d.ts +21 -0
  277. package/dist/utility/logging/harper_logger.js +160 -8
  278. package/dist/utility/logging/harper_logger.js.map +1 -1
  279. package/dist/utility/logging/logRotator.js +29 -17
  280. package/dist/utility/logging/logRotator.js.map +1 -1
  281. package/dist/utility/logging/readLog.d.ts +1 -1
  282. package/dist/utility/logging/readLog.js +305 -2
  283. package/dist/utility/logging/readLog.js.map +1 -1
  284. package/dist/utility/operationPermissions.d.ts +18 -0
  285. package/dist/utility/operationPermissions.js +35 -1
  286. package/dist/utility/operationPermissions.js.map +1 -1
  287. package/dist/utility/operation_authorization.d.ts +17 -0
  288. package/dist/utility/operation_authorization.js +38 -0
  289. package/dist/utility/operation_authorization.js.map +1 -1
  290. package/dist/utility/secretEnvelope.d.ts +30 -0
  291. package/dist/utility/secretEnvelope.js +94 -0
  292. package/dist/utility/secretEnvelope.js.map +1 -0
  293. package/dist/utility/signalling.d.ts +7 -0
  294. package/dist/utility/signalling.js +16 -0
  295. package/dist/utility/signalling.js.map +1 -1
  296. package/dist/validation/configValidator.js +70 -24
  297. package/dist/validation/configValidator.js.map +1 -1
  298. package/index.ts +4 -0
  299. package/json/systemSchema.json +38 -0
  300. package/npm-shrinkwrap.json +15066 -9347
  301. package/package.json +13 -8
  302. package/resources/DESIGN.md +15 -12
  303. package/resources/DatabaseTransaction.ts +206 -37
  304. package/resources/LMDBTransaction.ts +43 -9
  305. package/resources/PrimaryRocksDatabase.ts +201 -0
  306. package/resources/RecordEncoder.ts +81 -69
  307. package/resources/RequestTarget.ts +21 -0
  308. package/resources/Resource.ts +109 -25
  309. package/resources/ResourceInterface.ts +6 -2
  310. package/resources/Resources.ts +1 -3
  311. package/resources/Table.ts +238 -29
  312. package/resources/analytics/read.ts +25 -30
  313. package/resources/blob.ts +159 -23
  314. package/resources/crdt.ts +37 -31
  315. package/resources/databases.ts +143 -6
  316. package/resources/graphql.ts +8 -0
  317. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +251 -17
  318. package/resources/jsResource.ts +6 -0
  319. package/resources/loadEnv.ts +34 -3
  320. package/resources/login.ts +2 -2
  321. package/resources/models/Models.ts +19 -3
  322. package/resources/models/embedHook.ts +13 -2
  323. package/resources/models/openaiStream.ts +133 -0
  324. package/resources/replayLogs.ts +4 -3
  325. package/resources/search.ts +113 -11
  326. package/resources/secretDecryptor.ts +159 -0
  327. package/resources/tracked.ts +24 -17
  328. package/security/auth.ts +70 -10
  329. package/security/jsLoader.ts +12 -0
  330. package/security/tokenAuthentication.ts +42 -1
  331. package/server/DESIGN.md +10 -0
  332. package/server/DurableSubscriptionsSession.ts +8 -4
  333. package/server/REST.ts +26 -9
  334. package/server/fastifyRoutes.ts +10 -1
  335. package/server/graphqlQuerying.ts +4 -3
  336. package/server/http.ts +563 -77
  337. package/server/itc/serverHandlers.js +70 -0
  338. package/server/liveSubscriptionAuth.ts +152 -0
  339. package/server/loadRootComponents.js +2 -1
  340. package/server/middlewareChain.ts +107 -17
  341. package/server/mqtt.ts +2 -1
  342. package/server/operationsServer.ts +2 -2
  343. package/server/serverHelpers/Headers.ts +24 -0
  344. package/server/serverHelpers/JSONStream.ts +3 -3
  345. package/server/serverHelpers/Request.ts +127 -0
  346. package/server/serverHelpers/contentTypes.ts +3 -8
  347. package/server/serverHelpers/progressEmitter.ts +55 -5
  348. package/server/serverHelpers/registeredOperations.ts +259 -0
  349. package/server/serverHelpers/serverHandlers.js +11 -5
  350. package/server/serverHelpers/serverUtilities.ts +88 -5
  351. package/server/serverHelpers/uwsServer.ts +488 -0
  352. package/server/static.ts +180 -2
  353. package/server/status/index.ts +72 -4
  354. package/server/threads/manageThreads.js +102 -4
  355. package/server/threads/socketRouter.ts +5 -1
  356. package/server/threads/threadServer.js +93 -10
  357. package/studio/web/assets/{Chat-SdD1EZca.js → Chat-Cv_2paZE.js} +2 -2
  358. package/studio/web/assets/{Chat-SdD1EZca.js.map → Chat-Cv_2paZE.js.map} +1 -1
  359. package/studio/web/assets/{FloatingChat-GMq6tHK9.js → FloatingChat-CPyPIcVR.js} +4 -4
  360. package/studio/web/assets/{FloatingChat-GMq6tHK9.js.map → FloatingChat-CPyPIcVR.js.map} +1 -1
  361. package/studio/web/assets/{applications-B5hhv7uA.js → applications-C0jT2vdZ.js} +2 -2
  362. package/studio/web/assets/{applications-B5hhv7uA.js.map → applications-C0jT2vdZ.js.map} +1 -1
  363. package/studio/web/assets/{index-VFcpNWgq.js → index-D_GKOkhn.js} +6 -6
  364. package/studio/web/assets/index-D_GKOkhn.js.map +1 -0
  365. package/studio/web/assets/{index.lazy-DgcJojIA.js → index.lazy-CAmCIA65.js} +4 -4
  366. package/studio/web/assets/{index.lazy-DgcJojIA.js.map → index.lazy-CAmCIA65.js.map} +1 -1
  367. package/studio/web/assets/{profile-BqN-8vzm.js → profile-CaF-4aZe.js} +2 -2
  368. package/studio/web/assets/{profile-BqN-8vzm.js.map → profile-CaF-4aZe.js.map} +1 -1
  369. package/studio/web/assets/{setComponentFile-CHFvFq9u.js → setComponentFile-DLW1DHCt.js} +2 -2
  370. package/studio/web/assets/{setComponentFile-CHFvFq9u.js.map → setComponentFile-DLW1DHCt.js.map} +1 -1
  371. package/studio/web/assets/{setup-CQ3vWJSr.js → setup-TGCErIhq.js} +2 -2
  372. package/studio/web/assets/{setup-CQ3vWJSr.js.map → setup-TGCErIhq.js.map} +1 -1
  373. package/studio/web/assets/{status-BsW5fjuh.js → status-DG0Maoao.js} +2 -2
  374. package/studio/web/assets/{status-BsW5fjuh.js.map → status-DG0Maoao.js.map} +1 -1
  375. package/studio/web/assets/{swagger-ui-react-DITdeB9-.js → swagger-ui-react-8T4SgQ1m.js} +2 -2
  376. package/studio/web/assets/{swagger-ui-react-DITdeB9-.js.map → swagger-ui-react-8T4SgQ1m.js.map} +1 -1
  377. package/studio/web/assets/{tsMode-Dyph-OUs.js → tsMode-DJ6Sl24Q.js} +2 -2
  378. package/studio/web/assets/{tsMode-Dyph-OUs.js.map → tsMode-DJ6Sl24Q.js.map} +1 -1
  379. package/studio/web/assets/{useEntityRestURL-D0QTUqex.js → useEntityRestURL-JO2mfWTQ.js} +2 -2
  380. package/studio/web/assets/{useEntityRestURL-D0QTUqex.js.map → useEntityRestURL-JO2mfWTQ.js.map} +1 -1
  381. package/studio/web/index.html +1 -1
  382. package/upgrade/directives/5-2-0.ts +82 -0
  383. package/upgrade/directives/directivesController.ts +2 -1
  384. package/utility/common_utils.ts +5 -0
  385. package/utility/envFile.ts +218 -0
  386. package/utility/hdbTerms.ts +31 -0
  387. package/utility/logging/harper_logger.ts +156 -8
  388. package/utility/logging/logRotator.ts +24 -14
  389. package/utility/logging/readLog.ts +323 -2
  390. package/utility/operationPermissions.ts +35 -1
  391. package/utility/operation_authorization.ts +74 -1
  392. package/utility/secretEnvelope.ts +113 -0
  393. package/utility/signalling.ts +15 -0
  394. package/validation/configValidator.ts +78 -25
  395. package/studio/web/assets/index-VFcpNWgq.js.map +0 -1
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.bunServeConfigs = exports.universalHeaders = void 0;
39
+ exports.uwsServeConfigs = exports.bunServeConfigs = exports.universalHeaders = void 0;
40
40
  exports.registerUdsCleanupPaths = registerUdsCleanupPaths;
41
41
  exports.cleanupUdsFiles = cleanupUdsFiles;
42
42
  exports.writeUdsMetadata = writeUdsMetadata;
@@ -47,8 +47,10 @@ exports.deliverSocket = deliverSocket;
47
47
  exports.proxyRequest = proxyRequest;
48
48
  exports.registerServer = registerServer;
49
49
  exports.httpServer = httpServer;
50
- exports.registerBunFastifyInstance = registerBunFastifyInstance;
50
+ exports.registerFastifyInstance = registerFastifyInstance;
51
+ exports.describeMiddlewareChains = describeMiddlewareChains;
51
52
  exports.enableProxyProtocol = enableProxyProtocol;
53
+ exports.createH2CProxyFront = createH2CProxyFront;
52
54
  exports.logRequest = logRequest;
53
55
  exports.getRequestId = getRequestId;
54
56
  // @ts-nocheck
@@ -68,6 +70,7 @@ const keys_ts_1 = require("../security/keys.js");
68
70
  const node_http2_1 = require("node:http2");
69
71
  const node_https_1 = require("node:https");
70
72
  const node_http_1 = require("node:http");
73
+ const node_net_2 = require("node:net");
71
74
  const Request_ts_1 = require("./serverHelpers/Request.js");
72
75
  const Headers_ts_1 = require("./serverHelpers/Headers.js");
73
76
  const blob_ts_1 = require("../resources/blob.js");
@@ -81,7 +84,7 @@ const componentLoader_ts_1 = require("../components/componentLoader.js");
81
84
  const throttle_ts_1 = require("./throttle.js");
82
85
  const middlewareChain_ts_1 = require("./middlewareChain.js");
83
86
  const ws_1 = require("ws");
84
- const { errorToString } = harper_logger_ts_1.default;
87
+ const { errorToString, errorForLog } = harper_logger_ts_1.default;
85
88
  Server_ts_1.server.http = httpServer;
86
89
  Server_ts_1.server.request = onRequest;
87
90
  Server_ts_1.server.ws = onWebSocket;
@@ -92,8 +95,15 @@ let httpOptions = {};
92
95
  exports.universalHeaders = [];
93
96
  // Bun-specific: stores fetch handler configs per port, used by threadServer.js to call Bun.serve()
94
97
  exports.bunServeConfigs = {};
95
- // Bun-specific: stores non-function listeners (e.g. Fastify servers) per port for fallback delegation
96
- const bunFallbackServers = {};
98
+ // uWS backend (#914): stores { socketPath, secure, handler } configs keyed by UDS path. When
99
+ // HARPER_UWS_UDS is set, the per-worker UDS mirror is served by uWebSockets.js instead of a Node
100
+ // http server; threadServer.js consumes these and calls createUwsServer(). Symphony must forward
101
+ // client identity via X-Forwarded-For (not the PROXY protocol) on these sockets, as the Bun path does.
102
+ exports.uwsServeConfigs = {};
103
+ // Stores non-function listeners (e.g. Fastify servers) per port for fallback delegation on the
104
+ // backends that don't register their own Node http server (Bun, and the uWS HTTP path). Keeping
105
+ // them out of SERVERS is what prevents a competing Node server from binding the same port.
106
+ const fallbackServers = {};
97
107
  const udsCleanupPaths = [];
98
108
  function registerUdsCleanupPaths(socketPath, yamlPath) {
99
109
  udsCleanupPaths.push({ socketPath, yamlPath });
@@ -111,9 +121,13 @@ function cleanupUdsFiles() {
111
121
  }
112
122
  }
113
123
  /** Write YAML metadata for a UDS mirror socket, describing the TLS certs from the corresponding secure server. */
114
- function writeUdsMetadata(yamlPath, port, secureServer) {
124
+ function writeUdsMetadata(yamlPath, port, secureServer, protocol) {
115
125
  const contexts = secureServer.secureContexts;
116
126
  let yaml = `pid: ${process.pid}\ntid: ${(0, rocksdb_js_1.currentThreadId)()}\nport: ${port}\n`;
127
+ // Which application protocol this socket speaks (absent = http/1.1, the historical
128
+ // default) — lets a fronting proxy route by negotiated ALPN.
129
+ if (protocol)
130
+ yaml += `protocol: ${protocol}\n`;
117
131
  yaml += `certificates:\n`;
118
132
  if (contexts?.size > 0) {
119
133
  const seen = new Set();
@@ -359,7 +373,17 @@ function httpServer(listener, options) {
359
373
  }
360
374
  else if (Request_ts_1.isBun) {
361
375
  // On Bun, store non-function listeners (e.g. Fastify's http.Server) for fallback delegation
362
- bunFallbackServers[port] = listener;
376
+ fallbackServers[port] = listener;
377
+ }
378
+ else if (httpServers[port]?.uws) {
379
+ // uWS HTTP path (#914, HARPER_UWS_HTTP): the port is backed by uWebSockets.js, not a Node
380
+ // http server, so a raw non-function listener (e.g. Fastify's http.Server via
381
+ // server.http(fastify.server)) must NOT go through registerServer() — that would put it in
382
+ // SERVERS and threadServer would bind a Node http server competing with uWS on the same TCP
383
+ // port. Divert it to the fallback map like the Bun path; makeUwsHandler delegates unhandled
384
+ // requests to it via inject(). The { uws: true } marker is guaranteed present here: the
385
+ // getServer(port) call above (same loop iteration) sets it before this branch runs.
386
+ fallbackServers[port] = listener;
363
387
  }
364
388
  else {
365
389
  listener.isSecure = secure;
@@ -376,6 +400,29 @@ function getHTTPServer(port, secure, options) {
376
400
  if (!httpServers[port]) {
377
401
  // TODO: These should all come from httpOptions or operationsApiOptions
378
402
  const serverPrefix = isOperationsServer ? 'operationsApi_network' : (usageType ?? 'http');
403
+ // uWS plaintext-HTTP path (#914, HARPER_UWS_HTTP): back a non-secure TCP HTTP port with
404
+ // uWebSockets.js directly instead of a Node http server. This is the flag used to run the
405
+ // integration suite through uWS (no symphony/UDS needed). WebSocket upgrades are wired on this
406
+ // path via onWebSocket's uWS branch; it's opt-in and separate from HARPER_UWS_UDS.
407
+ const lastColon = String(port).lastIndexOf(':');
408
+ const uwsPort = lastColon > 0 ? +String(port).slice(lastColon + 1) : +port;
409
+ if (process.env.HARPER_UWS_HTTP &&
410
+ !secure &&
411
+ !isOperationsServer &&
412
+ !String(port).includes('/') &&
413
+ !Number.isNaN(uwsPort)) {
414
+ exports.uwsServeConfigs[port] = {
415
+ port: uwsPort,
416
+ host: lastColon > 0 ? String(port).slice(0, lastColon).replace(/[[\]]/g, '') : undefined,
417
+ secure: false,
418
+ handler: makeUwsHandler(port, isOperationsServer, env.get(serverPrefix + '_requestQueueLimit')),
419
+ };
420
+ // Marker so the httpServers guard is satisfied and the caller has a truthy handle; the
421
+ // actual listen happens in threadServer.js from uwsServeConfigs. onWebSocket() detects
422
+ // this marker (server.uws) and wires native uWS WebSocket handling into the same config.
423
+ httpServers[port] = { uws: true, port };
424
+ return httpServers[port];
425
+ }
379
426
  const keepAliveTimeout = env.get(serverPrefix + '_keepAliveTimeout');
380
427
  const requestTimeout = env.get(serverPrefix + '_timeout');
381
428
  const headersTimeout = env.get(serverPrefix + '_headersTimeout');
@@ -564,7 +611,9 @@ function getHTTPServer(port, secure, options) {
564
611
  }
565
612
  function onError(error) {
566
613
  const headers = error.headers;
567
- const status = error.statusCode || 500;
614
+ // the HTTP status may be carried as `statusCode` (our error classes) or `status` (e.g. a thrown plain object)
615
+ const statusCode = error.statusCode ?? error.status;
616
+ const status = statusCode || 500;
568
617
  try {
569
618
  nodeResponse.writeHead(status, (0, Headers_ts_1.toWriteHeadHeaders)(headers));
570
619
  }
@@ -572,14 +621,14 @@ function getHTTPServer(port, secure, options) {
572
621
  nodeResponse.end(errorToString(error));
573
622
  logRequest(nodeRequest, status, requestId, performance.now() - startTime);
574
623
  // a status code is interpreted as an expected error, so just info or warn, otherwise log as error
575
- if (error.statusCode) {
576
- if (error.statusCode === 500)
577
- harper_logger_ts_1.default.warn(error);
624
+ if (statusCode) {
625
+ if (statusCode === 500)
626
+ harper_logger_ts_1.default.warn(errorForLog(error));
578
627
  else
579
- harper_logger_ts_1.default.info(error);
628
+ harper_logger_ts_1.default.info(errorForLog(error));
580
629
  }
581
630
  else
582
- harper_logger_ts_1.default.error(error);
631
+ harper_logger_ts_1.default.error(errorForLog(error));
583
632
  }
584
633
  };
585
634
  // create a throttled version of the request handler, so we can throttle POST requests
@@ -638,34 +687,278 @@ function getHTTPServer(port, secure, options) {
638
687
  const socketName = `${(0, manageThreads_js_1.getWorkerIndex)()}-${port}`;
639
688
  const udsPath = (0, node_path_1.join)(socketsDir, `${socketName}.sock`);
640
689
  const yamlPath = (0, node_path_1.join)(socketsDir, `${socketName}.yaml`);
641
- // Create a plain HTTP server (no TLS) with the same request handler
642
- const udsServer = (0, node_http_1.createServer)({
643
- keepAliveTimeout,
644
- headersTimeout,
645
- requestTimeout,
646
- highWaterMark: 128 * 1024,
647
- noDelay: true,
648
- keepAlive: true,
649
- keepAliveInitialDelay: 600,
650
- maxHeaderSize: env.get(terms.CONFIG_PARAMS.HTTP_MAXHEADERSIZE),
651
- }, (nodeRequest, nodeResponse) => {
652
- const method = nodeRequest.method;
653
- if (method === 'GET' || method === 'OPTIONS' || method === 'HEAD')
654
- requestHandler(nodeRequest, nodeResponse);
655
- else
656
- throttledRequestHandler(nodeRequest, nodeResponse);
657
- });
658
- udsServer.isPerThreadSocket = true;
659
- enableProxyProtocol(udsServer);
660
- serverRegistry_ts_1.SERVERS[udsPath] = udsServer;
690
+ if (process.env.HARPER_UWS_UDS) {
691
+ // uWS backend (#914): serve the UDS mirror with uWebSockets.js instead of a Node http
692
+ // server. threadServer.js consumes uwsServeConfigs and calls createUwsServer(). uWS does
693
+ // not parse the PROXY protocol, so symphony must use sourceAddressHeader: 'xForwardedFor'
694
+ // for this socket (the same mode it uses for the Bun path).
695
+ exports.uwsServeConfigs[udsPath] = {
696
+ socketPath: udsPath,
697
+ secure: true,
698
+ handler: makeUwsHandler(port, isOperationsServer, env.get(serverPrefix + '_requestQueueLimit')),
699
+ };
700
+ }
701
+ else {
702
+ // Create a plain HTTP server (no TLS) with the same request handler
703
+ const udsServer = (0, node_http_1.createServer)({
704
+ keepAliveTimeout,
705
+ headersTimeout,
706
+ requestTimeout,
707
+ highWaterMark: 128 * 1024,
708
+ noDelay: true,
709
+ keepAlive: true,
710
+ keepAliveInitialDelay: 600,
711
+ maxHeaderSize: env.get(terms.CONFIG_PARAMS.HTTP_MAXHEADERSIZE),
712
+ }, (nodeRequest, nodeResponse) => {
713
+ const method = nodeRequest.method;
714
+ if (method === 'GET' || method === 'OPTIONS' || method === 'HEAD')
715
+ requestHandler(nodeRequest, nodeResponse);
716
+ else
717
+ throttledRequestHandler(nodeRequest, nodeResponse);
718
+ });
719
+ udsServer.isPerThreadSocket = true;
720
+ enableProxyProtocol(udsServer);
721
+ serverRegistry_ts_1.SERVERS[udsPath] = udsServer;
722
+ }
661
723
  registerUdsCleanupPaths(udsPath, yamlPath);
662
724
  const writeMetadata = () => writeUdsMetadata(yamlPath, port, server);
663
725
  options.SNICallback.ready.then(writeMetadata);
664
726
  server.secureContextsListeners.push(writeMetadata);
727
+ // Optional cleartext HTTP/2 mirror (spike: HARPER_H2C_UDS=1). A separate socket
728
+ // (`<worker>-<port>-h2.sock`) so a fronting proxy can route by negotiated ALPN:
729
+ // h2 connections here, http/1.1 to the plain mirror above. The metadata yaml
730
+ // carries `protocol: h2` so the proxy can discover which socket speaks what.
731
+ if (process.env.HARPER_H2C_UDS) {
732
+ const udsPathH2 = (0, node_path_1.join)(socketsDir, `${socketName}-h2.sock`);
733
+ const yamlPathH2 = (0, node_path_1.join)(socketsDir, `${socketName}-h2.yaml`);
734
+ const h2Server = (0, node_http2_1.createServer)({}, (nodeRequest, nodeResponse) => {
735
+ const method = nodeRequest.method;
736
+ if (method === 'GET' || method === 'OPTIONS' || method === 'HEAD')
737
+ requestHandler(nodeRequest, nodeResponse);
738
+ else
739
+ throttledRequestHandler(nodeRequest, nodeResponse);
740
+ });
741
+ // A stray non-h2 client (or a truncated preface) fails the session, not the worker.
742
+ h2Server.on('sessionError', (error) => {
743
+ harper_logger_ts_1.default.debug('h2c UDS session error:', error);
744
+ });
745
+ const h2Front = createH2CProxyFront(h2Server);
746
+ h2Front.isPerThreadSocket = true;
747
+ serverRegistry_ts_1.SERVERS[udsPathH2] = h2Front;
748
+ registerUdsCleanupPaths(udsPathH2, yamlPathH2);
749
+ const writeMetadataH2 = () => writeUdsMetadata(yamlPathH2, port, server, 'h2');
750
+ options.SNICallback.ready.then(writeMetadataH2);
751
+ server.secureContextsListeners.push(writeMetadataH2);
752
+ }
665
753
  }
666
754
  }
667
755
  return httpServers[port];
668
756
  }
757
+ /**
758
+ * uWS backend (#914): builds the per-request handler for a uWS UDS server. Mirrors the Bun
759
+ * fetchHandler's post-processing (httpChain, unhandled, universalHeaders, Server-Timing,
760
+ * analytics, logging) but returns a plain Harper response descriptor for createUwsServer to
761
+ * serialize onto the uWS HttpResponse. When the chain doesn't handle the request (status === -1)
762
+ * and a Fastify fallback is registered for the port, it delegates via inject() (see injectToFastify),
763
+ * mirroring the Bun path — so legacy Fastify routes work behind uWS too.
764
+ */
765
+ function makeUwsHandler(port, isOperationsServer, requestQueueLimit) {
766
+ // Build a fresh response descriptor rather than mutating what the chain returned: a handler may
767
+ // return a WHATWG `Response` (read-only `status`/`body` accessors), which the Bun path also never
768
+ // mutates. `headers` is normalized in place the same way the Bun path does.
769
+ const handle = async (request) => {
770
+ const startTime = performance.now();
771
+ let requestId = 0;
772
+ if (isOperationsServer)
773
+ request.isOperationsServer = true;
774
+ if (httpOptions.logging?.id)
775
+ request.requestId = requestId = getRequestId();
776
+ let response = await httpChain[port](request);
777
+ if (!response)
778
+ response = unhandled(request);
779
+ let headers = response.headers;
780
+ if (!headers?.set)
781
+ headers = new Headers_ts_1.Headers(headers);
782
+ for (const [key, value] of exports.universalHeaders)
783
+ headers.set(key, value);
784
+ if (response.status === -1) {
785
+ // The chain didn't handle it. If a Fastify fallback is registered for this port (legacy
786
+ // custom-function routes via server.http(fastify.server)), delegate to it via inject(),
787
+ // mirroring the Bun path; otherwise it's a genuine 404.
788
+ const fastify = fastifyInstances[port];
789
+ if (fastify) {
790
+ const injectResult = await injectToFastify(fastify, {
791
+ method: request.method,
792
+ url: request.url,
793
+ headers: request.headers.asObject,
794
+ body: request.body, // stream; inject() consumes it as the payload
795
+ user: request.user,
796
+ });
797
+ const respHeaders = new Headers_ts_1.Headers();
798
+ for (const [k, v] of Object.entries(injectResult.headers)) {
799
+ if (v == null)
800
+ continue;
801
+ // Keep Set-Cookie multi-valued (Harper Headers + writeHeaders emit each separately);
802
+ // only comma-join other repeated headers.
803
+ if (Array.isArray(v))
804
+ respHeaders.set(k, k.toLowerCase() === 'set-cookie' ? v : v.join(', '));
805
+ else
806
+ respHeaders.set(k, String(v));
807
+ }
808
+ logHttpRequest(request, injectResult.statusCode, requestId, performance.now() - startTime);
809
+ const responseStream = injectResult.stream();
810
+ // Event-stream (SSE) responses must reach the client incrementally — stream the body and,
811
+ // on client disconnect, destroy the inject response so the Fastify reply's teardown runs
812
+ // (matches the Bun path). Finite responses buffer so Content-Length stays set.
813
+ if (String(injectResult.headers['content-type'] ?? '').includes('text/event-stream')) {
814
+ const injectResponse = injectResult.raw?.res;
815
+ if (injectResponse && typeof injectResponse.destroy === 'function') {
816
+ responseStream.once('close', () => injectResponse.destroy());
817
+ }
818
+ return { status: injectResult.statusCode, headers: respHeaders, body: responseStream };
819
+ }
820
+ const chunks = [];
821
+ for await (const chunk of responseStream)
822
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
823
+ return {
824
+ status: injectResult.statusCode,
825
+ headers: respHeaders,
826
+ body: chunks.length > 0 ? Buffer.concat(chunks) : null,
827
+ };
828
+ }
829
+ logHttpRequest(request, 404, requestId, performance.now() - startTime);
830
+ return { status: 404, headers: new Headers_ts_1.Headers({ 'content-type': 'text/plain' }), body: 'Not found\n' };
831
+ }
832
+ const status = response.status || 200;
833
+ const executionTime = performance.now() - startTime;
834
+ if (!response.handlesHeaders) {
835
+ let serverTiming = `hdb;dur=${executionTime.toFixed(2)}`;
836
+ if (response.wasCacheMiss)
837
+ serverTiming += ', miss';
838
+ (0, Headers_ts_1.appendHeader)(headers, 'Server-Timing', serverTiming, true);
839
+ }
840
+ (0, write_ts_1.recordAction)(executionTime, 'duration', request.handlerPath, request.method, response.wasCacheMiss == undefined ? undefined : response.wasCacheMiss ? 'cache-miss' : 'cache-hit');
841
+ (0, write_ts_1.recordActionBinary)(status < 400, 'success', request.handlerPath, request.method);
842
+ (0, write_ts_1.recordActionBinary)(1, 'response_' + status, request.handlerPath, request.method);
843
+ logHttpRequest(request, status, requestId, executionTime);
844
+ // Static handlers (the only handlesHeaders producers) return a `send` SendStream that writes
845
+ // its own headers/body to a Node ServerResponse via .pipe(). uWS has no such object, and a
846
+ // SendStream doesn't start until piped, so streaming it directly hangs (headers never flush).
847
+ // Pipe it into a Writable shim that captures the headers and buffers the file, mirroring the
848
+ // Bun path. Non-handlesHeaders bodies keep streaming through normalizeUwsBody.
849
+ if (response.handlesHeaders && response.body && typeof response.body.pipe === 'function') {
850
+ // send() may return 304 (conditional GET) or 206/416 (Range) — honor the status it set.
851
+ const sent = await bufferSendStream(response.body, headers, status, request.signal);
852
+ return { status: sent.status, headers, handlesHeaders: true, body: sent.body };
853
+ }
854
+ const body = await normalizeUwsBody(response.body, request.signal);
855
+ return { status, headers, handlesHeaders: response.handlesHeaders, body };
856
+ };
857
+ // Shed data-modifying requests when the event queue is backed up (503), mirroring the Node UDS
858
+ // path — GET/OPTIONS/HEAD are cheap and always run, everything else goes through the throttle.
859
+ const throttledHandle = (0, throttle_ts_1.throttle)(handle, (_request) => {
860
+ (0, write_ts_1.recordAction)(true, 'service-unavailable', port);
861
+ return {
862
+ status: 503,
863
+ headers: new Headers_ts_1.Headers({ 'content-type': 'text/plain' }),
864
+ body: 'Service unavailable, exceeded request queue limit',
865
+ };
866
+ }, requestQueueLimit);
867
+ return (request) => {
868
+ const method = request.method;
869
+ if (method === 'GET' || method === 'OPTIONS' || method === 'HEAD')
870
+ return handle(request);
871
+ return throttledHandle(request);
872
+ };
873
+ }
874
+ /**
875
+ * uWS: normalize a Harper response body into what the adapter can serialize. Finite bodies collapse
876
+ * to a string/Buffer; a Node stream or async-iterable is returned as a Readable so writeResponse can
877
+ * stream it incrementally (buffering an SSE/event-stream body here would never return). `signal`
878
+ * aborts the collapse of a sync iterable if the client disconnects mid-response.
879
+ */
880
+ /**
881
+ * Drive a `send` SendStream to completion against a Writable shim, capturing the headers it writes
882
+ * (setHeader/writeHead) onto `headers` and the status it sets (statusCode/writeHead) and buffering
883
+ * the file body. `send` targets an http.ServerResponse (setHeader/writeHead/statusCode/finished);
884
+ * uWS has none, so we adapt — mirrors the Bun fetchHandler's SendStream path. The captured status
885
+ * carries send's conditional-GET (304) and Range (206/416) results. Buffering is fine for static
886
+ * assets and keeps Content-Length set. `defaultStatus` is used when send sets none.
887
+ */
888
+ function bufferSendStream(body, headers, defaultStatus, signal) {
889
+ return new Promise((resolve, reject) => {
890
+ const chunks = [];
891
+ const dest = new node_stream_1.Writable({
892
+ write(chunk, _encoding, callback) {
893
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
894
+ callback();
895
+ },
896
+ final(callback) {
897
+ callback();
898
+ resolve({ body: Buffer.concat(chunks), status: dest.statusCode || defaultStatus });
899
+ },
900
+ });
901
+ Object.assign(dest, {
902
+ setHeader: (n, v) => headers.set(n, v),
903
+ getHeader: (n) => headers.get(n),
904
+ removeHeader: (n) => headers.delete(n.toLowerCase()),
905
+ // send conveys 304/206/416 via statusCode and/or writeHead's status arg — capture both so
906
+ // conditional-GET and Range responses aren't flattened to the default 200.
907
+ writeHead: (s, hdrs) => {
908
+ if (s)
909
+ dest.statusCode = s;
910
+ if (hdrs)
911
+ for (const k in hdrs)
912
+ headers.set(k, hdrs[k]);
913
+ },
914
+ statusCode: defaultStatus,
915
+ headersSent: false,
916
+ // 'on-finished' (used by 'send') treats a non-false `finished` as already-done and destroys
917
+ // the read stream before data flows; keep it false so it waits for the 'finish' event.
918
+ finished: false,
919
+ });
920
+ const onAbort = () => {
921
+ body.destroy?.();
922
+ dest.destroy?.();
923
+ reject(new Error('client aborted'));
924
+ };
925
+ if (signal) {
926
+ if (signal.aborted)
927
+ return onAbort();
928
+ signal.addEventListener('abort', onAbort, { once: true });
929
+ }
930
+ body.on('error', reject);
931
+ dest.on('error', reject);
932
+ body.pipe(dest);
933
+ });
934
+ }
935
+ async function normalizeUwsBody(body, signal) {
936
+ if (body == null)
937
+ return null;
938
+ if (typeof body === 'string' || Buffer.isBuffer(body) || body instanceof Uint8Array)
939
+ return body;
940
+ if (body instanceof blob_ts_1.Blob)
941
+ return Buffer.from(await body.arrayBuffer());
942
+ if (typeof body.then === 'function')
943
+ return normalizeUwsBody(await body, signal);
944
+ // Already a Node stream — stream it as-is (re-wrapping in Readable.from breaks destroy propagation).
945
+ if (typeof body.pipe === 'function')
946
+ return body;
947
+ // Async-iterable (e.g. an event queue) — adapt to a Readable and stream it.
948
+ if (body[Symbol.asyncIterator])
949
+ return node_stream_1.Readable.from(body);
950
+ // Sync iterable — small/finite, collapse to a buffer.
951
+ if (body[Symbol.iterator]) {
952
+ const chunks = [];
953
+ for (const chunk of body) {
954
+ if (signal?.aborted)
955
+ throw new Error('client aborted');
956
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
957
+ }
958
+ return Buffer.concat(chunks);
959
+ }
960
+ return String(body);
961
+ }
669
962
  /**
670
963
  * Bun-specific HTTP server setup. Instead of creating a Node http.Server, we store a fetch handler config
671
964
  * that will be passed to Bun.serve() when listenOnPorts() is called in threadServer.js.
@@ -696,14 +989,14 @@ function getBunHTTPServer(port, secure, options) {
696
989
  response.headers.set(key, value);
697
990
  }
698
991
  if (response.status === -1) {
699
- const fallbackServer = bunFallbackServers[port];
992
+ const fallbackServer = fallbackServers[port];
700
993
  if (fallbackServer) {
701
994
  // Delegate to the fallback server (e.g. Fastify) via node:http compatibility.
702
995
  // We create a Node-compatible IncomingMessage/ServerResponse and emit 'request'
703
996
  // on the fallback server, then capture the response.
704
997
  return await bunDelegateToNodeServer(fallbackServer, webRequest, request);
705
998
  }
706
- logBunRequest(request, 404, requestId, performance.now() - startTime);
999
+ logHttpRequest(request, 404, requestId, performance.now() - startTime);
707
1000
  return new Response('Not found\n', { status: 404 });
708
1001
  }
709
1002
  const status = response.status || 200;
@@ -758,7 +1051,7 @@ function getBunHTTPServer(port, secure, options) {
758
1051
  (0, write_ts_1.recordAction)(executionTime, 'duration', handlerPath, method, response.wasCacheMiss == undefined ? undefined : response.wasCacheMiss ? 'cache-miss' : 'cache-hit');
759
1052
  (0, write_ts_1.recordActionBinary)(status < 400, 'success', handlerPath, method);
760
1053
  (0, write_ts_1.recordActionBinary)(1, 'response_' + status, handlerPath, method);
761
- logBunRequest(request, status, requestId, executionTime);
1054
+ logHttpRequest(request, status, requestId, executionTime);
762
1055
  // Convert body to something Bun's Response can accept
763
1056
  if (body instanceof ReadableStream) {
764
1057
  return new Response(body, { status, headers: responseHeaders });
@@ -813,16 +1106,18 @@ function getBunHTTPServer(port, secure, options) {
813
1106
  return new Response(body, { status, headers: responseHeaders });
814
1107
  }
815
1108
  catch (error) {
816
- const status = error.statusCode || 500;
817
- logBunRequest(null, status, requestId, performance.now() - startTime);
818
- if (error.statusCode) {
819
- if (error.statusCode === 500)
820
- harper_logger_ts_1.default.warn(error);
1109
+ // the HTTP status may be carried as `statusCode` (our error classes) or `status` (e.g. a thrown plain object)
1110
+ const statusCode = error.statusCode ?? error.status;
1111
+ const status = statusCode || 500;
1112
+ logHttpRequest(null, status, requestId, performance.now() - startTime);
1113
+ if (statusCode) {
1114
+ if (statusCode === 500)
1115
+ harper_logger_ts_1.default.warn(errorForLog(error));
821
1116
  else
822
- harper_logger_ts_1.default.info(error);
1117
+ harper_logger_ts_1.default.info(errorForLog(error));
823
1118
  }
824
1119
  else
825
- harper_logger_ts_1.default.error(error);
1120
+ harper_logger_ts_1.default.error(errorForLog(error));
826
1121
  return new Response(errorToString(error), { status });
827
1122
  }
828
1123
  };
@@ -858,42 +1153,53 @@ function getBunHTTPServer(port, secure, options) {
858
1153
  * Bridge a Bun fetch request to a Node.js http.Server (e.g. Fastify) by using Fastify's inject()
859
1154
  * method to send the request through its internal router without needing a real socket.
860
1155
  */
861
- let bunFastifyInstances = {};
862
- function registerBunFastifyInstance(port, instance) {
863
- bunFastifyInstances[port] = instance;
1156
+ let fastifyInstances = {};
1157
+ function registerFastifyInstance(port, instance) {
1158
+ fastifyInstances[port] = instance;
864
1159
  }
865
1160
  const INTERNAL_USER_HEADER = 'x-harper-internal-pre-auth-user';
1161
+ /**
1162
+ * Run a request through a Fastify instance via inject() — its internal router, no socket needed.
1163
+ * Shared by the Bun and uWS fallback-delegation paths. Strips any forged pre-auth header from the
1164
+ * client and, when Harper's auth middleware resolved a user without credentials (e.g. AUTHORIZE_LOCAL
1165
+ * for loopback in dev), forwards it so Fastify can skip its own auth — only when no Authorization
1166
+ * header was supplied, otherwise Fastify's Passport validates the credentials normally.
1167
+ * `payloadAsStream` makes inject() resolve as soon as the response headers are written and exposes
1168
+ * the body as a Readable, so a long-lived SSE response (the MCP server-push GET) streams instead of
1169
+ * buffering forever.
1170
+ */
1171
+ function injectToFastify(fastify, req) {
1172
+ const headers = {};
1173
+ for (const key in req.headers) {
1174
+ if (key.toLowerCase() !== INTERNAL_USER_HEADER)
1175
+ headers[key] = req.headers[key];
1176
+ }
1177
+ // Both callers pass already-lowercased header keys (uWS lowercases at the protocol level →
1178
+ // RequestHeaders.asObject; Bun's webRequest.headers.forEach yields lowercase), so the literal
1179
+ // 'authorization' lookup is reliable — the pre-auth user is only forwarded when the client sent
1180
+ // no credentials of its own.
1181
+ if (req.user && !headers['authorization']) {
1182
+ headers[INTERNAL_USER_HEADER] = JSON.stringify(req.user);
1183
+ }
1184
+ return fastify.inject({ method: req.method, url: req.url, headers, payload: req.body, payloadAsStream: true });
1185
+ }
866
1186
  async function bunDelegateToNodeServer(nodeServer, webRequest, bunRequest) {
867
1187
  // Check if there's a Fastify instance registered for this port (preferred path)
868
- for (const port in bunFallbackServers) {
869
- if (bunFallbackServers[port] === nodeServer && bunFastifyInstances[port]) {
870
- const fastify = bunFastifyInstances[port];
1188
+ for (const port in fallbackServers) {
1189
+ if (fallbackServers[port] === nodeServer && fastifyInstances[port]) {
1190
+ const fastify = fastifyInstances[port];
871
1191
  const url = new URL(webRequest.url);
872
1192
  const body = webRequest.body ? Buffer.from(await webRequest.arrayBuffer()) : undefined;
873
1193
  const headers = {};
874
1194
  webRequest.headers.forEach((value, key) => {
875
- // Strip any forged pre-auth header from real clients
876
- if (key.toLowerCase() !== INTERNAL_USER_HEADER)
877
- headers[key] = value;
1195
+ headers[key] = value;
878
1196
  });
879
- // If Harper's auth middleware authenticated this request without credentials (e.g. via
880
- // AUTHORIZE_LOCAL for loopback connections in dev mode), pass the user so Fastify can
881
- // skip its own auth. Only applies when there is no Authorization header — if credentials
882
- // were provided, let Fastify's Passport validate them normally.
883
- if (bunRequest?.user && !headers['authorization']) {
884
- headers[INTERNAL_USER_HEADER] = JSON.stringify(bunRequest.user);
885
- }
886
- // `payloadAsStream` makes inject() resolve as soon as the response headers are
887
- // written and exposes the body as a Readable, instead of buffering the whole
888
- // payload and resolving only on response end. Without it a long-lived SSE
889
- // response (the MCP server-push GET) never ends, so `await inject()` would
890
- // hang forever and the client would never receive headers.
891
- const injectResult = await fastify.inject({
1197
+ const injectResult = await injectToFastify(fastify, {
892
1198
  method: webRequest.method,
893
1199
  url: url.pathname + url.search,
894
1200
  headers,
895
- payload: body,
896
- payloadAsStream: true,
1201
+ body,
1202
+ user: bunRequest?.user,
897
1203
  });
898
1204
  const webHeaders = new globalThis.Headers();
899
1205
  for (const [k, v] of Object.entries(injectResult.headers)) {
@@ -940,14 +1246,68 @@ async function bunDelegateToNodeServer(nodeServer, webRequest, bunRequest) {
940
1246
  // No Fastify instance found — return 404
941
1247
  return new Response('Not found\n', { status: 404 });
942
1248
  }
943
- function makeCallbackChain(responders, portNum, requestArgIndex = 0) {
944
- return (0, middlewareChain_ts_1.makeCallbackChain)(responders, portNum, unhandled, () => {
945
- harper_logger_ts_1.default.warn(`Cycle detected in middleware before/after ordering on port ${portNum}; falling back to registration order.`);
946
- }, requestArgIndex);
1249
+ // Resolved order captured at chain-build time, keyed identically to httpChain/upgradeChains/
1250
+ // websocketChains (kind port → routes). Reporting the stored build-time order rather than
1251
+ // recomputing from current responders guarantees get_status matches the callback chain actually
1252
+ // serving that port — including cases where a late `port: 'all'` registration rebuilds only the
1253
+ // 'all' chain and leaves a concrete port's chain (and this description) unchanged (#1573).
1254
+ const resolvedChainDescriptions = {
1255
+ http: {},
1256
+ upgrade: {},
1257
+ websocket: {},
1258
+ };
1259
+ function makeCallbackChain(responders, portNum, requestArgIndex = 0, kind = 'http') {
1260
+ const onCycle = () => {
1261
+ harper_logger_ts_1.default.warn(`Cycle detected in ${kind} middleware before/after ordering on port ${portNum}; falling back to registration order.`);
1262
+ };
1263
+ // describeChains reuses the same resolvers as buildCallbackChain, so this is the served order.
1264
+ // onCycle is omitted: the build call owns the single cycle warning, and on a cycle describeChains
1265
+ // falls back to registration order exactly as the built chain does.
1266
+ const routes = (0, middlewareChain_ts_1.describeChains)(responders, portNum).map((route) => ({
1267
+ host: route.host,
1268
+ urlPath: route.urlPath,
1269
+ order: route.order.map((entry) => entry.name ?? '(anonymous)'),
1270
+ }));
1271
+ resolvedChainDescriptions[kind][portNum] = routes;
1272
+ if (harper_logger_ts_1.default.debug) {
1273
+ for (const route of routes) {
1274
+ const scope = route.host || route.urlPath ? ` [${route.host ?? '*'}${route.urlPath ?? ''}]` : '';
1275
+ harper_logger_ts_1.default.debug(`Resolved ${kind} middleware chain on port ${portNum}${scope}: ${route.order.join(' → ') || '(empty)'}`);
1276
+ }
1277
+ }
1278
+ return (0, middlewareChain_ts_1.makeCallbackChain)(responders, portNum, unhandled, onCycle, requestArgIndex, ({ entryName, kind: refKind, target }) => {
1279
+ harper_logger_ts_1.default.warn(`Middleware ordering: ${entryName ? `'${entryName}'` : 'a handler'} requested \`${refKind}: '${target}'\` but no handler named '${target}' is registered on port ${portNum}, so the constraint is ignored. Handler names are the config keys as registered (e.g. 'rest').`);
1280
+ });
1281
+ }
1282
+ /**
1283
+ * Returns the resolved middleware order for every built HTTP, upgrade, and WebSocket chain on the
1284
+ * current thread, as plain serializable data (listeners omitted). Surfaced via the `get_status`
1285
+ * operation so chain placement can be verified on a running instance (#1573). The 'all' pseudo-port
1286
+ * is excluded: makeCallbackChain builds a chain for it, but it is not a bound listener (its
1287
+ * responders already fold into every concrete port's chain).
1288
+ *
1289
+ * Note: this reflects the calling thread's built chains. All HTTP worker threads register
1290
+ * identically, so any worker's view is representative.
1291
+ */
1292
+ function describeMiddlewareChains() {
1293
+ const concretePorts = (byPort) => {
1294
+ const out = {};
1295
+ for (const port of Object.keys(byPort))
1296
+ if (port !== 'all')
1297
+ out[port] = byPort[port];
1298
+ return out;
1299
+ };
1300
+ return {
1301
+ http: concretePorts(resolvedChainDescriptions.http),
1302
+ upgrade: concretePorts(resolvedChainDescriptions.upgrade),
1303
+ websocket: concretePorts(resolvedChainDescriptions.websocket),
1304
+ };
947
1305
  }
948
1306
  function unhandled(request) {
949
- if (request.user) {
950
- // pass on authentication information to the next server
1307
+ if (request.user && request._nodeRequest) {
1308
+ // pass on authentication information to the next server (Node fallback delegation via the
1309
+ // 'unhandled' event chain). The Bun/uWS adapters have no _nodeRequest; they forward the
1310
+ // resolved user to the Fastify fallback via injectToFastify's INTERNAL_USER_HEADER instead.
951
1311
  request._nodeRequest.user = request.user;
952
1312
  }
953
1313
  return {
@@ -982,7 +1342,7 @@ function onUpgrade(listener, options) {
982
1342
  host: options?.host || undefined,
983
1343
  };
984
1344
  upgradeListeners[options?.runFirst ? 'unshift' : 'push'](entry);
985
- upgradeChains[port] = makeCallbackChain(upgradeListeners, port);
1345
+ upgradeChains[port] = makeCallbackChain(upgradeListeners, port, 0, 'upgrade');
986
1346
  }
987
1347
  }
988
1348
  const websocketListeners = [], websocketChains = {};
@@ -1000,7 +1360,39 @@ function onWebSocket(listener, options) {
1000
1360
  name: (0, componentLoader_ts_1.getComponentName)(),
1001
1361
  });
1002
1362
  const server = getHTTPServer(port, secure, options);
1003
- if (!websocketServers[port]) {
1363
+ if (server?.uws) {
1364
+ // uWS-backed port (HARPER_UWS_HTTP): uWS owns the socket, so route upgrades through uWS's
1365
+ // native app.ws() rather than the Node ws.WebSocketServer + server 'upgrade' event. We wire a
1366
+ // wsHandler into the shared uwsServeConfig; createUwsServer registers app.ws() when it listens.
1367
+ const cfg = exports.uwsServeConfigs[port];
1368
+ if (cfg && !cfg.wsHandler) {
1369
+ // Honor a configured WebSocket maxPayload on the uWS transport too (else it defaults to 100 MiB).
1370
+ if (options.maxPayload != null)
1371
+ cfg.wsMaxPayload = options.maxPayload;
1372
+ cfg.wsHandler = (ws, upgrade) => {
1373
+ try {
1374
+ const request = new Request_ts_1.UwsRequest({
1375
+ method: 'GET',
1376
+ url: upgrade.url,
1377
+ headers: upgrade.headers,
1378
+ secure,
1379
+ ip: upgrade.ip,
1380
+ });
1381
+ request.isWebSocket = true;
1382
+ const chainCompletion = httpChain[port](request);
1383
+ websocketChains[port](ws, request, chainCompletion);
1384
+ }
1385
+ catch (error) {
1386
+ harper_logger_ts_1.default.warn('Error in handling WS connection', error);
1387
+ try {
1388
+ ws.close();
1389
+ }
1390
+ catch { }
1391
+ }
1392
+ };
1393
+ }
1394
+ }
1395
+ else if (!websocketServers[port]) {
1004
1396
  websocketServers[port] = new ws_1.WebSocketServer({
1005
1397
  noServer: true,
1006
1398
  // TODO: this should be a global config and not per ws listener
@@ -1050,7 +1442,7 @@ function onWebSocket(listener, options) {
1050
1442
  host: options?.host || undefined,
1051
1443
  };
1052
1444
  websocketListeners[options?.runFirst ? 'unshift' : 'push'](wsEntry);
1053
- websocketChains[port] = makeCallbackChain(websocketListeners, port, 1);
1445
+ websocketChains[port] = makeCallbackChain(websocketListeners, port, 1, 'websocket');
1054
1446
  // mqtt doesn't invoke the http handler so this needs to be here to load up the http chains.
1055
1447
  httpChain[port] = makeCallbackChain(httpResponders, port);
1056
1448
  }
@@ -1128,6 +1520,97 @@ function enableProxyProtocol(httpServer) {
1128
1520
  });
1129
1521
  });
1130
1522
  }
1523
+ /**
1524
+ * Front a cleartext HTTP/2 server with optional PROXY v1 handling on a Unix domain socket.
1525
+ *
1526
+ * enableProxyProtocol() can't be used here: Node's Http2Session consumes the socket's
1527
+ * native handle directly, so data never surfaces as JS 'data' events to intercept. The
1528
+ * PROXY header must instead be consumed *before* the socket is handed to the HTTP/2
1529
+ * server. Bytes beyond the header (typically the coalesced h2 connection preface) are
1530
+ * unshifted back onto the socket; the native session picks them up (verified on Node 24
1531
+ * — covered by a unit test so a Node upgrade regressing this fails loudly).
1532
+ *
1533
+ * The returned server's close() also gracefully closes live h2 sessions (GOAWAY,
1534
+ * in-flight streams finish) so closeServers()'s generic server.close() drains instead
1535
+ * of riding its 5s force-exit backstop — the h1 mirror gets this via http.Server's
1536
+ * closeIdleConnections drain, which a net.Server doesn't have.
1537
+ */
1538
+ function createH2CProxyFront(h2Server, prehandoffTimeout = 10_000) {
1539
+ const sessions = new Set();
1540
+ const prehandoffSockets = new Set();
1541
+ let closing = false;
1542
+ h2Server.on('session', (session) => {
1543
+ // A connection can be mid-handoff (header read, session not yet created) when
1544
+ // close() runs — its session forms after the close sweep, so close it here or
1545
+ // it would never receive GOAWAY and would ride the 5s force-exit backstop.
1546
+ if (closing)
1547
+ session.close();
1548
+ sessions.add(session);
1549
+ session.on('close', () => sessions.delete(session));
1550
+ });
1551
+ const front = (0, node_net_2.createServer)({ noDelay: true }, (socket) => {
1552
+ let buf = null;
1553
+ // Until handoff the h2 session's own handlers aren't attached yet: swallow socket
1554
+ // errors (a reset mid-header) and bound how long we'll wait for the header, so a
1555
+ // stalled connection can't hold an fd forever.
1556
+ prehandoffSockets.add(socket);
1557
+ socket.on('close', () => prehandoffSockets.delete(socket));
1558
+ const onPrehandoffError = () => socket.destroy();
1559
+ socket.on('error', onPrehandoffError);
1560
+ const onPrehandoffTimeout = () => socket.destroy();
1561
+ socket.setTimeout(prehandoffTimeout, onPrehandoffTimeout);
1562
+ const handoff = (rest) => {
1563
+ prehandoffSockets.delete(socket);
1564
+ socket.removeListener('readable', onReadable);
1565
+ socket.removeListener('error', onPrehandoffError);
1566
+ socket.setTimeout(0);
1567
+ socket.removeListener('timeout', onPrehandoffTimeout);
1568
+ if (rest.length > 0)
1569
+ socket.unshift(rest);
1570
+ h2Server.emit('connection', socket);
1571
+ };
1572
+ const onReadable = () => {
1573
+ let chunk;
1574
+ while ((chunk = socket.read()) !== null) {
1575
+ buf = buf ? Buffer.concat([buf, chunk]) : chunk;
1576
+ // Compare against "PROXY " for as many bytes as we have so far; a non-PROXY
1577
+ // prefix (e.g. a direct h2 client with no fronting proxy) is handed off as-is.
1578
+ const cmpLen = Math.min(PROXY_V1_PREFIX.length, buf.length);
1579
+ if (buf.compare(PROXY_V1_PREFIX, 0, cmpLen, 0, cmpLen) !== 0)
1580
+ return handoff(buf);
1581
+ const eol = buf.indexOf('\r\n');
1582
+ if (eol !== -1) {
1583
+ // Complete header: "PROXY TCP4 <src-ip> <dst-ip> <src-port> <dst-port>"
1584
+ const parts = buf.toString('latin1', 0, eol).split(' ');
1585
+ if (parts.length === 6) {
1586
+ // Override the UDS socket's undefined remoteAddress/remotePort with the real
1587
+ // client values; http2's compat req.socket proxies through to these.
1588
+ Object.defineProperty(socket, 'remoteAddress', { value: parts[2], configurable: true });
1589
+ Object.defineProperty(socket, 'remotePort', { value: parseInt(parts[4], 10), configurable: true });
1590
+ }
1591
+ return handoff(buf.subarray(eol + 2));
1592
+ }
1593
+ // No CRLF within the spec max — not a valid PROXY header after all.
1594
+ if (buf.length >= PROXY_V1_MAX_HEADER)
1595
+ return handoff(buf);
1596
+ }
1597
+ };
1598
+ socket.on('readable', onReadable);
1599
+ });
1600
+ const netClose = front.close.bind(front);
1601
+ front.close = (callback) => {
1602
+ closing = true;
1603
+ for (const session of sessions)
1604
+ session.close();
1605
+ // Header-waiting sockets carry no in-flight work; drop them so they can't hold
1606
+ // the close callback open for the rest of the pre-handoff timeout.
1607
+ for (const socket of prehandoffSockets)
1608
+ socket.destroy();
1609
+ h2Server.close();
1610
+ return netClose(callback);
1611
+ };
1612
+ return front;
1613
+ }
1131
1614
  function defaultNotFound(request, response) {
1132
1615
  if (response.headersSent || response.writableEnded)
1133
1616
  return;
@@ -1136,7 +1619,7 @@ function defaultNotFound(request, response) {
1136
1619
  logRequest(request, 404, 0, request.requestId);
1137
1620
  }
1138
1621
  let httpLogger;
1139
- function logBunRequest(request, status, requestId, executionTime) {
1622
+ function logHttpRequest(request, status, requestId, executionTime) {
1140
1623
  const logging = httpOptions.logging;
1141
1624
  if (logging) {
1142
1625
  if (!httpLogger) {