@harperfast/harper 5.3.2 → 5.3.3

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 (251) hide show
  1. package/bin/cliOperations.ts +3 -3
  2. package/bin/copyDb.ts +4 -3
  3. package/components/DESIGN.md +12 -0
  4. package/components/packageComponent.ts +42 -17
  5. package/config/DESIGN.md +4 -0
  6. package/config/configUtils.ts +8 -8
  7. package/config/parseConfigFile.ts +11 -7
  8. package/dist/bin/cliOperations.js +3 -3
  9. package/dist/bin/cliOperations.js.map +1 -1
  10. package/dist/bin/copyDb.js +3 -3
  11. package/dist/bin/copyDb.js.map +1 -1
  12. package/dist/components/packageComponent.d.ts +9 -7
  13. package/dist/components/packageComponent.js +41 -16
  14. package/dist/components/packageComponent.js.map +1 -1
  15. package/dist/config/configUtils.js +4 -3
  16. package/dist/config/configUtils.js.map +1 -1
  17. package/dist/config/parseConfigFile.d.ts +1 -0
  18. package/dist/config/parseConfigFile.js +7 -3
  19. package/dist/config/parseConfigFile.js.map +1 -1
  20. package/dist/resources/DatabaseTransaction.d.ts +75 -10
  21. package/dist/resources/DatabaseTransaction.js +1080 -277
  22. package/dist/resources/DatabaseTransaction.js.map +1 -1
  23. package/dist/resources/Resource.js +40 -14
  24. package/dist/resources/Resource.js.map +1 -1
  25. package/dist/resources/RocksTransactionLogStore.js +1 -0
  26. package/dist/resources/RocksTransactionLogStore.js.map +1 -1
  27. package/dist/resources/Table.js +16 -2
  28. package/dist/resources/Table.js.map +1 -1
  29. package/dist/resources/analytics/write.js +34 -16
  30. package/dist/resources/analytics/write.js.map +1 -1
  31. package/dist/resources/databases.d.ts +5 -0
  32. package/dist/resources/databases.js +38 -4
  33. package/dist/resources/databases.js.map +1 -1
  34. package/dist/resources/recordLock.d.ts +9 -4
  35. package/dist/resources/recordLock.js +79 -19
  36. package/dist/resources/recordLock.js.map +1 -1
  37. package/dist/server/DurableSubscriptionsSession.d.ts +2 -0
  38. package/dist/server/DurableSubscriptionsSession.js +20 -17
  39. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  40. package/dist/server/mqtt.js +41 -23
  41. package/dist/server/mqtt.js.map +1 -1
  42. package/dist/server/serverHelpers/NodeAdapterResponse.js +18 -0
  43. package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -1
  44. package/dist/utility/npmUtilities.js +18 -7
  45. package/dist/utility/npmUtilities.js.map +1 -1
  46. package/package.json +1 -1
  47. package/resources/DESIGN.md +42 -10
  48. package/resources/DatabaseTransaction.ts +1244 -275
  49. package/resources/Resource.ts +39 -14
  50. package/resources/RocksTransactionLogStore.ts +1 -0
  51. package/resources/Table.ts +24 -2
  52. package/resources/analytics/DESIGN.md +17 -0
  53. package/resources/analytics/write.ts +28 -15
  54. package/resources/databases.ts +40 -4
  55. package/resources/record-locks.md +34 -17
  56. package/resources/recordLock.ts +89 -22
  57. package/server/DESIGN.md +2 -0
  58. package/server/DurableSubscriptionsSession.ts +18 -16
  59. package/server/mqtt.ts +39 -22
  60. package/server/serverHelpers/NodeAdapterResponse.ts +19 -1
  61. package/studio/web/assets/Chat-B-Rd-ft9.js +2100 -0
  62. package/studio/web/assets/FloatingChat-CFypO7kh.js +23 -0
  63. package/studio/web/assets/SearchDialog-BRIyPiBl.js +1 -0
  64. package/studio/web/assets/{abnfDiagram-O67JEVCF-raXE027J.js → abnfDiagram-YUKMZFIV-DPR2lFRk.js} +1 -1
  65. package/studio/web/assets/{apiToken-DNrfij0E.js → apiToken-BraYnzvC.js} +1 -1
  66. package/studio/web/assets/applications-XCds8-ZP.js +296 -0
  67. package/studio/web/assets/architecture-WOLXFQ4H-DtJHIK0_.js +1 -0
  68. package/studio/web/assets/{architectureDiagram-NJMV4G6O-C8_Eet7w.js → architectureDiagram-47P4ROYG-MDymp2dh.js} +3 -3
  69. package/studio/web/assets/authStore-DE1HFvVP.js +3 -0
  70. package/studio/web/assets/{blockDiagram-BEXU5L5S-DR6S5-1D.js → blockDiagram-E7TT5TSB-O5qRgZmd.js} +3 -3
  71. package/studio/web/assets/button-C37w80Uk.js +21 -0
  72. package/studio/web/assets/{c4Diagram-YGBWAQC7-BAfZ1G01.js → c4Diagram-CRA5TL53-BdPORGHo.js} +2 -2
  73. package/studio/web/assets/channel-yCStoq0_.js +1 -0
  74. package/studio/web/assets/{chevron-up-CFxbMGv2.js → chevron-up-DozwrIz-.js} +1 -1
  75. package/studio/web/assets/chunk-2BW5OAIV-DJ-iXJAw.js +10 -0
  76. package/studio/web/assets/{chunk-ZIGJFQKS-Cs8fUn7y.js → chunk-3YJQHVM4-BOgacqS1.js} +2 -2
  77. package/studio/web/assets/{chunk-7PRAP22T-KyqX6x3f.js → chunk-4EA7E6EY-C-7xqUBl.js} +1 -1
  78. package/studio/web/assets/{chunk-GWA4HPMP-DsejB6gA.js → chunk-7TKQ45FW-C3XA1bIN.js} +1 -1
  79. package/studio/web/assets/{chunk-GTNCS2PH-DGHLXyow.js → chunk-AW2ZBBNX-BdvgD1UW.js} +3 -3
  80. package/studio/web/assets/{chunk-5DYCD2WN-BceBfIHT.js → chunk-DBDB3WZW-CKKxIc6F.js} +1 -1
  81. package/studio/web/assets/{chunk-MBY4JIJT-BwSGweQP.js → chunk-E2ZNV5FY-bVefNHvz.js} +3 -3
  82. package/studio/web/assets/chunk-EU5HNXII-BHv2kWp4.js +213 -0
  83. package/studio/web/assets/chunk-FVRAUYC3-BoL_NO6b.js +2 -0
  84. package/studio/web/assets/{chunk-742MDFTN-CIkt8ijt.js → chunk-HJ2JQQFS-N_98oMRS.js} +1 -1
  85. package/studio/web/assets/{chunk-NETBCI7D-C8UnOw_f.js → chunk-HTAEGDNF-BQ1fSjPs.js} +1 -1
  86. package/studio/web/assets/{chunk-WEXAMYUT-BAIhTwyh.js → chunk-KQW6MTUR-qP0QiQAg.js} +1 -1
  87. package/studio/web/assets/chunk-NGNAAXSQ-DqoPQk1a.js +136 -0
  88. package/studio/web/assets/{chunk-UA2S7LBM-BHpEdH8D.js → chunk-NTY3LDVX-CSBFjlJg.js} +1 -1
  89. package/studio/web/assets/chunk-VPRB5NB3-BVHOe9Q-.js +129 -0
  90. package/studio/web/assets/{chunk-7INBJB4K-BpeAXyOi.js → chunk-XC4XBNZT-46uQwtF4.js} +10 -10
  91. package/studio/web/assets/{classDiagram-v2-NBCMYWYE-DmDPkt2u.js → classDiagram-v2-K4WV4PDN-Da0ps1Ui.js} +3 -3
  92. package/studio/web/assets/cn-DpF1PxBT.js +1 -0
  93. package/studio/web/assets/{createLucideIcon-fO5dL7D5.js → createLucideIcon-DLWRoWN-.js} +1 -1
  94. package/studio/web/assets/{cssMode-DBucrQdT.js → cssMode-BY1w91l8.js} +1 -1
  95. package/studio/web/assets/cynefin-EF2NZ3EQ-jQ2eCUaU.js +1 -0
  96. package/studio/web/assets/cynefinDiagram-3GCD6N5R-TNi5iS0E.js +62 -0
  97. package/studio/web/assets/dagre-W4DXFKR2-BnLHmA44.js +4 -0
  98. package/studio/web/assets/{diagram-22UHCM2B-DJCvcaWP.js → diagram-2UJZ2QOL-r_jehhZR.js} +7 -7
  99. package/studio/web/assets/{diagram-3UASUU5V-Du6VKWln.js → diagram-OVF4WLC6-BSUXGVh8.js} +11 -11
  100. package/studio/web/assets/{diagram-CDSNMT55-l5Ga0u-W.js → diagram-PFPMY2P6-BrVedycw.js} +3 -3
  101. package/studio/web/assets/{diagram-MLGK6HIB-CNQY47LA.js → diagram-UMYRVEAY-CBIaakBB.js} +1 -1
  102. package/studio/web/assets/diagram-YEKJPTXX-DFmmDXCl.js +41 -0
  103. package/studio/web/assets/diagram-ZIFT7M5P-CQfOWxOi.js +3 -0
  104. package/studio/web/assets/dist-9lpzt2Hn.js +11 -0
  105. package/studio/web/assets/{ebnfDiagram-ZINNZB2B-5OnkTpfi.js → ebnfDiagram-VR2GEFS7-FXA3Xe6e.js} +1 -1
  106. package/studio/web/assets/{editor-CHpQCuBq.js → editor-D5mpVs1b.js} +1 -1
  107. package/studio/web/assets/elk-IJKZMXRS-CC4gfVRU.js +27 -0
  108. package/studio/web/assets/{erDiagram-OPXOYQCR-CQlCX84D.js → erDiagram-O2IAPWRE-C-BkDTPl.js} +2 -2
  109. package/studio/web/assets/errorText-8xV8WZBt.js +1 -0
  110. package/studio/web/assets/eventmodeling-K75KTNOO-D0dJ_PbR.js +1 -0
  111. package/studio/web/assets/flowDiagram-OXPTDLAJ-C1LEc_Hf.js +1 -0
  112. package/studio/web/assets/{ganttDiagram-FUAMR5RP-bloX3Voy.js → ganttDiagram-R7TSEDQI-85F44BIo.js} +4 -4
  113. package/studio/web/assets/{getAnalytics-C0FHjtdQ.js → getAnalytics-B1xpd4kG.js} +1 -1
  114. package/studio/web/assets/gitGraph-VSP46ZUC-BBmqrmjK.js +1 -0
  115. package/studio/web/assets/gitGraphDiagram-XJZIOB7I-CFY6iMwg.js +106 -0
  116. package/studio/web/assets/graphlib-DS17s2tU.js +1 -0
  117. package/studio/web/assets/{html-B1iA8q38.js → html-DiXKiahz.js} +1 -1
  118. package/studio/web/assets/{htmlMode-CrL3YO5Y.js → htmlMode-DitVJ707.js} +1 -1
  119. package/studio/web/assets/index-CDnn0-PN.css +1 -0
  120. package/studio/web/assets/index-D8kyF6dt.js +833 -0
  121. package/studio/web/assets/index.lazy-DgbjUxLo.js +1 -0
  122. package/studio/web/assets/info-OHQRW6UA-Bs2aEco7.js +1 -0
  123. package/studio/web/assets/{infoDiagram-VRGFBTTK-B0J1132p.js → infoDiagram-5W2HQ5XZ-d6yG1yfZ.js} +1 -1
  124. package/studio/web/assets/invalidateEntityQueries-BkWXiqjz.js +1 -0
  125. package/studio/web/assets/{ishikawaDiagram-OU5B5YK6-BNk0tuQx.js → ishikawaDiagram-K3B6WC7H-CSYz-WRA.js} +2 -2
  126. package/studio/web/assets/{javascript-CXgQMkoa.js → javascript-BBVXeMDm.js} +1 -1
  127. package/studio/web/assets/{journeyDiagram-ZHPQQLJL-Be79RMQM.js → journeyDiagram-COXZFDF6-BDj6Wqcj.js} +3 -3
  128. package/studio/web/assets/{jsonMode-DycqRKOv.js → jsonMode-D85_j2oD.js} +1 -1
  129. package/studio/web/assets/{kanban-definition-PNTS6WVX-4uuoQP9W.js → kanban-definition-P3RFRI5V-C2Bjfbp-.js} +6 -6
  130. package/studio/web/assets/{languageServices-HYHV1wDJ.js → languageServices-U3mWhpRL.js} +1 -1
  131. package/studio/web/assets/{lspLanguageFeatures-CH_6SYiA.js → lspLanguageFeatures-CoymyM3c.js} +1 -1
  132. package/studio/web/assets/{mermaid-parser.core-V1UYMeXV.js → mermaid-parser.core-Ch_T61iL.js} +6 -6
  133. package/studio/web/assets/mermaid.core-BOAgKzo5.js +44 -0
  134. package/studio/web/assets/{mindmap-definition-NLK3R4M7-C70Ctm0J.js → mindmap-definition-LZFPQGKD-DzOapzMM.js} +3 -3
  135. package/studio/web/assets/notifications-CPgEC6ZP.js +1 -0
  136. package/studio/web/assets/notifications-D_VqhHnx.js +1 -0
  137. package/studio/web/assets/operations-D8aaKYty.js +4 -0
  138. package/studio/web/assets/packet-JDAUHWVQ-CGHB_EIE.js +1 -0
  139. package/studio/web/assets/{pegDiagram-GJSIUBJH-d6z1cc7W.js → pegDiagram-BGZESJAR-ADzOIKrz.js} +1 -1
  140. package/studio/web/assets/pie-XZMESJXO-Bijz5Y2o.js +1 -0
  141. package/studio/web/assets/pieDiagram-CAPJLFHJ-VM7fvbwO.js +39 -0
  142. package/studio/web/assets/profile-DM5E6Xls.js +1 -0
  143. package/studio/web/assets/{quadrantDiagram-O4NWA36T-CIF5Qkou.js → quadrantDiagram-GDMTTRAM-Bmza84_j.js} +3 -3
  144. package/studio/web/assets/radar-ABXABTNO-IaC8pB63.js +1 -0
  145. package/studio/web/assets/railroad-I3PHUGI6-CCrSUqeq.js +1 -0
  146. package/studio/web/assets/railroad-abnf-LNEFI6M7-BhobVp-t.js +1 -0
  147. package/studio/web/assets/railroad-ebnf-DWYD2UWJ-OHhGUTEh.js +1 -0
  148. package/studio/web/assets/railroad-peg-JR7BVNR7-FupC3Jcn.js +1 -0
  149. package/studio/web/assets/{railroadDiagram-XR7U4H2S-y3Jv2U6N.js → railroadDiagram-SM67HX2B-CgFGOVI5.js} +1 -1
  150. package/studio/web/assets/regions-WFvXqryO.js +1 -0
  151. package/studio/web/assets/{register-DCN4QUii.js → register-B7DvfAGj.js} +3 -3
  152. package/studio/web/assets/request-ChoMERRk.js +2 -0
  153. package/studio/web/assets/{requirementDiagram-PLB6GJNP-ChINZEmu.js → requirementDiagram-X7JNWC4A-BHoQgkoT.js} +4 -4
  154. package/studio/web/assets/restartGate-D_e93lFE.js +1 -0
  155. package/studio/web/assets/restartUnconfirmed-BqcDZ__Y.js +1 -0
  156. package/studio/web/assets/sankeyDiagram-UM26HJRW-8tFu_oUe.js +40 -0
  157. package/studio/web/assets/sequenceDiagram-ZO4K6R2Y-jryb1rwT.js +169 -0
  158. package/studio/web/assets/setComponentFile-BHk22Rh6.js +1 -0
  159. package/studio/web/assets/setup-wNdpTrVH.js +2 -0
  160. package/studio/web/assets/{stateDiagram-v2-GCMORJYK-Bs8RIb9K.js → stateDiagram-v2-TBUQTH76-CnWE5yFn.js} +2 -2
  161. package/studio/web/assets/status-CYoonm9V.js +61 -0
  162. package/studio/web/assets/{swimlanes-2SLR337P-B52xrf6t.js → swimlanes-N4OXWK64-CBlFzDJn.js} +1 -1
  163. package/studio/web/assets/swimlanesDiagram-K3J5GTZL-DvFLtUPi.js +8 -0
  164. package/studio/web/assets/{timeline-definition-EJHVYXUP-DJSITpQX.js → timeline-definition-YOQAKGHF-CFWqHrqI.js} +4 -4
  165. package/studio/web/assets/{toggleHighContrast-CfAp6Sxb.js → toggleHighContrast-CKEm-P4K.js} +1 -1
  166. package/studio/web/assets/treeView-D4JQ5SDB-CpV9zuVb.js +1 -0
  167. package/studio/web/assets/treemap-SAJKECNS-Bc94Tqiq.js +1 -0
  168. package/studio/web/assets/{tsMode-HZR0bfq4.js → tsMode-Dv35qHmn.js} +1 -1
  169. package/studio/web/assets/{typescript-UMJSfZyF.js → typescript-Bly1EW8o.js} +1 -1
  170. package/studio/web/assets/{useEntityRestURL-B379CS-h.js → useEntityRestURL-Cg3P0MOb.js} +1 -1
  171. package/studio/web/assets/usecaseDiagram-VIAY4XPW-Brf2xP3S.js +328 -0
  172. package/studio/web/assets/{vendor-core-6LhfJlRC.js → vendor-core-PejxhUWQ.js} +1 -1
  173. package/studio/web/assets/vendor-datadog-GjET_Cbn.js +6 -0
  174. package/studio/web/assets/{vendor-react-BOliS0BL.js → vendor-react-DSIx92Ld.js} +1 -1
  175. package/studio/web/assets/vendor-tanstack-C5Y-nrd2.js +2 -0
  176. package/studio/web/assets/vendor-ui-BBWHOEoQ.js +41 -0
  177. package/studio/web/assets/{vennDiagram-UO4OBE2U-DZTsDA4k.js → vennDiagram-BLWOH2XV-XxOyJPq_.js} +3 -3
  178. package/studio/web/assets/wardley-7MLQ67FV-DRW0zuE4.js +1 -0
  179. package/studio/web/assets/{wardleyDiagram-VNRHLVJA-qu_f7Tvl.js → wardleyDiagram-YMQ3BMBF-B3WskJ9W.js} +3 -3
  180. package/studio/web/assets/wasAReleasedBeforeB-D5grAaGM.js +1 -0
  181. package/studio/web/assets/{workers-Bn_0_L1R.js → workers-DVtjrvUa.js} +1 -1
  182. package/studio/web/assets/{xml-CG2N-1r1.js → xml-DjAPbgLN.js} +1 -1
  183. package/studio/web/assets/xychartDiagram-TAQBALBS-D5NV4Env.js +7 -0
  184. package/studio/web/assets/{yaml-68pda6Av.js → yaml-oTUyoJy1.js} +1 -1
  185. package/studio/web/index.html +17 -15
  186. package/utility/npmUtilities.ts +17 -7
  187. package/studio/web/assets/Chat-CrxPD4ph.js +0 -2294
  188. package/studio/web/assets/FloatingChat-EXzoUQ-9.js +0 -23
  189. package/studio/web/assets/SearchDialog-7qRL8Yqx.js +0 -1
  190. package/studio/web/assets/_basePickBy-QJXUw_pr.js +0 -1
  191. package/studio/web/assets/_baseUniq-DR4vVjyC.js +0 -1
  192. package/studio/web/assets/alertDialog-BbWD9Xxa.js +0 -1
  193. package/studio/web/assets/applications-CdqjiSQe.js +0 -296
  194. package/studio/web/assets/architecture-7GRP2DOG-DDmz3hpI.js +0 -1
  195. package/studio/web/assets/arrow-up-D2g_57vT.js +0 -1
  196. package/studio/web/assets/authStore-CJCaADw9.js +0 -3
  197. package/studio/web/assets/button-DWwfgGWI.js +0 -21
  198. package/studio/web/assets/channel-D0AkPjJ8.js +0 -1
  199. package/studio/web/assets/chunk-3FUC2YCW-DuRp-j19.js +0 -2
  200. package/studio/web/assets/chunk-7M6MHVWA-h6WlaTzO.js +0 -213
  201. package/studio/web/assets/chunk-FOHPRMQF-DzxwRta7.js +0 -161
  202. package/studio/web/assets/chunk-O7XYJQB3-CBS3nZYV.js +0 -128
  203. package/studio/web/assets/chunk-Z7XXMR3K-DSHbImdf.js +0 -10
  204. package/studio/web/assets/cn-6T8VranJ.js +0 -1
  205. package/studio/web/assets/cynefin-OW5HDTMX-DIvbXLSj.js +0 -1
  206. package/studio/web/assets/cynefinDiagram-VND7K2PF-BdtaXiCE.js +0 -62
  207. package/studio/web/assets/dagre-6A5THRUB-C7RUWHyz.js +0 -4
  208. package/studio/web/assets/diagram-ATOU4E4O-dxRfeG4B.js +0 -3
  209. package/studio/web/assets/diagram-MPIPVDR6-7prveD1B.js +0 -41
  210. package/studio/web/assets/dist-BrRuMUzt.js +0 -11
  211. package/studio/web/assets/download-Cn6tPG4L.js +0 -1
  212. package/studio/web/assets/elk-276RUBZZ-DxDQPjQT.js +0 -27
  213. package/studio/web/assets/errorText-NpBmvk9I.js +0 -1
  214. package/studio/web/assets/eventmodeling-NTZA5JFV-DONQf5aG.js +0 -1
  215. package/studio/web/assets/flowDiagram-KWPJA3E3-BoZ8_QR1.js +0 -1
  216. package/studio/web/assets/gitGraph-4MIJSDKK-BrXWmJt2.js +0 -1
  217. package/studio/web/assets/gitGraphDiagram-X574FWY7-C2KnnZeY.js +0 -106
  218. package/studio/web/assets/graphlib-C9Tl7QrK.js +0 -1
  219. package/studio/web/assets/index-QLZw6xrU.css +0 -1
  220. package/studio/web/assets/index-qCLyhyah.js +0 -828
  221. package/studio/web/assets/index.lazy-DCx-DxIV.js +0 -2
  222. package/studio/web/assets/info-A6RAGUB7-BhzBHe4U.js +0 -1
  223. package/studio/web/assets/mermaid.core-1PX6R49U.js +0 -44
  224. package/studio/web/assets/notifications-BSObVIY1.js +0 -1
  225. package/studio/web/assets/notifications-DFLXk68b.js +0 -1
  226. package/studio/web/assets/packet-AYTQ26CC-DWbXHx5g.js +0 -1
  227. package/studio/web/assets/pie-WAS4IAKB-DjoTm3yr.js +0 -1
  228. package/studio/web/assets/pieDiagram-5QR66LMP-Dz3RHpDA.js +0 -39
  229. package/studio/web/assets/profile-TBjzOMIy.js +0 -1
  230. package/studio/web/assets/radar-RG4KPBEZ-CVBgjCOj.js +0 -1
  231. package/studio/web/assets/railroad-74A4TZTK-BdhXw10Q.js +0 -1
  232. package/studio/web/assets/railroad-abnf-HS5TGJTU-BcoQPN39.js +0 -1
  233. package/studio/web/assets/railroad-ebnf-LZEXJU2U-B1Lke2JN.js +0 -1
  234. package/studio/web/assets/railroad-peg-WCYAUIDC-D-oJFkiV.js +0 -1
  235. package/studio/web/assets/regions-C4an4H7e.js +0 -1
  236. package/studio/web/assets/restartGate-nHAFLDh-.js +0 -1
  237. package/studio/web/assets/sankeyDiagram-IPEJSGJF-D9AyLce2.js +0 -40
  238. package/studio/web/assets/sequenceDiagram-PO4LG4MO-hfk4aIEm.js +0 -169
  239. package/studio/web/assets/setComponentFile-BrqOM1Zm.js +0 -1
  240. package/studio/web/assets/setup-0v2MCm-E.js +0 -2
  241. package/studio/web/assets/status-DdezofUu.js +0 -61
  242. package/studio/web/assets/swimlanesDiagram-TC7HE7FX-DfN7BAP5.js +0 -8
  243. package/studio/web/assets/tabs-BCTQzoQK.js +0 -1
  244. package/studio/web/assets/treeView-Q6P3EWNA-C-epdxRo.js +0 -1
  245. package/studio/web/assets/treemap-WGGIJYW6-DOVyp_zw.js +0 -1
  246. package/studio/web/assets/usecaseDiagram-POWQR4AR-B6252cbq.js +0 -328
  247. package/studio/web/assets/vendor-datadog-ozKf-pts.js +0 -6
  248. package/studio/web/assets/vendor-tanstack-Bntfka-M.js +0 -2
  249. package/studio/web/assets/vendor-ui-9EYYDgoN.js +0 -41
  250. package/studio/web/assets/wardley-WFR3VGLG-Cfqoe6z4.js +0 -1
  251. package/studio/web/assets/xychartDiagram-PMCCYNJV-UrOKRsmR.js +0 -7
