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
package/security/auth.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { getSuperUser } from './user.ts';
2
2
  import { server } from '../server/Server.ts';
3
3
  import { resources } from '../resources/Resources.ts';
4
- import { validateOperationToken, validateRefreshToken } from './tokenAuthentication.ts';
4
+ import { validateOperationToken, validateRefreshToken, validateLoginToken, decodeJWT } from './tokenAuthentication.ts';
5
5
  import { table, type Table } from '../resources/databases.ts';
6
6
  import { v4 as uuid } from 'uuid';
7
7
  import * as env from '../utility/environment/environmentManager.ts';
@@ -10,7 +10,7 @@ import harperLogger from '../utility/logging/harper_logger.ts';
10
10
  const { forComponent, AuthAuditLog } = harperLogger;
11
11
  import serverHandlers from '../server/itc/serverHandlers.js';
12
12
  const { user } = serverHandlers;
13
- import { Headers } from '../server/serverHelpers/Headers.ts';
13
+ import { Headers, addVaryHeader } from '../server/serverHelpers/Headers.ts';
14
14
  import { convertToMS } from '../utility/common_utils.ts';
15
15
  import { verifyCertificate } from './certificateVerification/index.ts';
16
16
  import { serializeMessage } from '../server/serverHelpers/contentTypes.ts';
@@ -33,16 +33,27 @@ function getSessionTable() {
33
33
  return _sessionTable;
34
34
  }
35
35
  const ENABLE_SESSIONS = env.get(CONFIG_PARAMS.AUTHENTICATION_ENABLESESSIONS) ?? true;
36
+ // env-var strings need boolean parsing: a raw 'false'/'0' string is truthy, which would turn
37
+ // AUTHENTICATION_AUTHORIZELOCAL=false into an *enabled* auth bypass
38
+ function envFlag(value: string | undefined): boolean | undefined {
39
+ if (value === undefined) return undefined;
40
+ const normalized = value.trim().toLowerCase();
41
+ return normalized !== 'false' && normalized !== '0' && normalized !== '';
42
+ }
36
43
  // check the environment for a flag to bypass authentication (for testing) since it doesn't necessarily get set on child threads
37
44
  let AUTHORIZE_LOCAL =
38
- process.env.AUTHENTICATION_AUTHORIZELOCAL ??
45
+ envFlag(process.env.AUTHENTICATION_AUTHORIZELOCAL) ??
39
46
  env.get(CONFIG_PARAMS.AUTHENTICATION_AUTHORIZELOCAL) ??
40
- process.env.DEV_MODE;
47
+ envFlag(process.env.DEV_MODE);
41
48
  const LOG_AUTH_SUCCESSFUL = env.get(CONFIG_PARAMS.LOGGING_AUDITAUTHEVENTS_LOGSUCCESSFUL) ?? false;
42
49
  const LOG_AUTH_FAILED = env.get(CONFIG_PARAMS.LOGGING_AUDITAUTHEVENTS_LOGFAILED) ?? false;
43
50
 
44
51
  const DEFAULT_COOKIE_EXPIRES = 'Tue, 01 Oct 8307 19:33:20 GMT';
45
52
 
53
+ // RFC 9111 cache-scope directives; boundaries on both sides so a token like `public-foo` doesn't match
54
+ const SHARED_CACHE_OPTIN = /(^|[,\s])(public|s-maxage)($|[\s,;=])/i;
55
+ const PRIVATE_SCOPE = /(^|[,\s])(private|no-store)($|[\s,;=])/i;
56
+
46
57
  let authorizationCache = new Map();