package/server/DESIGN.md CHANGED
@@ -358,6 +358,7 @@ A persisted QoS 1/2 entry is `{ qos, topic, startTime, databaseGeneration? }`, a
358
358
  - **Before CONNACK,** a collection entry bound to its database's current generation is checked against the floor (a record entry's own walk decides after CONNACK); a failure deletes the record and reports the session as not present. The connection then writes its incarnation (`takeOver`), and every later write first checks the stored incarnation: a connection that finds another's stops writing and closes (0x8E), and one resumed from a record never creates one. Any newer authenticated connection for the client id on the same thread, a clean start included, closes the older durable connection the same way before it reads or deletes the record, once that connection has saved its positions, and waits for that save to settle; a connection that has already closed counts until its last save settles. A save that failed leaves the record at the last one that landed, so the newer connection resumes from there and may redeliver, never skip. Simultaneous CONNECTs for one client id take their turn through a per-thread claim, so each sees the one before it; one that gives up waiting keeps its place until the one before it finishes. The CONNECT's pending session is assigned before it waits, so packets sent right behind the CONNECT wait with it rather than being refused; the wait is bounded (10 s, then CONNACK 0x88, or 0x03 on v3.1.1), and a CONNECT whose client closes while it waits takes nothing over. A session that has ended, by a disconnect or by yielding to a takeover, saves nothing after its final save, so a SUBSCRIBE still in flight cannot write over the record a newer connection claimed.
359
359
  - **Subscription changes are serial.** Packets are handled concurrently, so a durable session applies SUBSCRIBE, UNSUBSCRIBE and `resume()` one at a time: a topic's state belongs to one subscription. A SUBSCRIBE of any QoS is saved before SUBACK. A failed SUBSCRIBE leaves its topic unsubscribed and saves that. It ends the subscription it started, and drops the state of the one it replaced, which replacing ended. An UNSUBSCRIBE whose save fails is reported failed again on a retry until the removal is saved, and any failed save starts the checkpoint timer, so a session with only QoS 0 topics retries it too.
360
360
  - **Wills are kept per connection,** keyed `[clientId, incarnation]`: a connection closing after a takeover publishes and deletes only its own will (MQTT 5 §3.1.4 publishes a taken-over client's will), never the will of the connection that replaced it.
361
+ - **Only a will's publish is authorized** (`settleWill`, harper#1874). The `hdb_session_will` row is the session's bookkeeping: it is read and deleted on contexts of its own, not the session's `authorize: true` context, which spends its one-shot check on the first call (roles are not granted system tables, so a non-super user's will never published and its row leaked). The publish then runs on a fresh session context, as the connection's user or a scoped will's embedded role, and the row is deleted after any outcome, a refused or failed publish included, so a will that fails for a transient reason is lost: the boot sweep, the only retry path, would replay a kept row only at the next restart, with no liveness check (harper#3034). The publish commits before the delete, so a crash between them leaves the row for that sweep and the will may be published twice (by design; untested). `unitTests/server/sessionWill.test.js` and `integrationTests/mqtt/will-authorization.test.ts` enforce that only the publish is authorized and that the row is deleted after a publish, a refusal and a clean DISCONNECT.
361
362
  - **One terminal path** (`subscriptionFailed`): a refused resume subscribe, a final `Error` value (observed through the subscription's `close` as well as the consumer loop, which may be parked on socket back-pressure), or an iterator failure. Only a 410 (pruned history) discards the session — writers fenced, siblings ended, a v5 DISCONNECT (0x83, the client-error message when problem information is allowed) and a socket close that never waits on storage, then a deletion only if the record still carries this incarnation. Anything else, a 409 from a restored or copied database included, saves positions and closes the connection so the client resumes.
362
363
  - **Only the current generation is checked.** A position bound to another generation, another node's (`hdb_durable_session` replicates, and each node mints its own generations) or this database's before a restore or copy, resumes best-effort: it is not checked before CONNACK, replays unchecked from where it is, and binds again at the first certified position. Until a live write gives it a watermark, that replay saves no position, so a reconnect on a quiet database replays it again.
363
364
  - **Not covered:** concurrent connections for one client id on different threads, together with crash replay of a failed conditional write (harper#2940); late commits below a saved position, including a late transaction still queued behind the message being sent, which the session cannot see (harper#2928); a replay from another generation, which is unchecked.
@@ -647,6 +648,7 @@ The SQL and job paths are additive rather than exclusive: `verifyPermsAST` valid
647
648
  - **Headers commit exactly once, through `this.writeHead`.** `write()`, `end()`, `flushHeaders()` and `_implicitHeader()` all reach `this.writeHead(this.statusCode)` by property lookup, so a `writeHead` that `on-headers` replaced on the instance runs its listeners (the ones that set `Content-Encoding` and remove `Content-Length`) before the promise resolves. After commit, `setHeader`/`appendHeader`/`removeHeader` and a second `writeHead` throw `ERR_HTTP_HEADERS_SENT` as Node's do; `_header` (which `compression` ≤ 1.7 tests instead of `headersSent`) and `finished` (which `on-finished` tests) derive from that state.
648
649
  - **The adapter owns the `'error'` listener.** A `destroy(err)` right after `writeHead()` emits before the awaiting caller can attach one; the error stays in the stream's `errored` state for `pipeline()`, `finished()` or async iteration. Client disconnect (`Request.signal`) destroys the response without an error after headers (a plain premature close, which `pipeBodyToResponse` treats as routine) and rejects the promise with the abort reason before them; a handler that throws or rejects before ending the response destroys it, and one that fails after `end()` is logged at warn.
649
650
  - **Header names are case-insensitive on removal too.** `Headers.delete` lowercases like `set`/`get`/`has`; the inherited `Map.delete` silently left `send`'s `Content-Length` on a gzip body (truncated transfers).
651
+ - **Every piped chunk reaches `write()` by property lookup.** Node 26.11's `Readable.pipe` fast path (nodejs/node#66182) calls `_write()` directly when pipe's listener is a byte-mode source's only `'data'` listener, so `compression`'s instance patch of `res.write` was skipped for most chunks of `send(...).pipe(res)`: raw bytes inside the gzip stream, or a stall once `compression` had routed pipe's `'drain'` to its idle zlib stream. On runtimes that have the fast path (`'writeKnownBuffer' in Writable`, a static it added) the response adds an inert `'data'` listener to each byte-mode core `Readable` on `'pipe'`, which fails the fast path's single-listener check, and removes it on `'unpipe'` through a listener prepended in the constructor: pipe's own cleanup resumes a source awaiting `'drain'` while any `'data'` listener remains, which would leave an unpiped source flowing into nothing. Object-mode and decoded sources, and legacy `Stream.prototype.pipe` (which never emits `'unpipe'`), are skipped. Remove it once every supported Node 26 release dispatches pipe through an overridden `write()`.
650
652
  - **Express is not a target.** `express`'s `app.handle()` replaces the response's prototype with one rooted at `http.ServerResponse.prototype`, which no Writable-derived response survives, and would do the same to the request `Proxy`'s target, Harper's real `IncomingMessage`. Middleware that duck-types the response (Next.js, `compression`, `send`, `serve-static`, `finalhandler`, h3, fastify) is the supported surface.
651
653
 
652
654
  ## `manageThreads` has two different `workerCount`s (`server/threads/manageThreads.js`)
@@ -484,22 +484,7 @@ class SubscriptionsSession {
484
484
  const willId = this.willId;
485
485
  this.willId = undefined;
486
486
  if (willId) {
487
- const context = this.createContext();
488
- transaction(context, async () => {
489
- try {
490
- if (!clientTerminated) {
491
- const will = await getLastWill().get(willId);
492
- if (will && !isWillFromExpiredScopedToken(will)) {
493
- // A scoped will authorizes under its own embedded role, never the disconnecting
494
- // session's user (which may be a later same-clientId reconnect).
495
- const willContext = will.user?._scopedToken ? { ...context, user: will.user } : context;
496
- await publishMessage(will, will.data, willContext);
497
- }
498
- }
499
- } finally {
500
- await getLastWill().delete(willId);
501
- }
502
- }).catch((error) => {
487
+ this.settleWill(willId, !clientTerminated).catch((error) => {
503
488
  warn(`Error publishing MQTT will for ${this.sessionId}`, error);
504
489
  });
505
490
  }
@@ -509,6 +494,23 @@ class SubscriptionsSession {
509
494
  }
510
495
  this.subscriptions = [];
511
496
  }
497
+ /** The will row is session bookkeeping on contexts of its own; only the publish runs as the session (server/DESIGN.md). */
498
+ async settleWill(willId: [string, string], publish: boolean) {
499
+ try {
500
+ if (publish) {
501
+ const will = await getLastWill().get(willId, {});
502
+ if (will && !isWillFromExpiredScopedToken(will)) {
503
+ const context = this.createContext();
504
+ // A scoped will authorizes under its own embedded role, never the disconnecting
505
+ // session's user (which may be a later same-clientId reconnect).
506
+ if (will.user?._scopedToken) context.user = will.user;
507
+ await publishMessage(will, will.data, context);
508
+ }
509
+ }
510
+ } finally {
511
+ await getLastWill().delete(willId, {});
512
+ }
513
+ }
512
514
  receivedPacket() {
513
515
  if (this.keepalive) {
514
516
  clearTimeout(this.keepaliveTimer);
package/server/mqtt.ts CHANGED
@@ -206,6 +206,19 @@ const SERVER_SHUTTING_DOWN = 0x8b; // MQTT v5 DISCONNECT reason code
206
206
  const REASON_STRING_LIMIT = 256; // bytes
207
207
  /** Fixed header, message id and reason code, plus the property identifier and its length prefix. */
208
208
  const ACK_PACKET_OVERHEAD = 16;
209
+ /** `AccessViolation` is 401 for a request with no user and 403 for a user lacking permission; an MQTT client sees both as "Not authorized". */
210
+ const isNotAuthorized = (error: any) => error?.statusCode === 401 || error?.statusCode === 403;
211
+ /** Only failures this layer maps to a code of their own are safe to describe: any other carries an internal message. */
212
+ const hasClientSafeMessage = (error: any) => isNotAuthorized(error) || error?.statusCode === 404;
213
+ /** REASON_STRING_LIMIT counts encoded bytes, and a character must not be cut in half. */
214
+ function boundedReasonString(message: unknown): string {
215
+ const text = String(message);
216
+ if (Buffer.byteLength(text) <= REASON_STRING_LIMIT) return text;
217
+ const bytes = Buffer.from(text, 'utf8');
218
+ let end = REASON_STRING_LIMIT;
219
+ while (end > 0 && (bytes[end] & 0xc0) === 0x80) end--;
220
+ return bytes.subarray(0, end).toString('utf8');
221
+ }
209
222
 
210
223
  /**
211
224
  * Every live MQTT connection on this worker, so a shutdown can announce itself: a bare socket close
@@ -327,17 +340,14 @@ function onSocket(socket, send, request, user, mqttSettings) {
327
340
  function closeConnection(error?: Error) {
328
341
  try {
329
342
  if (mqttOptions.protocolVersion >= 5) {
330
- const packet: any = { cmd: 'disconnect', reasonCode: error ? 0x83 : 0x8e };
343
+ const packet: any = { cmd: 'disconnect', reasonCode: error ? (isNotAuthorized(error) ? 0x87 : 0x83) : 0x8e };
331
344
  // as for PUBACK: only failures this layer describes itself are safe to hand a client
332
345
  const describable =
333
346
  error instanceof DatabaseGenerationChangedError ||
334
347
  error instanceof ResumeHistoryUnavailableError ||
335
- (error as any)?.statusCode === 403 ||
336
- (error as any)?.statusCode === 404;
348
+ hasClientSafeMessage(error);
337
349
  const reasonString =
338
- describable && error.message && sendProblemInformation
339
- ? Buffer.from(String(error.message), 'utf8').subarray(0, REASON_STRING_LIMIT).toString('utf8')
340
- : undefined;
350
+ describable && error.message && sendProblemInformation ? boundedReasonString(error.message) : undefined;
341
351
  if (
342
352
  reasonString &&
343
353
  (!maximumPacketSize || maximumPacketSize >= Buffer.byteLength(reasonString) + ACK_PACKET_OVERHEAD)
@@ -547,7 +557,11 @@ function onSocket(socket, send, request, user, mqttSettings) {
547
557
  });
548
558
  const listener = async (topic, message, messageId, subscription, version) => {
549
559
  try {
550
- if (disconnected) throw new Error('Session disconnected while trying to send message to', topic);
560
+ if (disconnected) {
561
+ // a delivery already past the subscription when the client left; the disconnect has ended the session
562
+ mqttLog.debug?.(`Session ${session?.sessionId} disconnected while trying to send message to ${topic}`);
563
+ return false;
564
+ }
551
565
  const slashIndex = topic.indexOf('/', 1);
552
566
  const generalTopic = slashIndex > 0 ? topic.slice(0, slashIndex) : topic;
553
567
  const qos = subscription.qos || 0;
@@ -606,6 +620,8 @@ function onSocket(socket, send, request, user, mqttSettings) {
606
620
  break;
607
621
  case 'subscribe':
608
622
  const granted = [];
623
+ // a SUBACK carries one reason string for the whole packet, so the first refusal's is the one sent
624
+ let refusalMessage: string | undefined;
609
625
  for (const subscription of packet.subscriptions) {
610
626
  let grantedQos;
611
627
  try {
@@ -624,21 +640,27 @@ function onSocket(socket, send, request, user, mqttSettings) {
624
640
  grantedQos =
625
641
  mqttOptions.protocolVersion < 5
626
642
  ? 0x80 // the only error code in v3.1.1
627
- : error.statusCode === 403
643
+ : isNotAuthorized(error)
628
644
  ? 0x87 // unauthorized
629
645
  : error.statusCode === 404
630
646
  ? 0x8f // invalid topic
631
647
  : 0x80; // generic failure
648
+ if (mqttOptions.protocolVersion >= 5 && error.message && hasClientSafeMessage(error))
649
+ refusalMessage ??= error.message;
632
650
  }
633
651
  granted.push(grantedQos);
634
652
  }
635
653
  await session.committed;
636
- generateAndSendPacket({
637
- // Send a subscription acknowledgment
638
- cmd: 'suback',
639
- granted,
640
- messageId: packet.messageId,
641
- });
654
+ const subackPacket: any = { cmd: 'suback', granted, messageId: packet.messageId };
655
+ if (refusalMessage !== undefined && sendProblemInformation)
656
+ subackPacket.properties = { reasonString: boundedReasonString(refusalMessage) };
657
+ let suback = generate(subackPacket, mqttOptions);
658
+ // the result vector grows with the filter count, so only the encoded packet says whether the string fits
659
+ if (subackPacket.properties && maximumPacketSize && suback.length > maximumPacketSize) {
660
+ delete subackPacket.properties;
661
+ suback = generate(subackPacket, mqttOptions);
662
+ }
663
+ sendPacket(suback, 'suback');
642
664
  break;
643
665
  case 'unsubscribe': {
644
666
  const granted = [];
@@ -691,23 +713,18 @@ function onSocket(socket, send, request, user, mqttSettings) {
691
713
  reasonCode:
692
714
  mqttOptions.protocolVersion < 5
693
715
  ? 0x80 // the only error code in v3.1.1
694
- : error?.statusCode === 403
716
+ : isNotAuthorized(error)
695
717
  ? 0x87 // not authorized
696
718
  : error?.statusCode === 404
697
719
  ? 0x90 // topic name invalid
698
720
  : 0x80, // unspecified error
699
721
  };
700
- // Only errors this layer maps to a code of their own are safe to describe: any other
701
- // failure carries an internal message that a client must not be handed. Both limits
702
- // are in encoded bytes, so measure and trim the string in bytes too.
703
722
  const describable =
704
723
  mqttOptions.protocolVersion >= 5 &&
705
724
  sendProblemInformation &&
706
725
  error?.message &&
707
- (error.statusCode === 403 || error.statusCode === 404);
708
- const reasonString = describable
709
- ? Buffer.from(String(error.message), 'utf8').subarray(0, REASON_STRING_LIMIT).toString('utf8')
710
- : undefined;
726
+ hasClientSafeMessage(error);
727
+ const reasonString = describable ? boundedReasonString(error.message) : undefined;
711
728
  if (
712
729
  reasonString &&
713
730
  (!maximumPacketSize || maximumPacketSize >= Buffer.byteLength(reasonString) + ACK_PACKET_OVERHEAD)
@@ -6,7 +6,7 @@ import type {
6
6
  ServerResponse as NodeServerResponse,
7
7
  } from 'node:http';
8
8
  import type { Socket } from 'node:net';
9
- import { PassThrough } from 'node:stream';
9
+ import { PassThrough, Readable, Writable } from 'node:stream';
10
10
  import { Headers as ResponseHeaders, applyWriteHeadHeaders } from './Headers.ts';
11
11
 
12
12
  export interface AdaptedResponse {
@@ -29,6 +29,20 @@ class UnsupportedResponseMethodError extends Error {
29
29
  }
30
30
 
31
31
  const ignoreError = () => {};
32
+ const ignoreChunk = () => {};
33
+
34
+ // Node 26.11's pipe fast path (nodejs/node#66182) skips write() overrides such as compression's while pipe's listener
35
+ // is a byte-mode source's only 'data' listener; see server/DESIGN.md
36
+ const pipeSkipsWrite = 'writeKnownBuffer' in Writable;
37
+ function guardPipeSource(source: Readable) {
38
+ // object mode, a decoder, and legacy or userland pipe() never take the fast path; legacy pipe() never emits 'unpipe'
39
+ if (source instanceof Readable && !source.readableObjectMode && !source.readableEncoding)
40
+ source.on('data', ignoreChunk);
41
+ }
42
+ // must run before pipe's own unpipe cleanup, which resumes a source awaiting 'drain' while any 'data' listener remains
43
+ function releasePipeSource(source: unknown) {
44
+ if (source instanceof Readable) source.removeListener('data', ignoreChunk);
45
+ }
32
46
 
33
47
  export class NodeAdapterResponse extends PassThrough implements NodeServerResponse {
34
48
  statusCode = 200;
@@ -60,6 +74,10 @@ export class NodeAdapterResponse extends PassThrough implements NodeServerRespon
60
74
  this.#resolve = resolve;
61
75
  this.#reject = reject;
62
76
  this.on('error', ignoreError);
77
+ if (pipeSkipsWrite) {
78
+ this.on('pipe', guardPipeSource);
79
+ this.prependListener('unpipe', releasePipeSource);
80
+ }
63
81
  if (typeof nodeResponse?.on === 'function') {
64
82
  const forward = (...args: any[]) => this.emit('timeout', ...args);
65
83
  let forwarding = false;