47
58
  server.onInvalidatedUser(() => {
48
59
  // TODO: Eventually we probably want to be able to invalidate individual users
@@ -61,6 +72,9 @@ export async function authentication(request, nextHandler) {
61
72
  const cookie = headers.cookie;
62
73
  let origin = headers.origin;
63
74
  let responseHeaders = [];
75
+ // a 401 that gets rewritten to a login-redirect 302 below must still be treated as
76
+ // identity-dependent by applyResponseHeaders
77
+ let wasUnauthorized = false;
64
78
  try {
65
79
  if (origin) {
66
80
  const accessList = request.isOperationsServer
@@ -80,6 +94,8 @@ export async function authentication(request, nextHandler) {
80
94
  ['Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, PATCH, OPTIONS'],
81
95
  ['Access-Control-Allow-Headers', accessControlAllowHeaders],
82
96
  ['Access-Control-Allow-Origin', origin],
97
+ // the preflight response is keyed on the request Origin — a shared cache must partition on it
98
+ ['Vary', 'Origin'],
83
99
  ]);
84
100
  if (ENABLE_SESSIONS) headers.set('Access-Control-Allow-Credentials', 'true');
85
101
  return {
@@ -201,6 +217,10 @@ export async function authentication(request, nextHandler) {
201
217
  case 'Bearer':
202
218
  try {
203
219
  newUser = await validateOperationToken(credentials);
220
+ // Capture the token's expiry so a live subscription opened with this bearer
221
+ // token can be revoked once it expires (#1414).
222
+ const decoded = decodeJWT(credentials);
223
+ if (newUser && decoded?.exp) newUser.authExpiresAt = decoded.exp;
204
224
  } catch (error) {
205
225
  if (error.message === 'invalid token') {
206
226
  // see if they provided a refresh token; we can allow that and pass it on to operations API
@@ -315,14 +335,17 @@ export async function authentication(request, nextHandler) {
315
335
  expiresAt: expires ? Date.now() + convertToMS(expires) : undefined,
316
336
  });
317
337
  };
318
- request.login = async function (username: string, password: string) {
319
- const user: any = (request.user = await server.authenticateUser(username, password, request));
338
+ request.login = async function (username: string, password?: string, token?: string) {
339
+ const user: any = (request.user = token
340
+ ? await validateLoginToken(token)
341
+ : await server.authenticateUser(username, password, request));
320
342
  request.session.update({ user: user && (user.getId?.() ?? user.username) });
321
343
  };
322
344
  }
323
345
  const response = await nextHandler(request);
324
346
  if (!response) return response;
325
347
  if (response.status === 401) {
348
+ wasUnauthorized = true;
326
349
  if (
327
350
  headers['user-agent']?.startsWith('Mozilla') &&
328
351
  headers.accept?.startsWith('text/html') &&
@@ -340,13 +363,50 @@ export async function authentication(request, nextHandler) {
340
363
  }
341
364
  function applyResponseHeaders(response) {
342
365
  const l = responseHeaders.length;
343
- if (l > 0) {
366
+ // a response is identity-dependent if a principal was resolved (Authorization/session/mTLS) or
367
+ // if we are rejecting the credentials (401, possibly rewritten to a login redirect); such a
368
+ // response must never be stored by a shared cache and served to a different principal (#1565)
369
+ const rejectedAuth = response?.status === 401 || wasUnauthorized;
370
+ const identityDependent = !!request.user || rejectedAuth;
371
+ // with CORS enabled the response is origin-dependent — ACAO reflects the request Origin, and
372
+ // its absence when no Origin was sent is origin-dependent too — so a shared cache must
373
+ // partition on Origin either way (#1518)
374
+ const corsEnabled = request.isOperationsServer ? operationsCors : appsCors;
375
+ if ((l > 0 || corsEnabled || identityDependent) && typeof response === 'object') {
344
376
  let headers = response.headers;
345
- if (!headers) response.headers = headers = new Headers();
346
- for (let i = 0; i < l; ) {
377
+ // normalize plain-object headers so get/set below work uniformly
378
+ if (!headers || typeof headers.set !== 'function') response.headers = headers = new Headers(headers);
379
+ for (let i = 0; i < l;) {
347
380
  const name = responseHeaders[i++];
348
381
  headers.set(name, responseHeaders[i++]);
349
382
  }
383
+ if (corsEnabled) addVaryHeader(headers, 'Origin');
384
+ // #1565: keep identity-dependent responses out of shared caches. An explicit
385
+ // `public`/`s-maxage` set by the app is RFC 9111's opt-in for shared-caching an
386
+ // authenticated response, so that is trusted as-is — but only for successfully
387
+ // authenticated responses; a credential rejection must never opt into shared caching.
388
+ // Otherwise partition on the credential headers and ensure a `private` scope
389
+ // (`private, no-cache` when none was set, so browsers still revalidate against the
390
+ // ETag/Last-Modified we emit).
391
+ if (identityDependent) {
392
+ const existingCacheControl = headers.get('Cache-Control');
393
+ const cacheControlString = existingCacheControl
394
+ ? Array.isArray(existingCacheControl)
395
+ ? existingCacheControl.join(', ')
396
+ : existingCacheControl
397
+ : '';
398
+ const optedIn = !rejectedAuth && SHARED_CACHE_OPTIN.test(cacheControlString);
399
+ if (!optedIn) {
400
+ addVaryHeader(headers, 'Authorization');
401
+ if (ENABLE_SESSIONS) addVaryHeader(headers, 'Cookie');
402
+ // a 401 never opts into shared caching: any non-private Cache-Control it carries is
403
+ // replaced outright (an existing private/no-store is already at least as strict)
404
+ if (!cacheControlString || (rejectedAuth && !PRIVATE_SCOPE.test(cacheControlString)))
405
+ headers.set('Cache-Control', 'private, no-cache');
406
+ else if (!PRIVATE_SCOPE.test(cacheControlString))
407
+ headers.set('Cache-Control', cacheControlString + ', private');
408
+ }
409
+ }
350
410
  }
351
411
  responseHeaders = null;
352
412
  return response;
@@ -374,7 +434,7 @@ export async function login(loginObject) {
374
434
  loginObject.baseResponse.headers.set = (name, value) => {
375
435
  loginObject.fastifyResponse.header(name, value);
376
436
  };
377
- await loginObject.baseRequest.login(loginObject.username, loginObject.password ?? '');
437
+ await loginObject.baseRequest.login(loginObject.username, loginObject.password ?? '', loginObject.token);
378
438
  return 'Login successful';
379
439
  }
380
440
 
@@ -8,6 +8,7 @@ import { dirname, isAbsolute } from 'node:path';
8
8
  import { pathToFileURL, fileURLToPath } from 'node:url';
9
9
  import { SourceTextModule, SyntheticModule, createContext, runInContext, runInThisContext } from 'node:vm';
10
10
  import { ApplicationScope } from '../components/ApplicationScope.ts';
11
+ import { getSecretsForComponent, runWithComponentBinding } from '../components/componentSecrets.ts';
11
12
  import logger from '../utility/logging/harper_logger.ts';
12
13
  import { createRequire } from 'node:module';
13
14
  import * as env from '../utility/environment/environmentManager';
@@ -76,6 +77,13 @@ export async function scopedImport(filePath: string | URL, scope?: ApplicationSc
76
77
  }
77
78
  }
78
79
  const moduleUrl = (filePath instanceof URL ? filePath : pathToFileURL(filePath)).toString();
80
+ // Bind the component-load context for the duration of the load, so the process-wide `secrets`
81
+ // export (reached under the native loader, or by natively-loaded dependencies that import the
82
+ // real `harper` package) resolves to this scope's component while its modules evaluate.
83
+ return runWithComponentBinding(scope?.name, () => importScoped(moduleUrl, scope));
84
+ }
85
+
86
+ async function importScoped(moduleUrl: string, scope?: ApplicationScope) {
79
87
  try {
80
88
  const loaderMode = scope?.mode;
81
89
  if (scope && loaderMode !== 'native') {
@@ -773,6 +781,7 @@ function getGlobalObject(scope: ApplicationScope, copyIntrinsics = false) {
773
781
  logger: scope.logger ?? logger,
774
782
  resources: scope.resources,
775
783
  config: scope.config ?? {},
784
+ secrets: getSecretsForComponent(scope.name),
776
785
  fetch: APPLICATIONS_LOCKDOWN === 'ses' ? secureOnlyFetch : fetch,
777
786
  console,
778
787
  global: appGlobal,
@@ -790,6 +799,9 @@ function getHarperExports(scope: ApplicationScope) {
790
799
  logger: scope.logger ?? logger,
791
800
  resources: scope.resources,
792
801
  config: scope.config ?? {},
802
+ // The scoped-secrets view (#1550), bound exactly to this scope's component — per-scope, unlike
803
+ // the process-wide singletons below.
804
+ secrets: getSecretsForComponent(scope.name),
793
805
  Resource,
794
806
  tables,
795
807
  databases,
@@ -25,11 +25,20 @@ env.initSync();
25
25
  type StringValue = SignOptions['expiresIn'];
26
26
  const OPERATION_TOKEN_TIMEOUT: StringValue = env.get(CONFIG_PARAMS.AUTHENTICATION_OPERATIONTOKENTIMEOUT) || '1d';
27
27
  const REFRESH_TOKEN_TIMEOUT: StringValue = env.get(CONFIG_PARAMS.AUTHENTICATION_REFRESHTOKENTIMEOUT) || '30d';
28
+ // Default lifetime of a login-purpose token (see TOKEN_TYPE.LOGIN below). It only exists to be
29
+ // exchanged for a session cookie, so it defaults far shorter than an operation token; callers can
30
+ // still override via expires_in.
31
+ const LOGIN_TOKEN_TIMEOUT: StringValue = '1m';
28
32
  const RSA_ALGORITHM: Algorithm = 'RS256';
29
33
 
30
34
  const TOKEN_TYPE = {
31
35
  OPERATION: 'operation',
32
36
  REFRESH: 'refresh',
37
+ // Purpose-scoped exchange token minted by createTokens({ purpose: 'login' }) and accepted only
38
+ // by validateLoginToken (the `login` operation). Its `sub` claim differs from
39
+ // TOKEN_TYPE.OPERATION, so validateOperationToken's Bearer-API path rejects it automatically —
40
+ // it can't be replayed as a general API credential the way a full operation token could.
41
+ LOGIN: 'login',
33
42
  };
34
43
 
35
44
  interface JWTRSAKeys {
@@ -45,6 +54,9 @@ interface AuthObject {
45
54
  expires_in?: string | number;
46
55
  bypass_auth?: boolean;
47
56
  hdb_user?: User;
57
+ // 'login' mints a single short-lived, login-scoped token instead of an operation/refresh pair —
58
+ // see TOKEN_TYPE.LOGIN.
59
+ purpose?: 'login';
48
60
  }
49
61
 
50
62
  interface TokenObject {
@@ -52,6 +64,8 @@ interface TokenObject {
52
64
  }
53
65
 
54
66
  interface JWTTokens {
67
+ // Always the mint result: an operation JWT normally, or (purpose: 'login') the login-scoped
68
+ // exchange JWT — same field, same response shape, callers don't need to branch on purpose.
55
69
  operation_token: string;
56
70
  refresh_token?: string;
57
71
  }
@@ -99,7 +113,8 @@ export function clearJWTRSAKeysCache(): void {
99
113
  }
100
114
 
101
115
  /**
102
- * Creates a new operation token and refresh token.
116
+ * Creates a new operation token and refresh token (or, with `purpose: 'login'`, a single
117
+ * login-scoped token — see TOKEN_TYPE.LOGIN).
103
118
  * If there is no username and password, the hdb_user making the request is used in the token.
104
119
  * An optional role can be provided which will be saved in the token payload.
105
120
  * The token expires in the time specified in the expires_in field or the default time.
@@ -113,6 +128,7 @@ export async function createTokens(authObj: AuthObject): Promise<JWTTokens> {
113
128
  password: Joi.string().optional(),
114
129
  role: Joi.string().optional(),
115
130
  expires_in: Joi.alternatives(Joi.string(), Joi.number()).optional(),
131
+ purpose: Joi.string().valid('login').optional(),
116
132
  })
117
133
  );
118
134
  if (validation) throw new ClientError(validation.message);
@@ -147,6 +163,22 @@ export async function createTokens(authObj: AuthObject): Promise<JWTTokens> {
147
163
  if (authObj.role) payload.role = authObj.role;
148
164
 
149
165
  const keys: JWTRSAKeys = await getJWTRSAKeys();
166
+
167
+ if (authObj.purpose === 'login') {
168
+ // Login-scoped exchange token: no refresh token, no user record update — it's a one-shot
169
+ // ticket for the `login` operation to trade for a session cookie, not a standing credential.
170
+ const loginToken = jwt.sign(
171
+ { username: authObj.username },
172
+ { key: keys.privateKey, passphrase: keys.passphrase } satisfies Secret,
173
+ {
174
+ expiresIn: (authObj.expires_in ?? LOGIN_TOKEN_TIMEOUT) as StringValue,
175
+ algorithm: RSA_ALGORITHM,
176
+ subject: TOKEN_TYPE.LOGIN,
177
+ } satisfies SignOptions
178
+ );
179
+ return { operation_token: loginToken };
180
+ }
181
+
150
182
  const operationToken = jwt.sign(
151
183
  payload,
152
184
  { key: keys.privateKey, passphrase: keys.passphrase } satisfies Secret,
@@ -219,6 +251,15 @@ export async function validateRefreshToken(token: string): Promise<any> {
219
251
  return validateToken(token, TOKEN_TYPE.REFRESH);
220
252
  }
221
253
 
254
+ /**
255
+ * Validates a login-purpose token minted via createTokens({ purpose: 'login' }). Used solely by
256
+ * the `login` operation to exchange the token for an httpOnly session cookie — this is the only
257
+ * place a `sub: 'login'` token is accepted.
258
+ */
259
+ export async function validateLoginToken(token: string): Promise<any> {
260
+ return validateToken(token, TOKEN_TYPE.LOGIN);
261
+ }
262
+
222
263
  async function validateToken(token: string, tokenType: string): Promise<any> {
223
264
  try {
224
265
  const keys: JWTRSAKeys = await getJWTRSAKeys();
package/server/DESIGN.md CHANGED
@@ -120,6 +120,16 @@ The default WebSocket upgrade handler is registered automatically inside `onWebS
120
120
 
121
121
  `REST.ts → http(request, nextHandler)` is the chief integration point: it takes a `Request`, asks the `Resources` registry for a match, builds a `RequestTarget`, and dispatches into the Resource class's static method. Cache headers are translated to `request.expiresAt` / `onlyIfCached` / `noCache` flags within the same function.
122
122
 
123
+ ### Response Cache-Control / Vary policy (#1518, #1565)
124
+
125
+ Three tiers, applied in two places:
126
+
127
+ 1. **App/resource explicit** — a `Cache-Control` set by the resource (or `@table(cacheControl: "...")` for anonymous reads, emitted in `REST.ts → http()`) always wins. The declaration is required: anonymous readability alone never emits shared-cache headers, because a request-attribute-gated `allowRead` (IP, headers) would make inferred `public` unsound.
128
+ 2. **Identity floor** — `security/auth.ts → applyResponseHeaders` stamps `Cache-Control: private, no-cache` + `Vary: Authorization` (+ `Cookie` when sessions are on) on any response where a principal was resolved or credentials were rejected (401), _unless_ the app opted into shared caching with `public`/`s-maxage` (the RFC 9111 opt-in).
129
+ 3. **CORS partitioning** — when CORS is enabled, every response gets `Vary: Origin` (the ACAO header is reflected per-origin, and its absence on no-Origin requests is origin-dependent too).
130
+
131
+ The `@table(cacheControl:)` value is persisted on the primary-key attribute (like `expiration`), so all threads and future boots see it; `resources/databases.ts → table()` treats `null` as "schema explicitly has none" (clears on reload) and `undefined` as "caller is not schema-defining" (no clobber from `add_attribute`/cluster schema events).
132
+
123
133
  ---
124
134
 
125
135
  ## "Where is X" cheat sheet
@@ -126,7 +126,11 @@ export async function getSession({
126
126
  if (will) {
127
127
  will.id = sessionId;
128
128
  will.user = { username: user?.username };
129
- getLastWill().put(will);
129
+ // Must be durably persisted before CONNACK is sent (getSession() resolving is what lets
130
+ // mqtt.ts send CONNACK). Otherwise a client that connects and then disconnects abruptly
131
+ // (no DISCONNECT packet) can race ahead of this write: SubscriptionsSession.disconnect()
132
+ // reads this same record back to publish the will, finds nothing, and silently drops it.
133
+ await getLastWill().put(will);
130
134
  }
131
135
  if (keepalive) {
132
136
  // keep alive is the interval in seconds that the client will send a ping to the server
@@ -480,8 +484,8 @@ export class DurableSubscriptionsSession extends SubscriptionsSession {
480
484
  }
481
485
  subscription.acks.push(update.timestamp);
482
486
  trace('Received ack', topic, update.timestamp);
483
- getDurableSession().put(this.sessionRecord, { source: true }); // add source: true context to bypass any overloaded checks, as skipping this can lead to increased load
484
- return;
487
+ // add source: true context to bypass any overloaded checks, as skipping this can lead to increased load
488
+ return getDurableSession().put(this.sessionRecord, { source: true });
485
489
  }
486
490
  }
487
491
  }
@@ -493,7 +497,7 @@ export class DurableSubscriptionsSession extends SubscriptionsSession {
493
497
  subscription.startTime = update.timestamp;
494
498
  }
495
499
  }
496
- getDurableSession().put(this.sessionRecord, { source: true });
500
+ return getDurableSession().put(this.sessionRecord, { source: true });
497
501
  // TODO: Increment the timestamp for the corresponding subscription, possibly recording any interim unacked messages
498
502
  }
499
503
 
package/server/REST.ts CHANGED
@@ -13,7 +13,7 @@ import { Request } from '../server/serverHelpers/Request.ts';
13
13
  import { RequestTarget } from '../resources/RequestTarget';
14
14
  import { entryMap } from '../resources/RecordEncoder.ts';
15
15
 
16
- const { errorToString } = harperLogger;
16
+ const { errorToString, errorForLog } = harperLogger;
17
17
  const etagBytes = new Uint8Array(8);
18
18
  const etagFloat = new Float64Array(etagBytes.buffer, 0, 1);
19
19
  let httpOptions = {};
@@ -254,6 +254,20 @@ async function http(request: Request, nextHandler) {
254
254
  headers,
255
255
  body: undefined,
256
256
  };
257
+ // #1565 converse: a declared `@table(cacheControl: "...")`/static cacheControl is emitted on
258
+ // anonymous reads so a shared cache/CDN can store public responses. Emission requires the
259
+ // explicit declaration — "the anonymous request succeeded" alone doesn't establish that the
260
+ // content is uniformly public (an allowRead gated on IP or headers would leak across a
261
+ // URL-keyed cache). Authenticated responses are excluded here — the auth layer applies a
262
+ // `private` floor to them instead.
263
+ if (!request.user && (method === 'GET' || method === 'HEAD')) {
264
+ const responseStatus = responseObject.status;
265
+ if (responseStatus === 200 || responseStatus === 304) {
266
+ const cacheControl = (resource as any)?.cacheControl;
267
+ // setIfNone: a resource-set Cache-Control (including a full no-store opt-out) always wins
268
+ if (cacheControl) headers.setIfNone('Cache-Control', cacheControl);
269
+ }
270
+ }
257
271
  const loadedFromSource = target.loadedFromSource;
258
272
  if (loadedFromSource !== undefined) {
259
273
  // this appears to be a caching table with a source
@@ -270,10 +284,13 @@ async function http(request: Request, nextHandler) {
270
284
  return responseObject;
271
285
  } catch (error) {
272
286
  error ??= new Error('Unknown error occurred');
273
- let statusCode = error.statusCode ?? request.response.status;
287
+ // a thrown Response short-circuits as the response, the same as returning one
288
+ if (error instanceof Response) return finalizeResponse(error, headers, request.response.status, request);
289
+ // the HTTP status may be carried as `statusCode` (our error classes) or `status` (e.g. a thrown plain object)
290
+ let statusCode = error.statusCode ?? error.status ?? request.response.status;
274
291
  if (statusCode) {
275
- if (statusCode === 500) harperLogger.warn(error);
276
- else harperLogger.info(error);
292
+ if (statusCode === 500) harperLogger.warn(errorForLog(error));
293
+ else harperLogger.info(errorForLog(error));
277
294
  if (statusCode === 405) {
278
295
  if (error.method) error.message += ` to handle HTTP method ${error.method.toUpperCase() || ''}`;
279
296
  if (error.allow) {
@@ -281,7 +298,7 @@ async function http(request: Request, nextHandler) {
281
298
  headers.setIfNone('Allow', error.allow.map((method) => method.toUpperCase()).join(', '));
282
299
  }
283
300
  }
284
- } else harperLogger.error(error);
301
+ } else harperLogger.error(errorForLog(error));
285
302
 
286
303
  // RFC 9457 Problem Details
287
304
  const status = statusCode || 500;
@@ -348,7 +365,7 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
348
365
  let hasError;
349
366
  (ws as any).on('error', (error) => {
350
367
  hasError = true;
351
- harperLogger.warn(error);
368
+ harperLogger.warn(errorForLog(error));
352
369
  });
353
370
  let deserializer;
354
371
  (ws as any).on('message', function message(body) {
@@ -411,9 +428,9 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
411
428
  }
412
429
  } catch (error) {
413
430
  if (error.statusCode) {
414
- if (error.statusCode === 500) harperLogger.warn(error);
415
- else harperLogger.info(error);
416
- } else harperLogger.error(error);
431
+ if (error.statusCode === 500) harperLogger.warn(errorForLog(error));
432
+ else harperLogger.info(errorForLog(error));
433
+ } else harperLogger.error(errorForLog(error));
417
434
  ws.close(
418
435
  HTTP_TO_WEBSOCKET_CLOSE_CODES[error.statusCode] || // try to return a helpful code
419
436
  1011, // otherwise generic internal error
@@ -17,6 +17,7 @@ import getHeaderTimeoutConfig from './fastifyRoutes/helpers/getHeaderTimeoutConf
17
17
  import { serverErrorHandler } from '../server/serverHelpers/serverHandlers.js';
18
18
  import { registerContentHandlers } from '../server/serverHelpers/contentTypes.ts';
19
19
  import { server } from './Server.ts';
20
+ import { registerFastifyInstance } from './http.ts';
20
21
 
21
22
  let fastifyServer;
22
23
  const routeFolders = new Set();
@@ -44,7 +45,15 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
44
45
  }
45
46
  if (!fastifyServer) {
46
47
  fastifyServer = buildServer(isHttps);
47
- server.http((await fastifyServer).server);
48
+ const built = await fastifyServer;
49
+ server.http(built.server);
50
+ // Register the Fastify app for the http port(s). On plain Node the http.Server above
51
+ // cascades unhandled requests via the 'unhandled' event, but the Bun and uWS backends bind
52
+ // those ports themselves and have no Node http.Server to hand off to — they delegate to
53
+ // these legacy routes via inject() and look the instance up by port (see injectToFastify).
54
+ for (const port of [env.get(CONFIG_PARAMS.HTTP_PORT), env.get(CONFIG_PARAMS.HTTP_SECUREPORT)]) {
55
+ if (port != null) registerFastifyInstance(port, built);
56
+ }
48
57
  }
49
58
  const resolvedServer = await fastifyServer;
50
59
  const routeFolder = dirname(entry.absolutePath);
@@ -559,8 +559,9 @@ async function graphqlQueryingHandler(request: Request) {
559
559
  }
560
560
  case 'POST': {
561
561
  const requestBodyDeserialize = getDeserializer(request.headers.get('content-type'), true);
562
- // @ts-expect-error: _nodeRequest is a custom property on request and is the IncomingMessage with is a Readable
563
- const requestParams = await requestBodyDeserialize(request._nodeRequest);
562
+ // Read the body through request.body (as REST.ts does): it is a Readable-compatible
563
+ // body stream on every adapter, whereas _nodeRequest is null on the Bun/uWS adapters.
564
+ const requestParams = await requestBodyDeserialize(request.body as any);
564
565
  assertRequestParams(requestParams);
565
566
  return resolver(requestParams, request);
566
567
  }
@@ -582,7 +583,7 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
582
583
  // Await the `graphqlHandler` call here so that errors are caught.
583
584
  return await graphqlQueryingHandler(request as any);
584
585
  } catch (error) {
585
- logger.error(error);
586
+ logger.error(logger.errorForLog(error));
586
587
 
587
588
  // Error Handling
588
589
  // Based on the GraphQL specification, a GraphQL response (non-http) are a map with a `data` field and an `errors` field.