@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
@@ -0,0 +1,2100 @@
1
+ import{i as e,t}from"./rolldown-runtime-Dd_uD5pT.js";import{C as n,S as r,_ as i,a,b as o,c as s,f as c,g as l,h as u,l as d,m as f,n as p,o as m,p as h,r as g,s as _,t as v,v as y,w as b,x}from"./vendor-core-PejxhUWQ.js";import{n as S}from"./cn-DpF1PxBT.js";import{o as C}from"./vendor-datadog-GjET_Cbn.js";import{r as ee}from"./vendor-react-DSIx92Ld.js";import{K as te,P as ne,W as re,nt as ie}from"./vendor-tanstack-C5Y-nrd2.js";import{t as w}from"./button-C37w80Uk.js";import{zt as T}from"./vendor-ui-BBWHOEoQ.js";import{t as E}from"./createLucideIcon-DLWRoWN-.js";import{n as ae,r as oe,t as se}from"./chevron-up-DozwrIz-.js";import{C as ce,S as le,T as ue,b as de,c as fe,i as pe,l as me,r as he,t as ge,w as _e,y as ve}from"./setComponentFile-BHk22Rh6.js";import{l as ye,r as be,s as xe}from"./restartUnconfirmed-BqcDZ__Y.js";import{t as Se}from"./localStorageKeys-etzFY_xV.js";import{Ai as Ce,Bi as we,C as Te,Er as Ee,Fi as De,Gi as Oe,M as ke,Mr as Ae,Mt as je,Or as Me,Ri as Ne,T as Pe,Ti as Fe,U as Ie,V as Le,Vt as Re,Xr as ze,c as Be,fi as Ve,fn as He,g as Ue,gi as We,h as Ge,ki as Ke,mi as D,mn as qe,mr as Je,pn as Ye,xi as Xe,xt as Ze,yi as Qe,zn as $e}from"./index-D8kyF6dt.js";import{t as et}from"./useEntityRestURL-Cg3P0MOb.js";import{n as tt,t as nt}from"./FloatingChat-CFypO7kh.js";import{n as rt}from"./getAnalytics-B1xpd4kG.js";var it={name:`between-horizontal-start`,size:24,node:[[`rect`,{width:`13`,height:`7`,x:`8`,y:`3`,rx:`1`,key:`pkso9a`}],[`path`,{d:`m2 9 3 3-3 3`,key:`1agib5`}],[`rect`,{width:`13`,height:`7`,x:`8`,y:`14`,rx:`1`,key:`1q5fc1`}]],aliases:[`between-horizonal-start`]};it.node;var at=E(it),ot={name:`book`,size:24,node:[[`path`,{d:`M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20`,key:`k3hazp`}]]};ot.node;var st=E(ot),ct={name:`chart-area`,size:24,node:[[`path`,{d:`M3 3v16a2 2 0 0 0 2 2h16`,key:`c24i48`}],[`path`,{d:`M7 11.207a.5.5 0 0 1 .146-.353l2-2a.5.5 0 0 1 .708 0l3.292 3.292a.5.5 0 0 0 .708 0l4.292-4.292a.5.5 0 0 1 .854.353V16a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1z`,key:`q0gr47`}]],aliases:[`area-chart`]};ct.node;var lt=E(ct),ut={name:`circle-x`,size:24,node:[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]],aliases:[`x-circle`]};ut.node;var dt=E(ut),ft={name:`file-pen`,size:24,node:[[`path`,{d:`M12.659 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v9.34`,key:`o6klzx`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M10.378 12.622a1 1 0 0 1 3 3.003L8.36 20.637a2 2 0 0 1-.854.506l-2.867.837a.5.5 0 0 1-.62-.62l.836-2.869a2 2 0 0 1 .506-.853z`,key:`zhnas1`}]],aliases:[`file-edit`]};ft.node;var pt=E(ft),mt={name:`logs`,size:24,node:[[`path`,{d:`M3 5h1`,key:`1mv5vm`}],[`path`,{d:`M3 12h1`,key:`lp3yf2`}],[`path`,{d:`M3 19h1`,key:`w6f3n9`}],[`path`,{d:`M8 5h1`,key:`1nxr5w`}],[`path`,{d:`M8 12h1`,key:`1con00`}],[`path`,{d:`M8 19h1`,key:`k7p10e`}],[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}]]};mt.node;var ht=E(mt),gt={name:`message-square-heart`,size:24,node:[[`path`,{d:`M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z`,key:`18887p`}],[`path`,{d:`M7.5 9.5c0 .687.265 1.383.697 1.844l3.009 3.264a1.14 1.14 0 0 0 .407.314 1 1 0 0 0 .783-.004 1.14 1.14 0 0 0 .398-.31l3.008-3.264A2.77 2.77 0 0 0 16.5 9.5 2.5 2.5 0 0 0 12 8a2.5 2.5 0 0 0-4.5 1.5`,key:`1faxuh`}]]};gt.node;var _t=E(gt),vt={name:`send`,size:24,node:[[`path`,{d:`M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z`,key:`1ffxy3`}],[`path`,{d:`m21.854 2.147-10.94 10.939`,key:`12cjpa`}]]};vt.node;var yt=E(vt),bt={name:`user`,size:24,node:[[`path`,{d:`M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2`,key:`975kel`}],[`circle`,{cx:`12`,cy:`7`,r:`4`,key:`17ys0d`}]]};bt.node;var xt=E(bt),St={name:`wrench`,size:24,node:[[`path`,{d:`M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z`,key:`1ngwbx`}]]};St.node;var Ct=E(St);async function wt(){await S.delete(`/Chat/Messages/`)}var O=e(C(),1),k=ee();function Tt({setMessages:e}){let[t,n]=(0,O.useState)(!1),r=(0,O.useCallback)(async()=>{if(!t){n(!0);try{await wt(),e([])}catch(e){console.error(`Failed to clear chat:`,e)}finally{n(!1)}}},[t,e]);return(0,k.jsxs)(`button`,{type:`button`,className:`clear-chat-button gap-1`,onClick:r,disabled:t,title:`Clear chat`,children:[t?(0,k.jsx)(Ke,{className:`animate-spin`,size:18}):(0,k.jsx)(Qe,{size:18}),`Clear`]})}async function Et(){let{data:e}=await S.get(`/Chat/Messages/`);return e}var Dt=`vercel.ai.error`,Ot=Symbol.for(Dt),A=class e extends Error{constructor({name:e,message:t,cause:n}){super(t),this[Ot]=!0,this.name=e,this.cause=n}static isInstance(t){return e.hasMarker(t,Dt)}static hasMarker(e,t){let n=Symbol.for(t);return typeof e==`object`&&!!e&&n in e&&typeof e[n]==`boolean`&&e[n]===!0}},kt=`AI_APICallError`,At=`vercel.ai.error.${kt}`,jt=Symbol.for(At),Mt=class extends A{constructor({message:e,url:t,requestBodyValues:n,statusCode:r,responseHeaders:i,responseBody:a,cause:o,isRetryable:s=r!=null&&(r===408||r===409||r===429||r>=500),data:c}){super({name:kt,message:e,cause:o}),this[jt]=!0,this.url=t,this.requestBodyValues=n,this.statusCode=r,this.responseHeaders=i,this.responseBody=a,this.isRetryable=s,this.data=c}static isInstance(e){return A.hasMarker(e,At)}},Nt=`AI_EmptyResponseBodyError`,Pt=`vercel.ai.error.${Nt}`,Ft=Symbol.for(Pt),It=class extends A{constructor({message:e=`Empty response body`}={}){super({name:Nt,message:e}),this[Ft]=!0}static isInstance(e){return A.hasMarker(e,Pt)}};function Lt(e){return e==null?`unknown error`:typeof e==`string`?e:e instanceof Error?e.toString():JSON.stringify(e)}var Rt=`AI_InvalidArgumentError`,zt=`vercel.ai.error.${Rt}`,Bt=Symbol.for(zt),Vt=class extends A{constructor({message:e,cause:t,argument:n}){super({name:Rt,message:e,cause:t}),this[Bt]=!0,this.argument=n}static isInstance(e){return A.hasMarker(e,zt)}},Ht=`AI_JSONParseError`,Ut=`vercel.ai.error.${Ht}`,Wt=Symbol.for(Ut),Gt=class extends A{constructor({text:e,cause:t}){super({name:Ht,message:`JSON parsing failed: Text: ${e}.\nError message: ${Lt(t)}`,cause:t}),this[Wt]=!0,this.text=e}static isInstance(e){return A.hasMarker(e,Ut)}},Kt=`AI_TypeValidationError`,qt=`vercel.ai.error.${Kt}`,Jt=Symbol.for(qt),j=class e extends A{constructor({value:e,cause:t,context:n}){let r=`Type validation failed`;if(n?.field&&(r+=` for ${n.field}`),n?.entityName||n?.entityId){r+=` (`;let e=[];n.entityName&&e.push(n.entityName),n.entityId&&e.push(`id: "${n.entityId}"`),r+=e.join(`, `),r+=`)`}super({name:Kt,message:`${r}: Value: ${JSON.stringify(e)}.\nError message: ${Lt(t)}`,cause:t}),this[Jt]=!0,this.value=e,this.context=n}static isInstance(e){return A.hasMarker(e,qt)}static wrap({value:t,cause:n,context:r}){return e.isInstance(n)&&n.value===t&&n.context?.field===r?.field&&n.context?.entityName===r?.entityName&&n.context?.entityId===r?.entityId?n:new e({value:t,cause:n,context:r})}},Yt=`AI_UnsupportedFunctionalityError`,Xt=`vercel.ai.error.${Yt}`,Zt=Symbol.for(Xt),Qt=class extends A{constructor({functionality:e,message:t=`'${e}' functionality not supported.`}){super({name:Yt,message:t}),this[Zt]=!0,this.functionality=e}static isInstance(e){return A.hasMarker(e,Xt)}},$t=class extends Error{constructor(e,t){super(e),this.name=`ParseError`,this.type=t.type,this.field=t.field,this.value=t.value,this.line=t.line}},en=10,tn=13,M=32;function nn(e){}function rn(e){if(typeof e==`function`)throw TypeError("`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?");let{onEvent:t=nn,onError:n=nn,onRetry:r=nn,onComment:i,maxBufferSize:a}=e,o=[],s=0,c=!0,l,u=``,d=0,f,p=!1;function m(e){if(p)throw Error("Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.");if(c&&(c=!1,e.charCodeAt(0)===239&&e.charCodeAt(1)===187&&e.charCodeAt(2)===191&&(e=e.slice(3))),o.length===0){let t=g(e);t!==``&&(o.push(t),s=t.length),h();return}if(e.indexOf(`
2
+ `)===-1&&e.indexOf(`\r`)===-1){o.push(e),s+=e.length,h();return}o.push(e);let t=o.join(``);o.length=0,s=0;let n=g(t);n!==``&&(o.push(n),s=n.length),h()}function h(){a!==void 0&&(s+u.length<=a||(p=!0,o.length=0,s=0,l=void 0,u=``,d=0,f=void 0,n(new $t(`Buffered data exceeded max buffer size of ${a} characters`,{type:`max-buffer-size-exceeded`}))))}function g(e){let n=0;if(e.indexOf(`\r`)===-1){let r=e.indexOf(`
3
+ `,n);for(;r!==-1;){if(n===r){d>0&&t({id:l,event:f,data:u}),l=void 0,u=``,d=0,f=void 0,n=r+1,r=e.indexOf(`
4
+ `,n);continue}let i=e.charCodeAt(n);if(an(e,n,i)){let i=e.charCodeAt(n+5)===M?n+6:n+5,a=e.slice(i,r);if(d===0&&e.charCodeAt(r+1)===en){t({id:l,event:f,data:a}),l=void 0,u=``,f=void 0,n=r+2,r=e.indexOf(`
5
+ `,n);continue}u=d===0?a:`${u}
6
+ ${a}`,d++}else on(e,n,i)?f=e.slice(e.charCodeAt(n+6)===M?n+7:n+6,r)||void 0:_(e,n,r);n=r+1,r=e.indexOf(`
7
+ `,n)}return e.slice(n)}for(;n<e.length;){let t=e.indexOf(`\r`,n),r=e.indexOf(`
8
+ `,n),i=-1;if(t!==-1&&r!==-1?i=t<r?t:r:t===-1?r!==-1&&(i=r):i=t===e.length-1?-1:t,i===-1)break;_(e,n,i),n=i+1,e.charCodeAt(n-1)===tn&&e.charCodeAt(n)===en&&n++}return e.slice(n)}function _(e,t,n){if(t===n){y();return}let r=e.charCodeAt(t);if(an(e,t,r)){let r=e.charCodeAt(t+5)===M?t+6:t+5,i=e.slice(r,n);u=d===0?i:`${u}
9
+ ${i}`,d++;return}if(on(e,t,r)){f=e.slice(e.charCodeAt(t+6)===M?t+7:t+6,n)||void 0;return}if(r===105&&e.charCodeAt(t+1)===100&&e.charCodeAt(t+2)===58){let r=e.slice(e.charCodeAt(t+3)===M?t+4:t+3,n);r.includes(`\0`)||(l=r);return}if(r===58){if(i){let r=e.slice(t,n);i(r.slice(e.charCodeAt(t+1)===M?2:1))}return}let a=e.slice(t,n),o=a.indexOf(`:`);if(o===-1){v(a,``,a);return}let s=a.slice(0,o),c=a.charCodeAt(o+1)===M?2:1;v(s,a.slice(o+c),a)}function v(e,t,i){switch(e){case`event`:f=t||void 0;break;case`data`:u=d===0?t:`${u}
10
+ ${t}`,d++;break;case`id`:t.includes(`\0`)||(l=t);break;case`retry`:/^\d+$/.test(t)?r(parseInt(t,10)):n(new $t(`Invalid \`retry\` value: "${t}"`,{type:`invalid-retry`,value:t,line:i}));break;default:n(new $t(`Unknown field "${e.length>20?`${e.slice(0,20)}\u2026`:e}"`,{type:`unknown-field`,field:e,value:t,line:i}))}}function y(){d>0&&t({id:l,event:f,data:u}),l=void 0,u=``,d=0,f=void 0}function b(e={}){if(e.consume&&o.length>0){let e=o.join(``);_(e,0,e.length)}c=!0,l=void 0,u=``,d=0,f=void 0,o.length=0,s=0,p=!1}return{feed:m,reset:b}}function an(e,t,n){return n===100&&e.charCodeAt(t+1)===97&&e.charCodeAt(t+2)===116&&e.charCodeAt(t+3)===97&&e.charCodeAt(t+4)===58}function on(e,t,n){return n===101&&e.charCodeAt(t+1)===118&&e.charCodeAt(t+2)===101&&e.charCodeAt(t+3)===110&&e.charCodeAt(t+4)===116&&e.charCodeAt(t+5)===58}var sn=class extends TransformStream{constructor({onError:e,onRetry:t,onComment:n,maxBufferSize:r}={}){let i;super({start(a){i=rn({onEvent:e=>{a.enqueue(e)},onError(t){typeof e==`function`&&e(t),(e===`terminate`||t.type===`max-buffer-size-exceeded`)&&a.error(t)},onRetry:t,onComment:n,maxBufferSize:r})},transform(e){i.feed(e)}})}};new TextDecoder;var{btoa:cn,atob:ln}=globalThis;function N(e){if(e==null)return{};let t={};if(e instanceof Headers)e.forEach((e,n)=>{t[n.toLowerCase()]=e});else{Array.isArray(e)||(e=Object.entries(e));for(let[n,r]of e)r!=null&&(t[n.toLowerCase()]=r)}return t}var P=({prefix:e,size:t=16,alphabet:n=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`,separator:r=`-`}={})=>{let i=()=>{let e=n.length,r=Array(t);for(let i=0;i<t;i++)r[i]=n[Math.random()*e|0];return r.join(``)};if(e==null)return i;if(n.includes(r))throw new Vt({argument:`separator`,message:`The separator "${r}" must not be part of the alphabet "${n}".`});return()=>`${e}${r}${i()}`},un=P();function dn(e){return globalThis.Buffer?.isBuffer(e)??!1}var fn=/"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*:/,pn=/"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;function mn(e){let t=JSON.parse(e);return typeof t!=`object`||!t||fn.test(e)===!1&&pn.test(e)===!1?t:hn(t)}function hn(e){let t=[e];for(;t.length;){let e=t;t=[];for(let n of e){if(Object.prototype.hasOwnProperty.call(n,`__proto__`)||Object.prototype.hasOwnProperty.call(n,`constructor`)&&n.constructor!==null&&typeof n.constructor==`object`&&Object.prototype.hasOwnProperty.call(n.constructor,`prototype`))throw SyntaxError(`Object contains forbidden prototype property`);for(let e in n){let r=n[e];r&&typeof r==`object`&&t.push(r)}}}return e}function gn(e){let{stackTraceLimit:t}=Error;try{Error.stackTraceLimit=0}catch{return mn(e)}try{return mn(e)}finally{Error.stackTraceLimit=t}}function _n(e){if(e.type===`object`||Array.isArray(e.type)&&e.type.includes(`object`)){let{additionalProperties:t}=e;e.additionalProperties=t!=null&&typeof t!=`boolean`&&F(t);let{properties:n}=e;if(n!=null)for(let e of Object.keys(n))n[e]=F(n[e])}e.items!=null&&(e.items=Array.isArray(e.items)?e.items.map(F):F(e.items)),e.anyOf!=null&&(e.anyOf=e.anyOf.map(F)),e.allOf!=null&&(e.allOf=e.allOf.map(F)),e.oneOf!=null&&(e.oneOf=e.oneOf.map(F));let{definitions:t}=e;if(t!=null)for(let e of Object.keys(t))t[e]=F(t[e]);return e}function F(e){return typeof e==`boolean`?e:_n(e)}var vn=Symbol(`Let zodToJsonSchema decide on which parser to use`),yn={name:void 0,$refStrategy:`root`,basePath:[`#`],effectStrategy:`input`,pipeStrategy:`all`,dateStrategy:`format:date-time`,mapStrategy:`entries`,removeAdditionalStrategy:`passthrough`,allowedAdditionalProperties:!0,rejectedAdditionalProperties:!1,definitionPath:`definitions`,strictUnions:!1,definitions:{},errorMessages:!1,patternStrategy:`escape`,applyRegexFlags:!1,emailStrategy:`format:email`,base64Strategy:`contentEncoding:base64`,nameStrategy:`ref`},bn=e=>typeof e==`string`?{...yn,name:e}:{...yn,...e};function I(){return{}}function xn(e,t){let n={type:`array`};return e.type?._def&&e.type?._def?.typeName!==`ZodAny`&&(n.items=B(e.type._def,{...t,currentPath:[...t.currentPath,`items`]})),e.minLength&&(n.minItems=e.minLength.value),e.maxLength&&(n.maxItems=e.maxLength.value),e.exactLength&&(n.minItems=e.exactLength.value,n.maxItems=e.exactLength.value),n}function Sn(e){let t={type:`integer`,format:`int64`};if(!e.checks)return t;for(let n of e.checks)switch(n.kind){case`min`:n.inclusive?t.minimum=n.value:t.exclusiveMinimum=n.value;break;case`max`:n.inclusive?t.maximum=n.value:t.exclusiveMaximum=n.value;break;case`multipleOf`:t.multipleOf=n.value}return t}function Cn(){return{type:`boolean`}}function wn(e,t){return B(e.type._def,t)}var Tn=(e,t)=>B(e.innerType._def,t);function En(e,t,n){let r=n??t.dateStrategy;if(Array.isArray(r))return{anyOf:r.map(n=>En(e,t,n))};switch(r){case`string`:case`format:date-time`:return{type:`string`,format:`date-time`};case`format:date`:return{type:`string`,format:`date`};case`integer`:return Dn(e)}}var Dn=e=>{let t={type:`integer`,format:`unix-time`};for(let n of e.checks)switch(n.kind){case`min`:t.minimum=n.value;break;case`max`:t.maximum=n.value}return t};function On(e,t){return{...B(e.innerType._def,t),default:e.defaultValue()}}function kn(e,t){return t.effectStrategy===`input`?B(e.schema._def,t):I()}function An(e){return{type:`string`,enum:Array.from(e.values)}}var jn=e=>`type`in e&&e.type===`string`?!1:`allOf`in e;function Mn(e,t){let n=[B(e.left._def,{...t,currentPath:[...t.currentPath,`allOf`,`0`]}),B(e.right._def,{...t,currentPath:[...t.currentPath,`allOf`,`1`]})].filter(e=>!!e),r=[];return n.forEach(e=>{if(jn(e))r.push(...e.allOf);else{let t=e;if(`additionalProperties`in e&&e.additionalProperties===!1){let{additionalProperties:n,...r}=e;t=r}r.push(t)}}),r.length?{allOf:r}:void 0}function Nn(e){let t=typeof e.value;return t!==`bigint`&&t!==`number`&&t!==`boolean`&&t!==`string`?{type:Array.isArray(e.value)?`array`:`object`}:{type:t===`bigint`?`integer`:t,const:e.value}}var Pn=void 0,L={cuid:/^[cC][^\s-]{8,}$/,cuid2:/^[0-9a-z]+$/,ulid:/^[0-9A-HJKMNP-TV-Z]{26}$/,email:/^(?!\.)(?!.*\.\.)([a-zA-Z0-9_'+\-.]*)[a-zA-Z0-9_+-]@([a-zA-Z0-9][a-zA-Z0-9-]*\.)+[a-zA-Z]{2,}$/,emoji:()=>(Pn===void 0&&(Pn=RegExp(`^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`,`u`)),Pn),uuid:/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/,ipv4:/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,ipv4Cidr:/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/,ipv6:/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/,ipv6Cidr:/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,base64:/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,base64url:/^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/,nanoid:/^[a-zA-Z0-9_-]{21}$/,jwt:/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/};function Fn(e,t){let n={type:`string`};if(e.checks)for(let r of e.checks)switch(r.kind){case`min`:n.minLength=typeof n.minLength==`number`?Math.max(n.minLength,r.value):r.value;break;case`max`:n.maxLength=typeof n.maxLength==`number`?Math.min(n.maxLength,r.value):r.value;break;case`email`:switch(t.emailStrategy){case`format:email`:R(n,`email`,r.message,t);break;case`format:idn-email`:R(n,`idn-email`,r.message,t);break;case`pattern:zod`:z(n,L.email,r.message,t)}break;case`url`:R(n,`uri`,r.message,t);break;case`uuid`:R(n,`uuid`,r.message,t);break;case`regex`:z(n,r.regex,r.message,t);break;case`cuid`:z(n,L.cuid,r.message,t);break;case`cuid2`:z(n,L.cuid2,r.message,t);break;case`startsWith`:z(n,RegExp(`^${In(r.value,t)}`),r.message,t);break;case`endsWith`:z(n,RegExp(`${In(r.value,t)}$`),r.message,t);break;case`datetime`:R(n,`date-time`,r.message,t);break;case`date`:R(n,`date`,r.message,t);break;case`time`:R(n,`time`,r.message,t);break;case`duration`:R(n,`duration`,r.message,t);break;case`length`:n.minLength=typeof n.minLength==`number`?Math.max(n.minLength,r.value):r.value,n.maxLength=typeof n.maxLength==`number`?Math.min(n.maxLength,r.value):r.value;break;case`includes`:z(n,RegExp(In(r.value,t)),r.message,t);break;case`ip`:r.version!==`v6`&&R(n,`ipv4`,r.message,t),r.version!==`v4`&&R(n,`ipv6`,r.message,t);break;case`base64url`:z(n,L.base64url,r.message,t);break;case`jwt`:z(n,L.jwt,r.message,t);break;case`cidr`:r.version!==`v6`&&z(n,L.ipv4Cidr,r.message,t),r.version!==`v4`&&z(n,L.ipv6Cidr,r.message,t);break;case`emoji`:z(n,L.emoji(),r.message,t);break;case`ulid`:z(n,L.ulid,r.message,t);break;case`base64`:switch(t.base64Strategy){case`format:binary`:R(n,`binary`,r.message,t);break;case`contentEncoding:base64`:n.contentEncoding=`base64`;break;case`pattern:zod`:z(n,L.base64,r.message,t)}break;case`nanoid`:z(n,L.nanoid,r.message,t)}return n}function In(e,t){return t.patternStrategy===`escape`?Rn(e):e}var Ln=new Set(`ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789`);function Rn(e){let t=``;for(let n=0;n<e.length;n++)Ln.has(e[n])||(t+=`\\`),t+=e[n];return t}function R(e,t,n,r){e.format||e.anyOf?.some(e=>e.format)?(e.anyOf||=[],e.format&&(e.anyOf.push({format:e.format}),delete e.format),e.anyOf.push({format:t,...n&&r.errorMessages&&{errorMessage:{format:n}}})):e.format=t}function z(e,t,n,r){e.pattern||e.allOf?.some(e=>e.pattern)?(e.allOf||=[],e.pattern&&(e.allOf.push({pattern:e.pattern}),delete e.pattern),e.allOf.push({pattern:zn(t,r),...n&&r.errorMessages&&{errorMessage:{pattern:n}}})):e.pattern=zn(t,r)}function zn(e,t){if(!t.applyRegexFlags||!e.flags)return e.source;let n={i:e.flags.includes(`i`),m:e.flags.includes(`m`),s:e.flags.includes(`s`)},r=n.i?e.source.toLowerCase():e.source,i=``,a=!1,o=!1,s=!1;for(let e=0;e<r.length;e++){if(a){i+=r[e],a=!1;continue}if(n.i){if(o){if(r[e].match(/[a-z]/)){s?(i+=r[e],i+=`${r[e-2]}-${r[e]}`.toUpperCase(),s=!1):r[e+1]===`-`&&r[e+2]?.match(/[a-z]/)?(i+=r[e],s=!0):i+=`${r[e]}${r[e].toUpperCase()}`;continue}}else if(r[e].match(/[a-z]/)){i+=`[${r[e]}${r[e].toUpperCase()}]`;continue}}if(n.m){if(r[e]===`^`){i+=`(^|(?<=[\r
11
+ ]))`;continue}if(r[e]===`$`){i+=`($|(?=[\r
12
+ ]))`;continue}}if(n.s&&r[e]===`.`){i+=o?`${r[e]}\r\n`:`[${r[e]}\r\n]`;continue}i+=r[e],r[e]===`\\`?a=!0:o&&r[e]===`]`?o=!1:!o&&r[e]===`[`&&(o=!0)}try{new RegExp(i)}catch{return console.warn(`Could not convert regex pattern at ${t.currentPath.join(`/`)} to a flag-independent form! Falling back to the flag-ignorant source`),e.source}return i}function Bn(e,t){let n={type:`object`,additionalProperties:B(e.valueType._def,{...t,currentPath:[...t.currentPath,`additionalProperties`]})??t.allowedAdditionalProperties};if(e.keyType?._def.typeName===`ZodString`&&e.keyType._def.checks?.length){let{type:r,...i}=Fn(e.keyType._def,t);return{...n,propertyNames:i}}if(e.keyType?._def.typeName===`ZodEnum`)return{...n,propertyNames:{enum:e.keyType._def.values}};if(e.keyType?._def.typeName===`ZodBranded`&&e.keyType._def.type._def.typeName===`ZodString`&&e.keyType._def.type._def.checks?.length){let{type:r,...i}=wn(e.keyType._def,t);return{...n,propertyNames:i}}return n}function Vn(e,t){return t.mapStrategy===`record`?Bn(e,t):{type:`array`,maxItems:125,items:{type:`array`,items:[B(e.keyType._def,{...t,currentPath:[...t.currentPath,`items`,`items`,`0`]})||I(),B(e.valueType._def,{...t,currentPath:[...t.currentPath,`items`,`items`,`1`]})||I()],minItems:2,maxItems:2}}}function Hn(e){let t=e.values,n=Object.keys(e.values).filter(e=>typeof t[t[e]]!=`number`).map(e=>t[e]),r=Array.from(new Set(n.map(e=>typeof e)));return{type:r.length===1?r[0]===`string`?`string`:`number`:[`string`,`number`],enum:n}}function Un(){return{not:I()}}function Wn(){return{type:`null`}}var Gn={ZodString:`string`,ZodNumber:`number`,ZodBigInt:`integer`,ZodBoolean:`boolean`,ZodNull:`null`};function Kn(e,t){let n=e.options instanceof Map?Array.from(e.options.values()):e.options;if(n.every(e=>e._def.typeName in Gn&&(!e._def.checks||!e._def.checks.length))){let e=n.reduce((e,t)=>{let n=Gn[t._def.typeName];return n&&!e.includes(n)?[...e,n]:e},[]);return{type:e.length>1?e:e[0]}}if(n.every(e=>e._def.typeName===`ZodLiteral`&&!e.description)){let e=n.reduce((e,t)=>{let n=typeof t._def.value;switch(n){case`string`:case`number`:case`boolean`:return[...e,n];case`bigint`:return[...e,`integer`];case`object`:if(t._def.value===null)return[...e,`null`];default:return e}},[]);if(e.length===n.length){let t=e.filter((e,t,n)=>n.indexOf(e)===t);return{type:t.length>1?t:t[0],enum:n.reduce((e,t)=>e.includes(t._def.value)?e:[...e,t._def.value],[])}}}else if(n.every(e=>e._def.typeName===`ZodEnum`))return{type:`string`,enum:n.reduce((e,t)=>[...e,...t._def.values.filter(t=>!e.includes(t))],[])};return qn(e,t)}var qn=(e,t)=>{let n=(e.options instanceof Map?Array.from(e.options.values()):e.options).map((e,n)=>B(e._def,{...t,currentPath:[...t.currentPath,`anyOf`,`${n}`]})).filter(e=>!!e&&(!t.strictUnions||typeof e==`object`&&Object.keys(e).length>0));return n.length?{anyOf:n}:void 0};function Jn(e,t){if([`ZodString`,`ZodNumber`,`ZodBigInt`,`ZodBoolean`,`ZodNull`].includes(e.innerType._def.typeName)&&(!e.innerType._def.checks||!e.innerType._def.checks.length))return{type:[Gn[e.innerType._def.typeName],`null`]};let n=B(e.innerType._def,{...t,currentPath:[...t.currentPath,`anyOf`,`0`]});return n&&{anyOf:[n,{type:`null`}]}}function Yn(e){let t={type:`number`};if(!e.checks)return t;for(let n of e.checks)switch(n.kind){case`int`:t.type=`integer`;break;case`min`:n.inclusive?t.minimum=n.value:t.exclusiveMinimum=n.value;break;case`max`:n.inclusive?t.maximum=n.value:t.exclusiveMaximum=n.value;break;case`multipleOf`:t.multipleOf=n.value}return t}function Xn(e,t){let n={type:`object`,properties:{}},r=[],i=e.shape();for(let e in i){let a=i[e];if(a===void 0||a._def===void 0)continue;let o=Qn(a),s=B(a._def,{...t,currentPath:[...t.currentPath,`properties`,e],propertyPath:[...t.currentPath,`properties`,e]});s!==void 0&&(n.properties[e]=s,o||r.push(e))}r.length&&(n.required=r);let a=Zn(e,t);return a!==void 0&&(n.additionalProperties=a),n}function Zn(e,t){if(e.catchall._def.typeName!==`ZodNever`)return B(e.catchall._def,{...t,currentPath:[...t.currentPath,`additionalProperties`]});switch(e.unknownKeys){case`passthrough`:return t.allowedAdditionalProperties;case`strict`:return t.rejectedAdditionalProperties;case`strip`:return t.removeAdditionalStrategy===`strict`?t.allowedAdditionalProperties:t.rejectedAdditionalProperties}}function Qn(e){try{return e.isOptional()}catch{return!0}}var $n=(e,t)=>{if(t.currentPath.toString()===t.propertyPath?.toString())return B(e.innerType._def,t);let n=B(e.innerType._def,{...t,currentPath:[...t.currentPath,`anyOf`,`1`]});return n?{anyOf:[{not:I()},n]}:I()},er=(e,t)=>{if(t.pipeStrategy===`input`)return B(e.in._def,t);if(t.pipeStrategy===`output`)return B(e.out._def,t);let n=B(e.in._def,{...t,currentPath:[...t.currentPath,`allOf`,`0`]});return{allOf:[n,B(e.out._def,{...t,currentPath:[...t.currentPath,`allOf`,n?`1`:`0`]})].filter(e=>e!==void 0)}};function tr(e,t){return B(e.type._def,t)}function nr(e,t){let n={type:`array`,uniqueItems:!0,items:B(e.valueType._def,{...t,currentPath:[...t.currentPath,`items`]})};return e.minSize&&(n.minItems=e.minSize.value),e.maxSize&&(n.maxItems=e.maxSize.value),n}function rr(e,t){return e.rest?{type:`array`,minItems:e.items.length,items:e.items.map((e,n)=>B(e._def,{...t,currentPath:[...t.currentPath,`items`,`${n}`]})).reduce((e,t)=>t===void 0?e:[...e,t],[]),additionalItems:B(e.rest._def,{...t,currentPath:[...t.currentPath,`additionalItems`]})}:{type:`array`,minItems:e.items.length,maxItems:e.items.length,items:e.items.map((e,n)=>B(e._def,{...t,currentPath:[...t.currentPath,`items`,`${n}`]})).reduce((e,t)=>t===void 0?e:[...e,t],[])}}function ir(){return{not:I()}}function ar(){return I()}var or=(e,t)=>B(e.innerType._def,t),sr=(e,t,n)=>{switch(t){case`ZodString`:return Fn(e,n);case`ZodNumber`:return Yn(e);case`ZodObject`:return Xn(e,n);case`ZodBigInt`:return Sn(e);case`ZodBoolean`:return Cn();case`ZodDate`:return En(e,n);case`ZodUndefined`:return ir();case`ZodNull`:return Wn();case`ZodArray`:return xn(e,n);case`ZodUnion`:case`ZodDiscriminatedUnion`:return Kn(e,n);case`ZodIntersection`:return Mn(e,n);case`ZodTuple`:return rr(e,n);case`ZodRecord`:return Bn(e,n);case`ZodLiteral`:return Nn(e);case`ZodEnum`:return An(e);case`ZodNativeEnum`:return Hn(e);case`ZodNullable`:return Jn(e,n);case`ZodOptional`:return $n(e,n);case`ZodMap`:return Vn(e,n);case`ZodSet`:return nr(e,n);case`ZodLazy`:return()=>e.getter()._def;case`ZodPromise`:return tr(e,n);case`ZodNaN`:case`ZodNever`:return Un();case`ZodEffects`:return kn(e,n);case`ZodAny`:return I();case`ZodUnknown`:return ar();case`ZodDefault`:return On(e,n);case`ZodBranded`:return wn(e,n);case`ZodReadonly`:return or(e,n);case`ZodCatch`:return Tn(e,n);case`ZodPipeline`:return er(e,n);case`ZodFunction`:case`ZodVoid`:case`ZodSymbol`:return;default:return(e=>void 0)(t)}},cr=(e,t)=>{let n=0;for(;n<e.length&&n<t.length&&e[n]===t[n];n++);return[(e.length-n).toString(),...t.slice(n)].join(`/`)};function B(e,t,n=!1){let r=t.seen.get(e);if(t.override){let i=t.override?.(e,t,r,n);if(i!==vn)return i}if(r&&!n){let e=lr(r,t);if(e!==void 0)return e}let i={def:e,path:t.currentPath,jsonSchema:void 0};t.seen.set(e,i);let a=sr(e,e.typeName,t),o=typeof a==`function`?B(a(),t):a;if(o&&ur(e,t,o),t.postProcess){let n=t.postProcess(o,e,t);return i.jsonSchema=o,n}return i.jsonSchema=o,o}var lr=(e,t)=>{switch(t.$refStrategy){case`root`:return{$ref:e.path.join(`/`)};case`relative`:return{$ref:cr(t.currentPath,e.path)};case`none`:case`seen`:return e.path.length<t.currentPath.length&&e.path.every((e,n)=>t.currentPath[n]===e)?(console.warn(`Recursive reference detected at ${t.currentPath.join(`/`)}! Defaulting to any`),I()):t.$refStrategy===`seen`?I():void 0}},ur=(e,t,n)=>(e.description&&(n.description=e.description),n),dr=e=>{let t=bn(e),n=t.name===void 0?t.basePath:[...t.basePath,t.definitionPath,t.name];return{...t,currentPath:n,propertyPath:void 0,seen:new Map(Object.entries(t.definitions).map(([e,n])=>[n._def,{def:n._def,path:[...t.basePath,t.definitionPath,e],jsonSchema:void 0}]))}},fr=(e,t)=>{let n=dr(t),r=typeof t==`object`&&t.definitions?Object.entries(t.definitions).reduce((e,[t,r])=>({...e,[t]:B(r._def,{...n,currentPath:[...n.basePath,n.definitionPath,t]},!0)??I()}),{}):void 0,i=typeof t==`string`?t:t?.nameStrategy===`title`?void 0:t?.name,a=B(e._def,i===void 0?n:{...n,currentPath:[...n.basePath,n.definitionPath,i]},!1)??I(),o=typeof t==`object`&&t.name!==void 0&&t.nameStrategy===`title`?t.name:void 0;o!==void 0&&(a.title=o);let s=i===void 0?r?{...a,[n.definitionPath]:r}:a:{$ref:[...n.$refStrategy===`relative`?[]:n.basePath,n.definitionPath,i].join(`/`),[n.definitionPath]:{...r,[i]:a}};return s.$schema=`http://json-schema.org/draft-07/schema#`,s},pr=Symbol.for(`vercel.ai.schema`);function mr(e){let t;return()=>(t??=e(),t)}function V(e,{validate:t}={}){return{[pr]:!0,_type:void 0,get jsonSchema(){return typeof e==`function`&&(e=e()),e},validate:t}}function hr(e){return typeof e==`object`&&!!e&&pr in e&&e[pr]===!0&&`jsonSchema`in e&&`validate`in e}function gr(e){return e==null?V({type:`object`,properties:{},additionalProperties:!1}):hr(e)?e:`~standard`in e?e[`~standard`].vendor===`zod`?Sr(e):_r(e):e()}function _r(e){return V(()=>{if(!vr(e))throw Error(`Standard schema vendor '${e[`~standard`].vendor}' does not support JSON Schema conversion.`);return _n(e[`~standard`].jsonSchema.input({target:`draft-07`}))},{validate:async t=>{let n=await e[`~standard`].validate(t);return`value`in n?{success:!0,value:n.value}:{success:!1,error:new j({value:t,cause:n.issues})}}})}function vr(e){return e[`~standard`].jsonSchema!=null}function yr(e,t){let n=t?.useReferences??!1;return V(()=>fr(e,{$refStrategy:n?`root`:`none`}),{validate:async t=>{let n=await e.safeParseAsync(t);return n.success?{success:!0,value:n.data}:{success:!1,error:n.error}}})}function br(e,t){let r=t?.useReferences??!1;return V(()=>_n(b(e,{target:`draft-7`,io:`input`,reused:r?`ref`:`inline`})),{validate:async t=>{let r=await n(e,t);return r.success?{success:!0,value:r.data}:{success:!1,error:r.error}}})}function xr(e){return`_zod`in e}function Sr(e,t){return xr(e)?br(e,t):yr(e,t)}async function Cr({value:e,schema:t,context:n}){let r=await wr({value:e,schema:t,context:n});if(!r.success)throw j.wrap({value:e,cause:r.error,context:n});return r.value}async function wr({value:e,schema:t,context:n}){let r=gr(t);try{if(r.validate==null)return{success:!0,value:e,rawValue:e};let t=await r.validate(e);return t.success?{success:!0,value:t.value,rawValue:e}:{success:!1,error:j.wrap({value:e,cause:t.error,context:n}),rawValue:e}}catch(t){return{success:!1,error:j.wrap({value:e,cause:t,context:n}),rawValue:e}}}async function Tr({text:e,schema:t}){try{let n=gn(e);return t==null?{success:!0,value:n,rawValue:n}:await wr({value:n,schema:t})}catch(t){return{success:!1,error:Gt.isInstance(t)?t:new Gt({text:e,cause:t}),rawValue:void 0}}}function Er({stream:e,schema:t}){return e.pipeThrough(new TextDecoderStream).pipeThrough(new sn).pipeThrough(new TransformStream({async transform({data:e},n){e!==`[DONE]`&&n.enqueue(await Tr({text:e,schema:t}))}}))}async function H(e){return typeof e==`function`&&(e=e()),e}new TextDecoder;var Dr=`AI_InvalidArgumentError`,Or=`vercel.ai.error.${Dr}`,kr=Symbol.for(Or),U=class extends A{constructor({parameter:e,value:t,message:n}){super({name:Dr,message:`Invalid argument for parameter ${e}: ${n}`}),this[kr]=!0,this.parameter=e,this.value=t}static isInstance(e){return A.hasMarker(e,Or)}},Ar=`AI_UIMessageStreamError`,jr=`vercel.ai.error.${Ar}`,Mr=Symbol.for(jr),W=class extends A{constructor({chunkType:e,chunkId:t,message:n}){super({name:Ar,message:n}),this[Mr]=!0,this.chunkType=e,this.chunkId=t}static isInstance(e){return A.hasMarker(e,jr)}};function Nr(e,t){if(e===void 0&&t===void 0)return;if(e===void 0)return t;if(t===void 0)return e;let n={...e};for(let r in t)if(r!==`__proto__`&&r!==`constructor`&&r!==`prototype`&&Object.prototype.hasOwnProperty.call(t,r)){let i=t[r];if(i===void 0)continue;let a=r in e?e[r]:void 0,o=typeof i==`object`&&!!i&&!Array.isArray(i)&&!(i instanceof Date)&&!(i instanceof RegExp),s=typeof a==`object`&&!!a&&!Array.isArray(a)&&!(a instanceof Date)&&!(a instanceof RegExp);n[r]=o&&s?Nr(a,i):i}return n}var G={array:m,boolean:_,custom:s,discriminatedUnion:d,enum:v,instanceof:p,lazy:c,literal:h,looseObject:f,never:u,null:g,number:l,object:i,record:y,string:o,union:x,unknown:r},K=G.lazy(()=>G.union([G.null(),G.string(),G.number(),G.boolean(),G.record(G.string(),K.optional()),G.array(K)])),q=G.record(G.string(),G.record(G.string(),K.optional())),J=G.union([G.string(),G.instanceof(Uint8Array),G.instanceof(ArrayBuffer),G.custom(dn,{message:`Must be a Buffer`})]),Pr=G.record(G.string(),G.string()),Fr=G.object({type:G.literal(`text`),text:G.string(),providerOptions:q.optional()}),Ir=G.object({type:G.literal(`image`),image:G.union([J,G.instanceof(URL),Pr]),mediaType:G.string().optional(),providerOptions:q.optional()}),Lr=G.discriminatedUnion(`type`,[G.object({type:G.literal(`data`),data:J}),G.object({type:G.literal(`url`),url:G.instanceof(URL)}),G.object({type:G.literal(`reference`),reference:Pr}),G.object({type:G.literal(`text`),text:G.string()})]),Rr=G.discriminatedUnion(`type`,[G.object({type:G.literal(`data`),data:J}),G.object({type:G.literal(`url`),url:G.instanceof(URL)})]),zr=G.object({type:G.literal(`file`),data:G.union([Lr,J,G.instanceof(URL),Pr]),filename:G.string().optional(),mediaType:G.string(),providerOptions:q.optional()}),Br=G.object({type:G.literal(`reasoning`),text:G.string(),providerOptions:q.optional()}),Vr=G.object({type:G.literal(`custom`),kind:G.string().transform(e=>e),providerOptions:q.optional()}),Hr=G.object({type:G.literal(`reasoning-file`),data:G.union([Rr,J,G.instanceof(URL)]),mediaType:G.string(),providerOptions:q.optional()}),Ur=G.object({type:G.literal(`tool-call`),toolCallId:G.string(),toolName:G.string(),input:G.unknown(),providerOptions:q.optional(),providerExecuted:G.boolean().optional()}),Wr=G.discriminatedUnion(`type`,[G.object({type:G.literal(`text`),value:G.string(),providerOptions:q.optional()}),G.object({type:G.literal(`json`),value:K,providerOptions:q.optional()}),G.object({type:G.literal(`execution-denied`),reason:G.string().optional(),providerOptions:q.optional()}),G.object({type:G.literal(`error-text`),value:G.string(),providerOptions:q.optional()}),G.object({type:G.literal(`error-json`),value:K,providerOptions:q.optional()}),G.object({type:G.literal(`content`),value:G.array(G.union([G.object({type:G.literal(`text`),text:G.string(),providerOptions:q.optional()}),G.object({type:G.literal(`file`),data:Lr,mediaType:G.string(),filename:G.string().optional(),providerOptions:q.optional()}),G.object({type:G.literal(`file-data`),data:G.string(),mediaType:G.string(),filename:G.string().optional(),providerOptions:q.optional()}),G.object({type:G.literal(`file-url`),url:G.string(),mediaType:G.string().optional(),providerOptions:q.optional()}),G.object({type:G.literal(`file-id`),fileId:G.union([G.string(),G.record(G.string(),G.string())]),providerOptions:q.optional()}),G.object({type:G.literal(`file-reference`),providerReference:G.record(G.string(),G.string()),providerOptions:q.optional()}),G.object({type:G.literal(`image-data`),data:G.string(),mediaType:G.string(),providerOptions:q.optional()}),G.object({type:G.literal(`image-url`),url:G.string(),providerOptions:q.optional()}),G.object({type:G.literal(`image-file-id`),fileId:G.union([G.string(),G.record(G.string(),G.string())]),providerOptions:q.optional()}),G.object({type:G.literal(`image-file-reference`),providerReference:G.record(G.string(),G.string()),providerOptions:q.optional()}),G.object({type:G.literal(`custom`),providerOptions:q.optional()})]))})]),Gr=G.object({type:G.literal(`tool-result`),toolCallId:G.string(),toolName:G.string(),output:Wr,providerOptions:q.optional()}),Kr=G.object({type:G.literal(`tool-approval-request`),approvalId:G.string(),toolCallId:G.string(),reason:G.string().optional(),isAutomatic:G.boolean().optional(),signature:G.string().optional(),inputSchemaInput:G.unknown().optional()}),qr=G.object({type:G.literal(`tool-approval-response`),approvalId:G.string(),approved:G.boolean(),reason:G.string().optional()}),Jr=G.object({role:G.literal(`system`),content:G.string(),providerOptions:q.optional()}),Yr=G.object({role:G.literal(`user`),content:G.union([G.string(),G.array(G.union([Fr,Ir,zr]))]),providerOptions:q.optional()}),Xr=G.object({role:G.literal(`assistant`),content:G.union([G.string(),G.array(G.union([Fr,Vr,zr,Br,Hr,Ur,Gr,Kr]))]),providerOptions:q.optional()}),Zr=G.object({role:G.literal(`tool`),content:G.array(G.union([Gr,qr])),providerOptions:q.optional()});G.union([Jr,Yr,Xr,Zr]);function Qr(e){let t=[`ROOT`],n=-1,r=null,i=0;function a(e){return e>=`0`&&e<=`9`||e>=`A`&&e<=`F`||e>=`a`&&e<=`f`}function o(e,i,a){switch(e){case`"`:n=i,t.pop(),t.push(a),t.push(`INSIDE_STRING`);break;case`f`:case`t`:case`n`:n=i,r=i,t.pop(),t.push(a),t.push(`INSIDE_LITERAL`);break;case`-`:t.pop(),t.push(a),t.push(`INSIDE_NUMBER`);break;case`0`:case`1`:case`2`:case`3`:case`4`:case`5`:case`6`:case`7`:case`8`:case`9`:n=i,t.pop(),t.push(a),t.push(`INSIDE_NUMBER`);break;case`{`:n=i,t.pop(),t.push(a),t.push(`INSIDE_OBJECT_START`);break;case`[`:n=i,t.pop(),t.push(a),t.push(`INSIDE_ARRAY_START`)}}function s(e,r){switch(e){case`,`:t.pop(),t.push(`INSIDE_OBJECT_AFTER_COMMA`);break;case`}`:n=r,t.pop()}}function c(e,r){switch(e){case`,`:t.pop(),t.push(`INSIDE_ARRAY_AFTER_COMMA`);break;case`]`:n=r,t.pop()}}for(let l=0;l<e.length;l++){let u=e[l];switch(t[t.length-1]){case`ROOT`:o(u,l,`FINISH`);break;case`INSIDE_OBJECT_START`:switch(u){case`"`:t.pop(),t.push(`INSIDE_OBJECT_KEY`);break;case`}`:n=l,t.pop()}break;case`INSIDE_OBJECT_AFTER_COMMA`:u===`"`&&(t.pop(),t.push(`INSIDE_OBJECT_KEY`));break;case`INSIDE_OBJECT_KEY`:u===`"`&&(t.pop(),t.push(`INSIDE_OBJECT_AFTER_KEY`));break;case`INSIDE_OBJECT_AFTER_KEY`:u===`:`&&(t.pop(),t.push(`INSIDE_OBJECT_BEFORE_VALUE`));break;case`INSIDE_OBJECT_BEFORE_VALUE`:o(u,l,`INSIDE_OBJECT_AFTER_VALUE`);break;case`INSIDE_OBJECT_AFTER_VALUE`:s(u,l);break;case`INSIDE_STRING`:switch(u){case`"`:t.pop(),n=l;break;case`\\`:t.push(`INSIDE_STRING_ESCAPE`);break;default:n=l}break;case`INSIDE_ARRAY_START`:switch(u){case`]`:n=l,t.pop();break;default:n=l,o(u,l,`INSIDE_ARRAY_AFTER_VALUE`)}break;case`INSIDE_ARRAY_AFTER_VALUE`:switch(u){case`,`:t.pop(),t.push(`INSIDE_ARRAY_AFTER_COMMA`);break;case`]`:n=l,t.pop();break;default:n=l}break;case`INSIDE_ARRAY_AFTER_COMMA`:o(u,l,`INSIDE_ARRAY_AFTER_VALUE`);break;case`INSIDE_STRING_ESCAPE`:t.pop(),u===`u`?(i=0,t.push(`INSIDE_STRING_UNICODE_ESCAPE`)):n=l;break;case`INSIDE_STRING_UNICODE_ESCAPE`:a(u)&&(i++,i===4&&(t.pop(),n=l));break;case`INSIDE_NUMBER`:switch(u){case`0`:case`1`:case`2`:case`3`:case`4`:case`5`:case`6`:case`7`:case`8`:case`9`:n=l;break;case`e`:case`E`:case`-`:case`.`:break;case`,`:t.pop(),t[t.length-1]===`INSIDE_ARRAY_AFTER_VALUE`&&c(u,l),t[t.length-1]===`INSIDE_OBJECT_AFTER_VALUE`&&s(u,l);break;case`}`:t.pop(),t[t.length-1]===`INSIDE_OBJECT_AFTER_VALUE`&&s(u,l);break;case`]`:t.pop(),t[t.length-1]===`INSIDE_ARRAY_AFTER_VALUE`&&c(u,l);break;default:t.pop()}break;case`INSIDE_LITERAL`:{let i=e.substring(r,l+1);!`false`.startsWith(i)&&!`true`.startsWith(i)&&!`null`.startsWith(i)?(t.pop(),t[t.length-1]===`INSIDE_OBJECT_AFTER_VALUE`?s(u,l):t[t.length-1]===`INSIDE_ARRAY_AFTER_VALUE`&&c(u,l)):n=l;break}}}let l=e.slice(0,n+1);for(let n=t.length-1;n>=0;n--)switch(t[n]){case`INSIDE_STRING`:l+=`"`;break;case`INSIDE_OBJECT_KEY`:case`INSIDE_OBJECT_AFTER_KEY`:case`INSIDE_OBJECT_AFTER_COMMA`:case`INSIDE_OBJECT_START`:case`INSIDE_OBJECT_BEFORE_VALUE`:case`INSIDE_OBJECT_AFTER_VALUE`:l+=`}`;break;case`INSIDE_ARRAY_START`:case`INSIDE_ARRAY_AFTER_COMMA`:case`INSIDE_ARRAY_AFTER_VALUE`:l+=`]`;break;case`INSIDE_LITERAL`:{let t=e.substring(r,e.length);`true`.startsWith(t)?l+=`true`.slice(t.length):`false`.startsWith(t)?l+=`false`.slice(t.length):`null`.startsWith(t)&&(l+=`null`.slice(t.length))}}return l}async function $r(e){if(e===void 0)return{value:void 0,state:`undefined-input`};let t=await Tr({text:e});return t.success?{value:t.value,state:`successful-parse`}:(t=await Tr({text:Qr(e)}),t.success?{value:t.value,state:`repaired-parse`}:{value:void 0,state:`failed-parse`})}new TextEncoder,new TextEncoder,P({prefix:`aitxt`,size:24}),P({prefix:`call`,size:24}),TransformStream;var Y=G.record(G.string(),K.optional()),ei=mr(()=>Sr(G.union([G.looseObject({type:G.literal(`text-start`),id:G.string(),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`text-delta`),id:G.string(),delta:G.string(),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`text-end`),id:G.string(),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`error`),errorText:G.string()}),G.looseObject({type:G.literal(`tool-input-start`),toolCallId:G.string(),toolName:G.string(),providerExecuted:G.boolean().optional(),providerMetadata:q.optional(),toolMetadata:Y.optional(),dynamic:G.boolean().optional(),title:G.string().optional()}),G.looseObject({type:G.literal(`tool-input-delta`),toolCallId:G.string(),inputTextDelta:G.string()}),G.looseObject({type:G.literal(`tool-input-available`),toolCallId:G.string(),toolName:G.string(),input:G.unknown(),providerExecuted:G.boolean().optional(),providerMetadata:q.optional(),toolMetadata:Y.optional(),dynamic:G.boolean().optional(),title:G.string().optional()}),G.looseObject({type:G.literal(`tool-input-error`),toolCallId:G.string(),toolName:G.string(),input:G.unknown(),providerExecuted:G.boolean().optional(),providerMetadata:q.optional(),toolMetadata:Y.optional(),dynamic:G.boolean().optional(),errorText:G.string(),title:G.string().optional()}),G.looseObject({type:G.literal(`tool-approval-request`),approvalId:G.string(),toolCallId:G.string(),approvalDescriptor:G.unknown().optional(),inputSchemaInput:G.unknown().optional(),reason:G.string().optional(),isAutomatic:G.boolean().optional(),signature:G.string().optional()}),G.looseObject({type:G.literal(`tool-approval-response`),approvalId:G.string(),approved:G.boolean(),reason:G.string().optional(),providerExecuted:G.boolean().optional(),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`tool-output-available`),toolCallId:G.string(),output:G.unknown(),providerExecuted:G.boolean().optional(),providerMetadata:q.optional(),toolMetadata:Y.optional(),dynamic:G.boolean().optional(),preliminary:G.boolean().optional()}),G.looseObject({type:G.literal(`tool-output-error`),toolCallId:G.string(),errorText:G.string(),providerExecuted:G.boolean().optional(),providerMetadata:q.optional(),toolMetadata:Y.optional(),dynamic:G.boolean().optional()}),G.looseObject({type:G.literal(`tool-output-denied`),toolCallId:G.string()}),G.looseObject({type:G.literal(`reasoning-start`),id:G.string(),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`reasoning-delta`),id:G.string(),delta:G.string(),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`reasoning-end`),id:G.string(),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`custom`),kind:G.string().transform(e=>e),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`source-url`),sourceId:G.string(),url:G.string(),title:G.string().optional(),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`source-document`),sourceId:G.string(),mediaType:G.string(),title:G.string(),filename:G.string().optional(),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`file`),url:G.string(),mediaType:G.string(),providerMetadata:q.optional()}),G.looseObject({type:G.literal(`reasoning-file`),url:G.string(),mediaType:G.string(),providerMetadata:q.optional()}),G.looseObject({type:G.custom(e=>typeof e==`string`&&e.startsWith(`data-`),{message:`Type must start with "data-"`}),id:G.string().optional(),data:G.unknown(),transient:G.boolean().optional()}),G.looseObject({type:G.literal(`start-step`)}),G.looseObject({type:G.literal(`finish-step`)}),G.looseObject({type:G.literal(`reset-step`)}),G.looseObject({type:G.literal(`start`),messageId:G.string().optional(),messageMetadata:G.unknown().optional()}),G.looseObject({type:G.literal(`finish`),finishReason:G.enum([`stop`,`length`,`content-filter`,`tool-calls`,`error`,`other`]).optional(),messageMetadata:G.unknown().optional()}),G.looseObject({type:G.literal(`abort`),reason:G.string().optional()}),G.looseObject({type:G.literal(`message-metadata`),messageMetadata:G.unknown()})])));function ti(e){return e.type.startsWith(`data-`)}function X(){return Object.create(null)}function ni(e){return e.type===`text`}function ri(e){return e.type.startsWith(`tool-`)}function ii(e){return e.type===`dynamic-tool`}function Z(e){return ri(e)||ii(e)}function ai(e){return e.type.split(`-`).slice(1).join(`-`)}function oi(e){return ii(e)?e.toolName:ai(e)}function si({lastMessage:e,messageId:t}){let n=e?.role===`assistant`?e:{id:t,metadata:void 0,role:`assistant`,parts:[]},r=X(),i=n.parts.findLastIndex(e=>e.type===`step-start`),a=0;for(let e of n.parts.slice(i+1)){if(!Z(e))continue;let t=a;ri(e)&&a++,e.state===`input-streaming`&&(r[e.toolCallId]={text:e.rawInput??``,index:t,toolName:e.type===`dynamic-tool`?e.toolName:ai(e),dynamic:e.type===`dynamic-tool`,title:e.title,toolMetadata:e.toolMetadata})}return{message:n,activeTextParts:X(),activeReasoningParts:X(),partialToolCalls:r}}function ci({stream:e,messageMetadataSchema:t,dataPartSchemas:n,runUpdateMessageJob:r,onError:i,onToolCall:a,onData:o,resetStateOnMessageIdChange:s=!1,resetStateOnFirstMessageStart:c=!1}){return e.pipeThrough(new TransformStream({async transform(e,l){await r(async({state:r,write:u})=>{function d(){let e=r.message.parts,t=e.length-1;for(;t>=0&&e[t].type!==`step-start`;)t--;return e.slice(t+1)}function f(){return d().filter(Z)}function p(e){let t=f().find(t=>t.toolCallId===e);if(t==null){let n=r.message.parts;for(let r=n.length-1;r>=0;r--){let i=n[r];if(Z(i)&&i.toolCallId===e){t=i;break}}}if(t==null)throw new W({chunkType:`tool-invocation`,chunkId:e,message:`No tool invocation found for tool call ID "${e}".`});return t}function m(e){let t=r.message.parts.filter(Z).find(t=>t.approval?.id===e);if(t==null)throw new W({chunkType:`tool-approval-response`,chunkId:e,message:`No tool invocation found for approval ID "${e}".`});return t}function h(e,t){let n=t??d().find(t=>ri(t)&&t.toolCallId===e.toolCallId),i=e,a=n;if(n!=null){n.state=e.state,a.input=i.input,a.output=i.output,a.errorText=i.errorText,a.rawInput=i.rawInput,a.preliminary=i.preliminary,e.title!==void 0&&(a.title=e.title),e.toolMetadata!==void 0&&(a.toolMetadata=e.toolMetadata),a.providerExecuted=i.providerExecuted??n.providerExecuted;let t=i.providerMetadata;if(t!=null){if(e.state===`output-available`||e.state===`output-error`){let e=n;e.resultProviderMetadata=t}else n.callProviderMetadata=t}}else r.message.parts.push({type:`tool-${e.toolName}`,toolCallId:e.toolCallId,state:e.state,title:e.title,...e.toolMetadata===void 0?{}:{toolMetadata:e.toolMetadata},input:i.input,output:i.output,rawInput:i.rawInput,errorText:i.errorText,providerExecuted:i.providerExecuted,preliminary:i.preliminary,...i.providerMetadata!=null&&(e.state===`output-available`||e.state===`output-error`)?{resultProviderMetadata:i.providerMetadata}:{},...i.providerMetadata!=null&&e.state!==`output-available`&&e.state!==`output-error`?{callProviderMetadata:i.providerMetadata}:{}})}function g(e,t){let n=t??d().find(t=>t.type===`dynamic-tool`&&t.toolCallId===e.toolCallId),i=e,a=n;if(n!=null){n.state=e.state,a.toolName=e.toolName,a.input=i.input,a.output=i.output,a.errorText=i.errorText,a.rawInput=i.rawInput,a.preliminary=i.preliminary,e.title!==void 0&&(a.title=e.title),e.toolMetadata!==void 0&&(a.toolMetadata=e.toolMetadata),a.providerExecuted=i.providerExecuted??n.providerExecuted;let t=i.providerMetadata;if(t!=null){if(e.state===`output-available`||e.state===`output-error`){let e=n;e.resultProviderMetadata=t}else n.callProviderMetadata=t}}else r.message.parts.push({type:`dynamic-tool`,toolName:e.toolName,toolCallId:e.toolCallId,state:e.state,input:i.input,output:i.output,errorText:i.errorText,preliminary:i.preliminary,providerExecuted:i.providerExecuted,title:e.title,...e.toolMetadata===void 0?{}:{toolMetadata:e.toolMetadata},...i.providerMetadata!=null&&(e.state===`output-available`||e.state===`output-error`)?{resultProviderMetadata:i.providerMetadata}:{},...i.providerMetadata!=null&&e.state!==`output-available`&&e.state!==`output-error`?{callProviderMetadata:i.providerMetadata}:{}})}async function _(e){if(e!=null){let n=r.message.metadata==null?e:Nr(r.message.metadata,e);t!=null&&await Cr({value:n,schema:t,context:{field:`message.metadata`,entityId:r.message.id}}),r.message.metadata=n}}switch(e.type){case`text-start`:{let t={type:`text`,text:``,providerMetadata:e.providerMetadata,state:`streaming`};r.activeTextParts[e.id]=t,r.message.parts.push(t),u();break}case`text-delta`:{let t=r.activeTextParts[e.id];if(t==null)throw new W({chunkType:`text-delta`,chunkId:e.id,message:`Received text-delta for missing text part with ID "${e.id}". Ensure a "text-start" chunk is sent before any "text-delta" chunks.`});t.text+=e.delta,t.providerMetadata=e.providerMetadata??t.providerMetadata,u();break}case`text-end`:{let t=r.activeTextParts[e.id];if(t==null)throw new W({chunkType:`text-end`,chunkId:e.id,message:`Received text-end for missing text part with ID "${e.id}". Ensure a "text-start" chunk is sent before any "text-end" chunks.`});t.state=`done`,t.providerMetadata=e.providerMetadata??t.providerMetadata,delete r.activeTextParts[e.id],u();break}case`custom`:{let t={type:`custom`,kind:e.kind,providerMetadata:e.providerMetadata};r.message.parts.push(t),u();break}case`reasoning-start`:{let t={type:`reasoning`,id:e.id,text:``,providerMetadata:e.providerMetadata,state:`streaming`};r.activeReasoningParts[e.id]=t,r.message.parts.push(t),u();break}case`reasoning-delta`:{let t=r.activeReasoningParts[e.id];if(t==null)throw new W({chunkType:`reasoning-delta`,chunkId:e.id,message:`Received reasoning-delta for missing reasoning part with ID "${e.id}". Ensure a "reasoning-start" chunk is sent before any "reasoning-delta" chunks.`});t.text+=e.delta,t.providerMetadata=e.providerMetadata??t.providerMetadata,u();break}case`reasoning-end`:{let t=r.activeReasoningParts[e.id];if(t==null)throw new W({chunkType:`reasoning-end`,chunkId:e.id,message:`Received reasoning-end for missing reasoning part with ID "${e.id}". Ensure a "reasoning-start" chunk is sent before any "reasoning-end" chunks.`});t.providerMetadata=e.providerMetadata??t.providerMetadata,t.state=`done`,delete r.activeReasoningParts[e.id],u();break}case`file`:case`reasoning-file`:r.message.parts.push({type:e.type,mediaType:e.mediaType,url:e.url,...e.providerMetadata==null?{}:{providerMetadata:e.providerMetadata}}),u();break;case`source-url`:r.message.parts.push({type:`source-url`,sourceId:e.sourceId,url:e.url,title:e.title,providerMetadata:e.providerMetadata}),u();break;case`source-document`:r.message.parts.push({type:`source-document`,sourceId:e.sourceId,mediaType:e.mediaType,title:e.title,filename:e.filename,providerMetadata:e.providerMetadata}),u();break;case`tool-input-start`:{let t=d().filter(ri);r.partialToolCalls[e.toolCallId]={text:``,toolName:e.toolName,index:t.length,dynamic:e.dynamic,title:e.title,toolMetadata:e.toolMetadata},e.dynamic?g({toolCallId:e.toolCallId,toolName:e.toolName,state:`input-streaming`,input:void 0,providerExecuted:e.providerExecuted,title:e.title,toolMetadata:e.toolMetadata,providerMetadata:e.providerMetadata}):h({toolCallId:e.toolCallId,toolName:e.toolName,state:`input-streaming`,input:void 0,providerExecuted:e.providerExecuted,title:e.title,toolMetadata:e.toolMetadata,providerMetadata:e.providerMetadata}),u();break}case`tool-input-delta`:{let t=r.partialToolCalls[e.toolCallId];if(t==null)throw new W({chunkType:`tool-input-delta`,chunkId:e.toolCallId,message:`Received tool-input-delta for missing tool call with ID "${e.toolCallId}". Ensure a "tool-input-start" chunk is sent before any "tool-input-delta" chunks.`});t.text+=e.inputTextDelta;let{value:n}=await $r(t.text);t.dynamic?g({toolCallId:e.toolCallId,toolName:t.toolName,state:`input-streaming`,input:n,rawInput:t.text,title:t.title,toolMetadata:t.toolMetadata}):h({toolCallId:e.toolCallId,toolName:t.toolName,state:`input-streaming`,input:n,rawInput:t.text,title:t.title,toolMetadata:t.toolMetadata}),u();break}case`tool-input-available`:e.dynamic?g({toolCallId:e.toolCallId,toolName:e.toolName,state:`input-available`,input:e.input,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,title:e.title,toolMetadata:e.toolMetadata}):h({toolCallId:e.toolCallId,toolName:e.toolName,state:`input-available`,input:e.input,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,title:e.title,toolMetadata:e.toolMetadata}),u(),a&&!e.providerExecuted&&await a({toolCall:e});break;case`tool-input-error`:{let t=d().filter(Z).find(t=>t.toolCallId===e.toolCallId);(t==null?e.dynamic:t.type===`dynamic-tool`)?g({toolCallId:e.toolCallId,toolName:e.toolName,state:`output-error`,input:e.input,errorText:e.errorText,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,toolMetadata:e.toolMetadata}):h({toolCallId:e.toolCallId,toolName:e.toolName,state:`output-error`,input:e.input,errorText:e.errorText,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,toolMetadata:e.toolMetadata}),u();break}case`tool-approval-request`:{let t=p(e.toolCallId);t.state=`approval-requested`,t.approval={id:e.approvalId,...e.approvalDescriptor==null?{}:{descriptor:e.approvalDescriptor},...Object.prototype.hasOwnProperty.call(e,`inputSchemaInput`)?{inputSchemaInput:e.inputSchemaInput}:{},...e.reason==null?{}:{requestReason:e.reason},...e.isAutomatic===!0?{isAutomatic:!0}:{},...e.signature==null?{}:{signature:e.signature}},u();break}case`tool-approval-response`:{let t=m(e.approvalId),n=t.approval==null?{id:e.approvalId}:t.approval;t.state=`approval-responded`,t.approval={...n,id:e.approvalId,approved:e.approved,...e.reason==null?{}:{reason:e.reason}},e.providerExecuted!=null&&(t.providerExecuted=e.providerExecuted),e.providerMetadata!=null&&(t.callProviderMetadata=e.providerMetadata),u();break}case`tool-output-denied`:{let t=p(e.toolCallId);t.state=`output-denied`,u();break}case`tool-output-available`:{let t=p(e.toolCallId);t.type===`dynamic-tool`?g({toolCallId:e.toolCallId,toolName:t.toolName,state:`output-available`,input:t.input,output:e.output,preliminary:e.preliminary,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,title:t.title,toolMetadata:e.toolMetadata??t.toolMetadata},t):h({toolCallId:e.toolCallId,toolName:ai(t),state:`output-available`,input:t.input,output:e.output,providerExecuted:e.providerExecuted,preliminary:e.preliminary,providerMetadata:e.providerMetadata,title:t.title,toolMetadata:e.toolMetadata??t.toolMetadata},t),u();break}case`tool-output-error`:{let t=p(e.toolCallId);t.type===`dynamic-tool`?g({toolCallId:e.toolCallId,toolName:t.toolName,state:`output-error`,input:t.input,errorText:e.errorText,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,title:t.title,toolMetadata:e.toolMetadata??t.toolMetadata},t):h({toolCallId:e.toolCallId,toolName:ai(t),state:`output-error`,input:t.input,rawInput:t.rawInput,errorText:e.errorText,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,title:t.title,toolMetadata:e.toolMetadata??t.toolMetadata},t),u();break}case`start-step`:r.message.parts.push({type:`step-start`});break;case`finish-step`:break;case`reset-step`:{let e=d();r.activeTextParts=X(),r.activeReasoningParts=X(),r.partialToolCalls=X(),e.length>0&&(r.message.parts.splice(r.message.parts.length-e.length,e.length),u());break}case`start`:e.messageId!=null&&(c||s&&e.messageId!==r.message.id)&&Object.assign(r,si({lastMessage:void 0,messageId:e.messageId}),{finishReason:void 0}),e.messageId!=null&&(r.message.id=e.messageId),await _(e.messageMetadata),(e.messageId!=null||e.messageMetadata!=null)&&u({updateStatus:!1});break;case`finish`:e.finishReason!=null&&(r.finishReason=e.finishReason),await _(e.messageMetadata),e.messageMetadata!=null&&u();break;case`message-metadata`:await _(e.messageMetadata),e.messageMetadata!=null&&u();break;case`error`:i?.(Error(e.errorText));break;default:if(ti(e)){if(n?.[e.type]!=null){let t=r.message.parts.findIndex(t=>`id`in t&&`data`in t&&t.id===e.id&&t.type===e.type),i=t>=0?t:r.message.parts.length;await Cr({value:e.data,schema:n[e.type],context:{field:`message.parts[${i}].data`,entityName:e.type,entityId:e.id}})}let t=e;if(t.transient){o?.(t);break}let i=t.id==null?void 0:r.message.parts.find(e=>t.type===e.type&&t.id===e.id);i==null?r.message.parts.push(t):i.data=t.data,o?.(t),u()}}l.enqueue(e)})}}))}async function li({stream:e,onError:t,abortSignal:n}){let r=e.getReader(),i=()=>{r.cancel().catch(()=>{})};n?.aborted?i():n?.addEventListener(`abort`,i,{once:!0});try{for(;;){let{done:e}=await r.read();if(e)break}}catch(e){t?.(e)}finally{n?.removeEventListener(`abort`,i),r.releaseLock()}}function ui(){let e,t;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}P({prefix:`aitxt`,size:24}),P({prefix:`call`,size:24}),P({prefix:`aitxt`,size:24}),P({prefix:`call`,size:24}),G.record(G.string(),K.optional()),G.record(G.string(),G.string()),P({prefix:`call`,size:24}),P({prefix:`call`,size:24}),new TextEncoder,P({prefix:`call`,size:24}),P({prefix:`aiobj`,size:24});var{atob:di}=globalThis,fi=class{constructor(){this.queue=[],this.isProcessing=!1,this.idleWaiters=[]}async processQueue(){if(!this.isProcessing){this.isProcessing=!0;try{for(;this.queue.length>0;)await this.queue[0](),this.queue.shift()}finally{this.isProcessing=!1;for(let e of this.idleWaiters.splice(0))e()}}}async run(e){return new Promise((t,n)=>{this.queue.push(async()=>{try{await e(),t()}catch(e){n(e)}}),this.processQueue()})}async waitForIdle(){(this.isProcessing||this.queue.length!==0)&&await new Promise(e=>{this.idleWaiters.push(e)})}};P({prefix:`aiobj`,size:24}),P({prefix:`call`,size:24}),G.object({token:G.string().refine(e=>e.trim().length>0),url:G.string().refine(e=>{try{let t=new URL(e);return(t.protocol===`ws:`||t.protocol===`wss:`)&&t.hostname!==``}catch{return!1}}),expiresAt:G.number().positive().max(2**53-1).optional(),tools:G.array(G.object({type:G.literal(`function`),name:G.string().min(1),description:G.string().optional(),parameters:G.record(G.string(),G.unknown())})).optional()}),P({prefix:`call`,size:24}),P({prefix:`call`,size:24}),P({prefix:`call`,size:24});async function pi({response:e,url:t,fallbackMessage:n}){let r=await e.text();return new Mt({message:r||n,url:t,requestBodyValues:void 0,statusCode:e.status,responseBody:r})}async function mi(e){if(e==null)return[];if(!globalThis.FileList||!(e instanceof globalThis.FileList))throw new Qt({functionality:`FileList`,message:`FileList is not supported in the current environment`});return Promise.all(Array.from(e).map(async e=>{let{name:t,type:n}=e;return{type:`file`,mediaType:n,filename:t,url:await new Promise((t,n)=>{let r=new FileReader;r.onload=e=>{t(e.target?.result)},r.onerror=e=>n(e),r.readAsDataURL(e)})}}))}function hi(e,t){let n=e.search(/[?#]/),r=n===-1?e:e.slice(0,n),i=n===-1?``:e.slice(n);return`${r.endsWith(`/`)&&t.startsWith(`/`)?r.slice(0,-1):r}${t}${i}`}var gi=class{constructor({api:e=`/api/chat`,credentials:t,headers:n,body:r,fetch:i,prepareSendMessagesRequest:a,prepareReconnectToStreamRequest:o}){this.api=e,this.credentials=t,this.headers=n,this.body=r,this.fetch=i,this.prepareSendMessagesRequest=a,this.prepareReconnectToStreamRequest=o}async sendMessages({abortSignal:e,...t}){let n=await H(this.body),r=await H(this.headers),i=await H(this.credentials),a={...N(r),...N(t.headers)},o=await this.prepareSendMessagesRequest?.({api:this.api,id:t.chatId,messages:t.messages,body:{...n,...t.body},headers:a,credentials:i,requestMetadata:t.metadata,trigger:t.trigger,messageId:t.messageId}),s=o?.api??this.api,c=o?.headers===void 0?a:N(o.headers),l=o?.body===void 0?{...n,...t.body,id:t.chatId,messages:t.messages,trigger:t.trigger,messageId:t.messageId}:o.body,u=o?.credentials??i,d=await(this.fetch??globalThis.fetch)(s,{method:`POST`,headers:{"content-type":`application/json`,...c},body:JSON.stringify(l),credentials:u,signal:e});if(!d.ok)throw await pi({response:d,url:s,fallbackMessage:`Failed to fetch the chat response.`});if(!d.body)throw new It({message:`The response body is empty.`});return this.processResponseStream(d.body)}async reconnectToStream(e){let t=await H(this.body),n=await H(this.headers),r=await H(this.credentials),i={...N(n),...N(e.headers)},a=await this.prepareReconnectToStreamRequest?.({api:this.api,id:e.chatId,body:{...t,...e.body},headers:i,credentials:r,requestMetadata:e.metadata}),o=a?.api;if(o==null){if(e.chatId===`.`||e.chatId===`..`)throw new U({parameter:`chatId`,value:e.chatId,message:`Chat IDs must not be "." or ".." when using the default reconnect URL.`});o=hi(this.api,`/${encodeURIComponent(e.chatId)}/stream`)}let s=a?.headers===void 0?i:N(a.headers),c=a?.credentials??r,l=await(this.fetch??globalThis.fetch)(o,{method:`GET`,headers:s,credentials:c,signal:e.abortSignal});if(l.status===204)return null;if(!l.ok)throw await pi({response:l,url:o,fallbackMessage:`Failed to fetch the chat response.`});if(!l.body)throw new It({message:`The response body is empty.`});return this.processResponseStream(l.body)}},_i=class extends gi{constructor(e={}){super(e)}processResponseStream(e){return Er({stream:e,schema:ei}).pipeThrough(new TransformStream({async transform(e,t){if(!e.success)throw e.error;t.enqueue(e.value)}}))}},vi=class{constructor({generateId:e=un,id:t=e(),transport:n=new _i,messageMetadataSchema:r,dataPartSchemas:i,state:a,onError:o,onToolCall:s,onFinish:c,onData:l,sendAutomaticallyWhen:u}){this.pendingMessagePreparations=new Set,this.activeResponse=void 0,this.activeResumeRequest=void 0,this.resumableStreamState=void 0,this.jobExecutor=new fi,this.activeStopCount=0,this.stopGeneration=0,this.sendMessage=async(e,t)=>{if(e==null){let e=this.pendingApprovalMessageId;if(e==null){e=this.lastMessage?.id;for(let t=this.state.messages.length-1;t>=0;t--){let n=this.state.messages[t];if(n.role===`assistant`&&n.parts.some(e=>Z(e)&&e.state===`approval-responded`)){e=n.id;break}}}let n=e!=null&&e===this.pendingApprovalMessageId?this.state.messages.findIndex(t=>t.id===e):-1;await this.makeRequestForToolApproval({messageId:e,messageIndex:n,...t});return}let n;if(`text`in e||`files`in e){let t=new AbortController;this.pendingMessagePreparations.add(t);let r;try{r=Array.isArray(e.files)?e.files:await mi(e.files)}finally{this.pendingMessagePreparations.delete(t)}if(t.signal.aborted)return;n={parts:[...r,...`text`in e&&e.text!=null?[{type:`text`,text:e.text}]:[]]}}else n=e;if(e.messageId!=null){let t=this.state.messages.findIndex(t=>t.id===e.messageId);if(t===-1)throw new U({parameter:`message.messageId`,value:e.messageId,message:`message with id ${e.messageId} not found`});if(this.state.messages[t].role!==`user`)throw new U({parameter:`message.messageId`,value:e.messageId,message:`message with id ${e.messageId} is not a user message`});this.state.messages=this.state.messages.slice(0,t+1),this.state.replaceMessage(t,{id:e.messageId,...n,role:n.role??`user`,metadata:e.metadata})}else this.state.pushMessage({...n,id:n.id??this.generateId(),role:n.role??`user`,metadata:e.metadata});await this.makeRequest({trigger:`submit-message`,messageId:e.messageId,...t})},this.regenerate=async({messageId:e,...t}={})=>{let n=e==null?this.state.messages.length-1:this.state.messages.findIndex(t=>t.id===e);if(n===-1)throw new U({parameter:`messageId`,value:e,message:`message ${e} not found`});this.state.messages=this.state.messages.slice(0,this.messages[n].role===`assistant`?n:n+1),await this.makeRequest({trigger:`regenerate-message`,messageId:e,...t})},this.resumeStream=async(e={})=>{await this.makeRequest({trigger:`resume-stream`,...e})},this.clearError=()=>{this.status===`error`&&(this.state.error=void 0,this.setStatus({status:`ready`}))},this.addToolApprovalResponse=async({id:e,approved:t,reason:n,options:r})=>this.jobExecutor.run(async()=>{let i=this.state.messages,a=r=>Z(r)&&r.state===`approval-requested`&&r.approval.id===e?{...r,state:`approval-responded`,approval:{...r.approval,id:e,approved:t,reason:n}}:r,o=i.findIndex(t=>t.parts.some(t=>Z(t)&&t.state===`approval-requested`&&t.approval.id===e));if(o!==-1){let e=i[o];this.state.replaceMessage(o,{...e,parts:e.parts.map(a)}),this.pendingApprovalMessageId=e.id}if(this.activeResponse&&(this.activeResponse.state.message.parts=this.activeResponse.state.message.parts.map(a)),o!==-1&&this.status!==`streaming`&&this.status!==`submitted`&&this.sendAutomaticallyWhen){let e=await this.shouldSendAutomatically();e&&this.runAutomaticRequest(()=>{let e=o===-1?this.lastMessage?.id:i[o].id;return this.makeRequestForToolApproval({messageId:e,messageIndex:o,...r})},e)}}),this.addToolOutput=async({state:e,toolCallId:t,output:n,errorText:r,options:i})=>this.jobExecutor.run(async()=>{let a=this.state.messages,o=a[a.length-1],s=i=>{if(!Z(i)||i.toolCallId!==t)return i;let{approval:a,...o}=i,s=a?.approved===!0?{approval:{...a,approved:a.approved}}:{};return e===`output-error`?{...o,...s,state:e,input:i.input,output:void 0,errorText:r}:{...o,...s,state:`output-available`,input:i.input,output:n,errorText:void 0}};if(this.state.replaceMessage(a.length-1,{...o,parts:o.parts.map(s)}),this.activeResponse&&(this.activeResponse.state.message.parts=this.activeResponse.state.message.parts.map(s)),this.status!==`streaming`&&this.status!==`submitted`&&this.sendAutomaticallyWhen){let e=await this.shouldSendAutomatically();e&&this.runAutomaticRequest(()=>this.makeRequest({trigger:`submit-message`,messageId:this.lastMessage?.id,...i}),e)}}),this.addToolResult=this.addToolOutput,this.stop=async()=>{this.activeStopCount++,this.stopGeneration++;try{let e=this.activeResumeRequest,t=this.activeResponse;for(let e of this.pendingMessagePreparations)e.abort();e?.abortController.abort(),t?.abortController.abort(),await Promise.all([e?.completionPromise,t?.completionPromise]),await this.jobExecutor.waitForIdle()}finally{this.activeStopCount--}},this.dispose=async()=>{await this.stop(),this.transport.close?.()},this.id=t,this.transport=n,this.generateId=e,this.messageMetadataSchema=r,this.dataPartSchemas=i,this.state=a,this.onError=o,this.onToolCall=s,this.onFinish=c,this.onData=l,this.sendAutomaticallyWhen=u}get status(){return this.state.status}setStatus({status:e,error:t}){if(this.status===e){this.error!==t&&(this.state.error=t);return}this.state.status=e,this.state.error=t}get error(){return this.state.error}get messages(){return this.state.messages}get lastMessage(){return this.state.messages[this.state.messages.length-1]}set messages(e){this.state.messages=e}async shouldSendAutomatically(){if(!this.sendAutomaticallyWhen)return!1;let e=this.sendAutomaticallyWhen({messages:this.state.messages});return e&&typeof e==`object`&&`then`in e?await e:e}async runAutomaticRequest(e,t){let n=this.stopGeneration,r=this.activeStopCount>0;(t??await this.shouldSendAutomatically())&&(r||n!==this.stopGeneration||this.activeStopCount>0||await e())}async makeRequestForToolApproval({messageId:e,messageIndex:t,...n}){let r=e!=null&&e===this.pendingApprovalMessageId;r&&(this.pendingApprovalMessageId=void 0),await this.makeRequest({trigger:`submit-message`,messageId:e,...n}),r&&this.status===`error`&&this.pendingApprovalMessageId==null&&(this.pendingApprovalMessageId=this.state.messages[t]?.id??e)}async makeRequest(e){let t=ui();try{await this.makeRequestImpl({...e,completionPromise:t.promise})}finally{t.resolve()}}async makeRequestImpl({trigger:e,metadata:t,headers:n,body:r,messageId:i,completionPromise:a}){e!==`resume-stream`&&(this.resumableStreamState=void 0);let o=new AbortController,s=e===`resume-stream`?{abortController:o,completionPromise:a}:void 0;s&&(this.activeResumeRequest?.abortController.abort(),this.activeResumeRequest=s);let c=()=>s==null||this.activeResumeRequest===s,l=()=>{this.activeResumeRequest===s&&(this.activeResumeRequest=void 0)},u;if(e===`resume-stream`)try{let e=await this.transport.reconnectToStream({chatId:this.id,abortSignal:o.signal,metadata:t,headers:n,body:r});if(o.signal.aborted||!c()){await e?.cancel().catch(()=>{}),c()&&(this.setStatus({status:`ready`}),this.resumableStreamState=void 0),l();return}if(e==null){this.setStatus({status:`ready`}),this.resumableStreamState=void 0,l();return}u=e}catch(e){if(o.signal.aborted||e.name===`AbortError`){c()&&(this.setStatus({status:`ready`}),this.resumableStreamState=void 0),l();return}if(!c())return;this.onError&&e instanceof Error&&this.onError(e),this.setStatus({status:`error`,error:e}),l();return}this.setStatus({status:`submitted`,error:void 0});let d=this.lastMessage,f=e===`submit-message`&&i!=null?this.state.messages.findIndex(e=>e.id===i):this.state.messages.length-1,p=f===-1?d:this.state.messages[f],m=e===`resume-stream`&&p?.role===`assistant`&&p.parts.some(e=>Z(e)&&(e.state===`input-streaming`||e.state===`input-available`&&e.providerExecuted||e.state===`approval-responded`))?this.state.snapshot(p):void 0,h=f!==-1&&f<this.state.messages.length-1&&p?.role===`assistant`,g=!1,_=!1,v=!1,y;try{let s={state:e===`resume-stream`&&this.resumableStreamState!=null?this.resumableStreamState:si({lastMessage:e===`resume-stream`?m:e===`regenerate-message`?void 0:this.state.snapshot(p),messageId:this.generateId()}),abortController:o,completionPromise:a};y=s,s.abortController.signal.addEventListener(`abort`,()=>{g=!0}),this.activeResponse=s;let l;if(l=e===`resume-stream`?u:await this.transport.sendMessages({chatId:this.id,messages:this.state.messages,abortSignal:s.abortController.signal,metadata:t,headers:n,body:r,trigger:e,messageId:i}),await li({stream:ci({stream:l,resetStateOnMessageIdChange:e===`resume-stream`,resetStateOnFirstMessageStart:e===`resume-stream`&&this.transport.resumeStreamIsReplay,onToolCall:this.onToolCall,onData:this.onData,messageMetadataSchema:this.messageMetadataSchema,dataPartSchemas:this.dataPartSchemas,runUpdateMessageJob:e=>this.jobExecutor.run(()=>s.abortController.signal.aborted?Promise.resolve():e({state:s.state,write:({updateStatus:e=!0}={})=>{s.abortController.signal.aborted||(e&&this.setStatus({status:`streaming`}),h?this.state.replaceMessage(f,s.state.message):s.state.message.id===this.lastMessage?.id?this.state.replaceMessage(this.state.messages.length-1,s.state.message):this.state.pushMessage(s.state.message))}})),onError:e=>{throw e}}),abortSignal:s.abortController.signal,onError:e=>{throw e}}),g)return c()&&(this.setStatus({status:`ready`}),this.resumableStreamState===s.state&&(this.resumableStreamState=void 0)),null;c()&&(this.setStatus({status:`ready`}),this.resumableStreamState===s.state&&(this.resumableStreamState=void 0))}catch(e){if(g||e.name===`AbortError`)return g=!0,c()&&(this.setStatus({status:`ready`}),this.resumableStreamState===y?.state&&(this.resumableStreamState=void 0)),null;if(!c())return null;if(v=!0,e instanceof TypeError){let t=e.message.toLowerCase();_=t.includes(`fetch`)||t.includes(`network`)||t===`load failed`}_?this.resumableStreamState=y?.state:this.resumableStreamState===y?.state&&(this.resumableStreamState=void 0),this.onError&&e instanceof Error&&this.onError(e),this.setStatus({status:`error`,error:e})}finally{try{y&&this.onFinish?.({message:y.state.message,messages:this.state.messages,isAbort:g,isDisconnect:_,isError:v,finishReason:y.state.finishReason})}finally{this.activeResponse===y&&(this.activeResponse=void 0),l()}}!g&&!v&&await this.runAutomaticRequest(()=>this.makeRequest({trigger:`submit-message`,messageId:this.lastMessage?.id,metadata:t,headers:n,body:r}))}};function yi({messages:e}){let t=e[e.length-1];if(!t||t.role!==`assistant`)return!1;let n=t.parts.reduce((e,t,n)=>t.type===`step-start`?n:e,-1),r=t.parts.slice(n+1),i=r.filter(Z).filter(e=>!e.providerExecuted),a=r.reduce((e,t,n)=>Z(t)&&!t.providerExecuted?n:e,-1);return i.length>0&&!r.slice(a+1).some(e=>e.type===`text`&&e.state!==`done`)&&i.every(e=>e.state===`output-available`&&e.preliminary!==!0||e.state===`output-error`)}function bi(e){return new _i({api:Je(`/`,`Chat/Messages/`),credentials:`include`,body:{orgId:e}})}var xi=e(t(((e,t)=>{function n(e,t){if(typeof e!=`function`)throw TypeError(`Expected the first argument to be a \`function\`, got \`${typeof e}\`.`);let n,r=0;return function(...i){clearTimeout(n);let a=Date.now(),o=t-(a-r);o<=0?(r=a,e.apply(this,i)):n=setTimeout(()=>{r=Date.now(),e.apply(this,i)},o)}}t.exports=n}))(),1);function Si(e,t){return t==null?e:(0,xi.default)(e,t)}function Ci(e){return Array.isArray(e)?[...e]:typeof e==`object`&&e&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null)?{...e}:e}var wi=class{#e;#t;#n;#r;#i;#a;constructor(e=[]){this.#t=`ready`,this.#n=void 0,this.#r=new Set,this.#i=new Set,this.#a=new Set,this.pushMessage=e=>{this.#e=this.#e.concat(e),this.#o()},this.popMessage=()=>{this.#e=this.#e.slice(0,-1),this.#o()},this.replaceMessage=(e,t)=>{this.#e=[...this.#e.slice(0,e),this.snapshot(t),...this.#e.slice(e+1)],this.#o()},this.snapshot=e=>{if(typeof e!=`object`||!e||!(`parts`in e)||!Array.isArray(e.parts))return e;let t=e,n={...t,parts:t.parts.map(e=>({...e}))};return`metadata`in t&&(n.metadata=Ci(t.metadata)),n},this[`~registerMessagesCallback`]=(e,t)=>{let n=t?Si(e,t):e;return this.#r.add(n),()=>{this.#r.delete(n)}},this[`~registerStatusCallback`]=e=>(this.#i.add(e),()=>{this.#i.delete(e)}),this[`~registerErrorCallback`]=e=>(this.#a.add(e),()=>{this.#a.delete(e)}),this.#o=()=>{this.#r.forEach(e=>e())},this.#s=()=>{this.#i.forEach(e=>e())},this.#c=()=>{this.#a.forEach(e=>e())},this.#e=e}get status(){return this.#t}set status(e){this.#t=e,this.#s()}get error(){return this.#n}set error(e){this.#n=e,this.#c()}get messages(){return this.#e}set messages(e){this.#e=[...e],this.#o()}#o;#s;#c},Ti=class extends vi{#e;constructor({messages:e,...t}){let n=new wi(e);super({...t,state:n}),this[`~registerMessagesCallback`]=(e,t)=>this.#e[`~registerMessagesCallback`](e,t),this[`~registerStatusCallback`]=e=>this.#e[`~registerStatusCallback`](e),this[`~registerErrorCallback`]=e=>this.#e[`~registerErrorCallback`](e),this.#e=n}},Ei=new WeakMap;function Di({chat:e,registration:t}){let n=Ei.get(e);n??(n=new Set,Ei.set(e,n));let r=n.size===0;return n.add(t),r&&e.resumeStream(),()=>{n.delete(t),n.size===0&&Ei.delete(e)}}function Oi(e){return{chat:e,messages:e.messages,status:e.status,error:e.error}}function ki(e,t){return e.chat===t.chat&&e.messages===t.messages&&e.status===t.status&&e.error===t.error}function Ai({throttle:e,experimental_throttle:t,resume:n=!1,...r}={}){let i=e??t,a=(0,O.useRef)({}),o=(0,O.useRef)({});`chat`in r||(o.current={onToolCall:r.onToolCall,onData:r.onData,onFinish:r.onFinish,onError:r.onError,sendAutomaticallyWhen:r.sendAutomaticallyWhen,transport:r.transport});let s,c=()=>o.current.transport??(s??=new _i),l={...r,transport:{sendMessages:e=>c().sendMessages(e),reconnectToStream:e=>c().reconnectToStream(e)},onToolCall:e=>o.current.onToolCall?.(e),onData:e=>o.current.onData?.(e),onFinish:e=>o.current.onFinish?.(e),onError:e=>o.current.onError?.(e),sendAutomaticallyWhen:e=>o.current.sendAutomaticallyWhen?.(e)??!1},u=`chat`in r?r.chat:r.id,{chat:d,isExternallyManaged:f}=(0,O.useMemo)(()=>({chat:`chat`in r?r.chat:new Ti(l),isExternallyManaged:`chat`in r}),[u]);(0,O.useEffect)(()=>{if(!f)return()=>{d.stop()}},[d,f]);let[p,m]=(0,O.useState)(()=>Oi(d));p.chat!==d&&m(Oi(d)),(0,O.useEffect)(()=>{let e=!0,t=()=>{if(!e)return;let t=Oi(d);m(e=>e.chat!==d||ki(e,t)?e:t)},n=[d[`~registerMessagesCallback`](t,i),d[`~registerStatusCallback`](t),d[`~registerErrorCallback`](t)],r=Oi(d);return m(e=>ki(e,r)?e:r),()=>{e=!1,n.forEach(e=>e())}},[d,i]);let{messages:h,status:g,error:_}=p,v=(0,O.useCallback)(e=>{typeof e==`function`&&(e=e(d.messages)),d.messages=e},[d]);return(0,O.useEffect)(()=>{if(n)return Di({chat:d,registration:a.current})},[n,d]),{id:d.id,messages:h,setMessages:v,sendMessage:d.sendMessage,regenerate:d.regenerate,clearError:d.clearError,stop:d.stop,error:_,resumeStream:d.resumeStream,status:g,addToolResult:d.addToolOutput,addToolOutput:d.addToolOutput,addToolApprovalResponse:d.addToolApprovalResponse}}function ji({input:e,setInput:t,onSubmit:n,disabled:r,autoFocus:i}){let a=(0,O.useRef)(null);return(0,O.useEffect)(()=>{i&&!r&&a.current?.focus()},[i,r]),(0,k.jsxs)(`form`,{onSubmit:n,className:`input-area`,children:[(0,k.jsx)(`input`,{ref:a,value:e,onChange:e=>t(e.target.value),placeholder:`Type a message...`,disabled:r}),(0,k.jsx)(`button`,{type:`submit`,disabled:r||!e.trim(),children:(0,k.jsx)(yt,{size:18})})]})}function Mi(){return(0,k.jsxs)(`div`,{className:`loading-state`,children:[(0,k.jsx)(Ke,{className:`animate-spin`,size:24}),(0,k.jsx)(`p`,{children:`Loading conversation...`})]})}var Ni={name:`collectFeedback`,description:`Collects feedback from the user by fetching a link to a pre-populated GitHub Discussion. Use this if the user lets us know we did something well, or if the user seems frustrated, or wants to report a bug.`,inputSchema:i({feedbackSummary:o().describe(`A brief summary of the feedback, used as the discussion title.`),feedbackDetails:o().describe(`Detailed feedback from the user or agent observation.`),recap:o().describe(`A sanitized recap of what the agent and user did together and if it was successful. No sensitive information.`)})};async function Pi({input:{feedbackSummary:e,feedbackDetails:t,recap:n}}){let r=e,i=`${t}\n\n${n}`;return{success:!0,url:`https://github.com/HarperFast/harper-agent/discussions/new?category=usage-feedback&title=${encodeURIComponent(r)}&body=${encodeURIComponent(i)}`,message:`Feedback URL created! Ask the user to open it in their browser, and they will be brought to a form to approve the details of the feedback.`}}var Fi={...Ni,icon:_t,execute:Pi},Ii={name:`createApp`,description:`Create a new Harper app with the specified name and template type.`,inputSchema:i({name:o().trim(),type:v(ce)})};function Q(e){return ye(e)?xe:String(e)}function Li(e,t){let{title:n,description:r}=Ve(t);T.error(n,{id:e,description:r,duration:1e4})}async function Ri({input:{name:e,type:t},instanceClientParams:n,baseURL:r}){let i=$e(e),a=ve(le,`id`,t);if(!a)return{success:!1,message:`Error: Invalid template type, ${t}, please choose from: ${le.map(e=>e.id).join(`, `)}`};let o=T.loading(`Creating from template...`,{description:`This may take a bit.`,duration:3e5});try{await de({...n,project:i,template:a.npm||a.githubUrl})}catch(e){return Li(o,e),{success:!1,message:`Error: ${Q(e)}`}}ke(`ReloadApplicationRootEntries`,!0),T.loading(`Created successfully!`,{description:`${i} created! Restarting the HTTP service...`,id:o,duration:3e5});try{await je({...n,operation:`restart_service`,replicated:n.entityType===`cluster`})}catch(e){return Li(o,e),e instanceof be?{success:!0,message:`App "${i}" created. Harper accepted the restart that loads it, but Studio couldn't confirm the instance came back, so it may still be starting. Don't create the app or restart again until its status shows it is back.`,webURL:r}:{success:!1,message:`Error: ${Q(e)}`}}return T.success(`Created successfully!`,{description:`${i} created!`,id:o,duration:5e3}),{success:!0,message:`App "${i}" created successfully.`,webURL:r}}var zi={...Ii,icon:Ce,execute:Ri,requiresApproval:!0},Bi={name:`deleteTableRecords`,description:`Deletes records in a particular table in a particular database on the server by their primary keys.`,inputSchema:i({database:o().trim(),table:o().trim(),primaryKeys:m(o())})};async function Vi({input:{database:e,table:t,primaryKeys:n},instanceClientParams:r,params:i}){try{let a=await Ue({...r,databaseName:e,tableName:t,hashValues:n}),{databaseName:o,tableName:s}=i;return await D.invalidateQueries({queryKey:[r.entityId,o,s]}),{success:!0,data:a}}catch(e){return{success:!1,message:`Error: ${Q(e)}`}}}var Hi={...Bi,icon:at,execute:Vi,requiresApproval:!0},Ui={name:`dropComponentFile`,description:`Drops a component file by its full path (which was returned by getComponents)`,inputSchema:i({path:o().trim()})};async function Wi({input:{path:e},instanceClientParams:t}){try{let n=e.split(`/`),r=n.shift(),i=n.join(`/`),a=await fe({...t,file:i,project:r});return me(D,t.entityId,e.replace(/\/+$/,``)),{success:!0,data:a}}catch(e){return{success:!1,message:`Error: ${Q(e)}`}}}var Gi={...Ui,icon:Qe,execute:Wi,requiresApproval:!0},Ki={name:`getAnalytics`,description:`Retrieves analytics metrics for the server, such as CPU usage or database operations (reads, writes, messages).`,inputSchema:i({metricName:o().describe(`The name of the metric to retrieve.`),startTime:l().describe(`The start of the time range for the metric, in milliseconds since the Unix epoch.`),endTime:l().describe(`The end of the time range for the metric, in milliseconds since the Unix epoch.`)})};async function qi({input:e,instanceClientParams:t}){try{let{metricName:n,startTime:r,endTime:i}=e;return{success:!0,data:await rt({metric:n,startTime:r,endTime:i,instanceParams:t})}}catch(e){return{success:!1,message:`Error retrieving analytics: ${e}`}}}var Ji={...Ki,icon:lt,execute:qi},Yi={name:`getComponentFile`,description:`Returns the contents of a component file by its full path (which was returned by getComponents)`,inputSchema:i({path:o().trim()})};async function Xi({input:{path:e},instanceClientParams:t}){try{let n=e.split(`/`),r=n.shift(),i=n.join(`/`);return{success:!0,data:await Ie({...t,file:i,project:r})}}catch(e){return{success:!1,message:`Error: ${e}`}}}var Zi={...Yi,icon:ue,execute:Xi},Qi={name:`getComponents`,description:`Retrieves a tree of all the component (files and folders) names and sizes stored on the server.`,inputSchema:i({})};async function $i({instanceClientParams:e}){try{let t=await Re(e),{rootEntries:n}=he(t.entries);return{success:!0,items:pe(n).items}}catch(e){return{success:!1,message:`Error: ${e}`}}}var ea={...Qi,icon:_e,execute:$i},ta={name:`getDescribeAll`,description:`Retrieves a map of the names of databases and their tables stored on the server.`,inputSchema:i({})};async function na({instanceClientParams:e}){try{return{success:!0,map:await Ze(e)}}catch(e){return{success:!1,message:`Error: ${e}`}}}var ra={...ta,icon:we,execute:na},ia={name:`getDescribeTable`,description:`Returns the schema metadata describing a particular table in a particular database on the server.`,inputSchema:i({database:o().trim(),table:o().trim()})};async function aa({input:{database:e,table:t},instanceClientParams:n}){try{return{success:!0,data:await Pe({...n,databaseName:e,tableName:t})}}catch(e){return{success:!1,message:`Error: ${e}`}}}var oa={...ia,icon:Xe,execute:aa},sa={name:`getUserContext`,description:`Retrieves the context of what the user is looking at on their screen, such as the current page, the file they are viewing, the database and table visible to them, that sort of thing.`,inputSchema:i({})};async function ca({instanceClientParams:e,params:t}){let n=Ee();if(n.includes(`/databases`)){let{databaseName:e,tableName:n}=t;return{success:!0,description:`viewing the database`,data:{databaseName:e,tableName:n}}}if(n.endsWith(`/apis`))return{success:!0,description:`viewing the APIs`};if(n.endsWith(`/operations`))return{success:!0,description:`sending raw requests from the operations API console`};if(n.endsWith(`/status`))return{success:!0,description:`viewing the status graphs`};if(n.endsWith(`/logs`))return{success:!0,description:`viewing the logs`};if(n.includes(`/config/`))return{success:!0,description:`viewing the configuration pages`};let r=Me(`FileFocused/${e.entityId}`,void 0),i=Me(`FolderOpened/${e.entityId}`,[]),a=Me(`FileSelected/${e.entityId}`,[]);return r?{success:!0,description:`editing application files`,data:{openedPath:r,expandedItems:i,selectedItems:a}}:{success:!0,description:n}}var la={...sa,icon:xt,execute:ca},ua={name:`insertTableRecords`,description:`Inserts one or more records into a particular table in a particular database on the server.`,inputSchema:i({database:o().trim(),table:o().trim(),records:m(a())})};async function da({input:{database:e,table:t,records:n},instanceClientParams:r,params:i}){try{let a=await Le({...r,databaseName:e,tableName:t,records:n}),{databaseName:o,tableName:s}=i;return await D.invalidateQueries({queryKey:[r.entityId,o,s]}),{success:!0,data:a}}catch(e){return{success:!1,message:`Error: ${Q(e)}`}}}var fa={...ua,icon:at,execute:da,requiresApproval:!0},pa={name:`listAnalyticsMetrics`,description:`Lists the available analytics metric names available for calling getAnalytics upon.`,inputSchema:i({metricTypes:v([`builtin`,`custom`]),customWindowMS:l().optional().describe(`Default to one week time window for finding custom metrics.`)})};async function ma({metricTypes:e,customWindowMS:t,instanceParams:n}){let{data:r}=await n.instanceClient.post(`/`,{operation:`list_analytics_metrics`,metricTypes:e,customWindowMS:t});return r}async function ha({input:e,instanceClientParams:t}){try{let{metricTypes:n,customWindowMS:r}=e;return{success:!0,metricNames:await ma({metricTypes:n,customWindowMS:r,instanceParams:t})}}catch(e){return{success:!1,message:`Error retrieving analytics: ${e}`}}}var ga={...pa,icon:lt,execute:ha},_a=[`adding-tables-with-schemas`,`automatic-apis`,`caching`,`checking-authentication`,`creating-a-fabric-account-and-cluster`,`creating-harper-apps`,`custom-resources`,`defining-relationships`,`delegating-to-the-built-in-agent`,`deploying-to-harper-fabric`,`extending-tables`,`handling-binary-data`,`load-env`,`logging`,`programmatic-table-requests`,`querying-rest-apis`,`real-time-apps`,`schema-design-tooling`,`serving-web-content`,`typescript-type-stripping`,`using-blob-datatype`,`v5-upgrade`,`vector-indexing`],va={"adding-tables-with-schemas":"---\nname: adding-tables-with-schemas\ndescription: Guidelines for adding tables to a Harper database using GraphQL schemas.\nmetadata:\n mode: synthesized\n---\n\n# Adding Tables with Schemas\n\nInstructions for the agent to follow when adding tables to a Harper database.\n\n## When to Use\n\nUse this skill when you need to define new data structures or modify existing ones in a Harper database.\n\n## How It Works\n\n1. **Create Dedicated Schema Files**: Prefer having a dedicated schema `.graphql` file for each table. Check the `config.yaml` file under `graphqlSchema.files` to see how it's configured. It typically accepts wildcards (e.g., `schemas/*.graphql`), but may be configured to point at a single file.\n2. **Use Directives**: All available directives for defining your schema are defined in `node_modules/harper/schema.graphql`. Common directives include `@table`, `@export`, `@primaryKey`, `@indexed`, and `@relationship`.\n3. **Define Relationships**: Link tables together using the `@relationship` directive. For more details, see the [Defining Relationships](defining-relationships.md) skill.\n4. **Enable Automatic APIs**: If you add `@table @export` to a schema type, Harper automatically sets up REST and WebSocket APIs for basic CRUD operations against that table. **Important**: REST endpoints also require `rest: true` in `config.yaml` — without it, `@export`ed tables will not respond to HTTP requests. For a detailed list of available endpoints and how to use them, see the [Automatic REST APIs](automatic-apis.md) skill.\n - `GET /{TableName}`: Describes the resource itself — table, database, and declared attributes. No trailing slash.\n - `GET /{TableName}/`: Lists all records (supports filtering, sorting, and pagination via query parameters). See the [Querying REST APIs](querying-rest-apis.md) skill for details.\n - `GET /{TableName}/{id}`: Retrieves a single record by its primary key.\n - `POST /{TableName}/`: Creates a record and returns `201` with the Harper-assigned primary key in the `Location` response header (the bare key, not a URL). **The trailing slash is required** — `POST /{TableName}` returns `404`, and `POST /{TableName}/{id}` returns `405`.\n - `PUT /{TableName}/{id}`: Creates **or replaces** the record at `{id}` (upsert). **Properties omitted from the body are removed** — send the complete record, or use `PATCH` to change a subset. Three exceptions survive the replacement: an `@updatedTime` attribute is re-stamped with the time of the write, a `@createdTime` attribute keeps its original value, and the primary key is forced to match the `{id}` in the URL, so a mismatched key in the body cannot create a second record.\n - `PATCH /{TableName}/{id}`: Merges the request body into the existing record, preserving unspecified properties. The merge is **shallow** — a nested object in the body replaces the stored one wholesale rather than being deep-merged.\n - `DELETE /{TableName}/{id}`: Deletes a single record by its primary key.\n - `DELETE /{TableName}/`: Deletes every record matching the query parameters. **With no query parameters it matches — and deletes — every record in the table.** Always pass a filter unless emptying the table is the intent.\n5. **Consider Table Extensions**: If you are going to [extend the table](./extending-tables.md) in your resources, then do not `@export` the table from the schema.\n\n## Examples\n\nIn a hypothetical `schemas/ExamplePerson.graphql`:\n\n```graphql\ntype ExamplePerson @table @export {\n id: ID @primaryKey\n name: String\n tag: String @indexed\n}\n```\n","automatic-apis":'---\nname: automatic-apis\ndescription: How to use Harper\'s automatically generated REST and WebSocket APIs.\nmetadata:\n mode: generate\n sources:\n - reference/v5/rest/overview.md#How the REST Interface Works\n - reference/v5/rest/overview.md#Configuration\n - reference/v5/rest/overview.md#Tables and Their Automatic Endpoints\n - reference/v5/rest/overview.md#URL Structure\n - reference/v5/rest/overview.md#HTTP Methods\n - reference/v5/rest/overview.md#Content Types\n - reference/v5/rest/overview.md#OpenAPI\n - reference/v5/rest/websockets.md\n sourceCommit: 2cfb81318f17e0aca2d600109e6ad813d2d0443e\n inputHash: fc3e7ae596f475b2\n---\n\n# Automatic APIs\n\nInstructions for the agent to follow when using Harper\'s automatically generated REST and WebSocket APIs for exported tables and resources.\n\n## When to Use\n\nApply this rule when enabling HTTP REST endpoints or WebSocket subscriptions for Harper tables and custom resources. Use it whenever you need to configure the REST plugin, understand the auto-generated endpoint surface, or wire up real-time WebSocket connections. See [querying-rest-apis.md](querying-rest-apis.md) for query syntax and [real-time-apps.md](real-time-apps.md) for real-time patterns.\n\n## How It Works\n\n1. **Enable the REST plugin** by adding `rest: true` to your application\'s `config.yaml`:\n\n ```yaml\n rest: true\n ```\n\n Extended options:\n\n ```yaml\n rest:\n lastModified: true # enables Last-Modified response header support\n webSocket: false # disables automatic WebSocket support (enabled by default)\n exactCount: true # opt in to Prefer: count=exact scans (off by default)\n ```\n\n2. **Export the table in your schema** using `@export`. Tables are not exposed by default — `@export` is required for Harper to register a REST route:\n\n ```graphql\n type Product @table @export {\n id: Long @primaryKey\n name: String\n price: Float\n }\n ```\n\n Without `@export`, the table has no REST route and callers receive `404`. Without `rest: true` in `config.yaml`, even an exported table does not respond to HTTP requests.\n\n3. **Understand the auto-generated endpoints**. With both `@export` and `rest: true` in place, Harper registers the following on the application HTTP server port (default `9926`):\n\n | Endpoint | Description |\n | ---------------------------- | ------------------------------------------------------------------------------------ |\n | `GET /Product` | Resource description — table name, database, declared attributes |\n | `GET /Product/` | Record collection; append query parameters to filter, sort, page |\n | `GET /Product/{id}` | Single record by primary key; `404` if not found |\n | `GET /Product/{id}.property` | Single declared property of one record |\n | `POST /Product/` | Creates a record; responds `201`; primary key returned in `Location` header |\n | `PUT /Product/{id}` | Creates or replaces the record at `{id}` (upsert); omitted properties are removed |\n | `PATCH /Product/{id}` | Shallow-merges body into existing record; unspecified top-level properties preserved |\n | `DELETE /Product/{id}` | Deletes the record at `{id}` |\n | `DELETE /Product/?query` | Deletes every record matching the query |\n - `HEAD` is served exactly as `GET` with the response body omitted. `QUERY` is accepted on the collection path (`QUERY /Product/`) and runs a search taken from the request body rather than the URL.\n\n4. **Use the correct URL structure**. The trailing slash is significant:\n\n | Path | Addresses |\n | ------------------------- | ---------------------------------------------- |\n | `/my-resource` | The resource itself (metadata) |\n | `/my-resource/` | The record collection |\n | `/my-resource/record-id` | A specific record by primary key |\n | `/my-resource/record-id/` | Collection of records with the given id prefix |\n\n5. **Handle `POST` primary keys via `Location`**. On a successful `POST`, the new record\'s primary key is returned in the `Location` response header — the value supplied by the body, or a Harper-assigned key if the body omitted the primary-key property.\n\n6. **Use conditional GET requests for caching**. GET responses include an `ETag` header. Send `If-None-Match` on subsequent requests; if the record is unchanged, Harper returns `304 Not Modified` with no body.\n\n7. **Apply `@updatedTime` for write timestamps**. On `PUT`, Harper re-stamps any `@updatedTime` attribute with the time of the write, preserves any `@createdTime` value from the original record, and forces the primary key to match the `{id}` in the URL.\n\n8. **Control OpenAPI visibility**. Every non-hidden exported resource appears in the generated OpenAPI document at `GET /openapi`. To exclude a type, mark it `@hidden` in the schema, or set `static hidden = true` on a programmatic resource class.\n\n9. **Implement a custom `connect()` handler** when default subscription behavior is insufficient. The method must return an async iterable that produces messages to send to the client:\n\n ```javascript\n export class Echo extends Resource {\n async *connect(incomingMessages) {\n for await (let message of incomingMessages) {\n yield message; // echo each message back\n }\n }\n }\n ```\n\n10. **Connect via WebSocket**. WebSocket support is enabled automatically with `rest: true`. Connect to a resource URL to subscribe to change events:\n\n ```javascript\n let ws = new WebSocket(\'wss://server/my-resource/341\');\n ws.onmessage = (event) => {\n let data = JSON.parse(event.data);\n };\n ```\n\n Disable WebSocket support independently with `webSocket: false` under `rest`.\n\n11. **Use MQTT over WebSockets** by setting the sub-protocol header:\n\n ```\n Sec-WebSocket-Protocol: mqtt\n ```\n\n12. **Specify response format** using the `Accept` header. The suffixes `.json`, `.cbor`, `.msgpack`, and `.csv` are reserved as content-type selectors on property paths and take precedence over property names.\n\n## Examples\n\n**Schema and config for a REST-enabled table:**\n\n```graphql\n# schema.graphql\ntype Product @table @export {\n id: Long @primaryKey\n name: String\n price: Float\n}\n```\n\n```yaml\n# config.yaml\ngraphqlSchema:\n files: schema.graphql\nrest: true\n```\n\n**Conditional GET with ETag caching:**\n\n```http\nGET /Product/123\nIf-None-Match: "abc123"\n```\n\nResponse when unchanged:\n\n```\nHTTP/1.1 304 Not Modified\nETag: "abc123"\n```\n\n**POST and read the Location header:**\n\n```http\nPOST /Product/\nContent-Type: application/json\n\n{ "name": "Widget" }\n```\n\n```\nHTTP/1.1 201 Created\nLocation: 7f3a9c\n```\n\n**PATCH (shallow merge only):**\n\n```http\nPATCH /Product/123\nContent-Type: application/json\n\n{ "status": "active" }\n```\n\n**Custom WebSocket connect handler:**\n\n```javascript\nexport class Echo extends Resource {\n async *connect(incomingMessages) {\n for await (let message of incomingMessages) {\n yield message; // echo each message back\n }\n }\n}\n```\n\n**Hide a resource from OpenAPI — schema directive:**\n\n```graphql\ntype InternalLog @table @export @hidden {\n id: Long @primaryKey\n}\n```\n\n**Hide a resource from OpenAPI — programmatic class:**\n\n```javascript\nexport class InternalLog extends Resource {\n static hidden = true;\n}\n```\n\n## Notes\n\n- A component directory with **no** `config.yaml` inherits Harper\'s built-in default, which enables `rest` automatically. As soon as a `config.yaml` exists, it is used verbatim — omitting `rest` from it disables REST even if the directory previously had it without a config file. Always add `rest: true` when adding a config file.\n- `POST /Product` (no trailing slash) returns `404`. The trailing slash is required for collection operations.\n- A `POST` to a primary key that already exists fails with `409` rather than overwriting.\n- PATCH merge is **shallow** — a nested object in the body replaces the stored nested object wholesale; nested properties not included in the body are dropped.\n- The `.msgpack` suffix (along with `.json`, `.cbor`, `.csv`) is reserved as a content-type selector and cannot be used as a property name in dot-path access.\n- Server-Sent Events subscriptions are served on the same paths, negotiated via `Accept: text/event-stream`; they are not affected by the `webSocket` option.\n- See [querying-rest-apis.md](querying-rest-apis.md) for full query syntax on collection endpoints and [real-time-apps.md](real-time-apps.md) for real-time subscription patterns.\n',caching:`---
13
+ name: caching
14
+ description: How to implement integrated data caching in Harper from external sources.
15
+ metadata:
16
+ mode: generate
17
+ sources:
18
+ - learn/developers/caching-with-harper.md
19
+ sourceCommit: 4fe4c9c95e0974eaa77032f6f10e36fbd8ec64ac
20
+ inputHash: 60ad55fa37b5eec5
21
+ ---
22
+
23
+ # Caching External Data Sources in Harper
24
+
25
+ Instructions for the agent to implement integrated data caching from external sources using Harper's cache table directives and \`sourcedFrom\` API.
26
+
27
+ ## When to Use
28
+
29
+ Apply this rule when an application needs to wrap an external API, microservice, or database with a fast local cache. Use it when you need to define TTL-based cache expiration, connect an upstream data source to a Harper table, or implement on-demand cache invalidation.
30
+
31
+ ## How It Works
32
+
33
+ 1. **Define a cache table with \`expiration\`**: Add the \`expiration\` argument to the \`@table\` directive in \`schema.graphql\`. The value is in seconds. When a record becomes stale, the next request fetches a fresh copy from the upstream source.
34
+
35
+ \`\`\`graphql
36
+ type JokeCache @table(expiration: 60) @export {
37
+ id: ID @primaryKey
38
+ setup: String
39
+ punchline: String
40
+ }
41
+ \`\`\`
42
+
43
+ 2. **Implement an upstream source object**: In \`resources.js\`, create an object with a \`get(id)\` method that fetches data from the external API.
44
+
45
+ \`\`\`javascript
46
+ const jokeAPI = {
47
+ async get(id) {
48
+ const response = await fetch(\`https://official-joke-api.appspot.com/jokes/\${id}\`);
49
+ return response.json();
50
+ },
51
+ };
52
+ \`\`\`
53
+
54
+ 3. **Connect the source with \`sourcedFrom\`**: Call \`sourcedFrom\` on the table to register the upstream source. Harper will call \`jokeAPI.get()\` automatically when a record is missing or stale.
55
+
56
+ \`\`\`javascript
57
+ tables.JokeCache.sourcedFrom(jokeAPI);
58
+ \`\`\`
59
+
60
+ Harper's request flow after \`sourcedFrom\` is registered:
61
+ - Request arrives for \`/JokeCache/1\`.
62
+ - Harper checks if the record exists and is not stale.
63
+ - If fresh, Harper returns it immediately.
64
+ - If missing or stale, Harper calls \`jokeAPI.get()\`, stores the result in \`JokeCache\`, and returns it.
65
+ - Multiple simultaneous requests for the same missing or stale record wait on a single upstream call — Harper prevents cache stampedes automatically.
66
+
67
+ 4. **Configure plugins in \`config.yaml\`**: Enable \`graphqlSchema\`, \`rest\`, and \`jsResource\`.
68
+
69
+ \`\`\`yaml
70
+ graphqlSchema:
71
+ files: 'schema.graphql'
72
+ rest: true
73
+ jsResource:
74
+ files: 'resources.js'
75
+ \`\`\`
76
+
77
+ 5. **Implement on-demand invalidation**: To invalidate a cache entry before its TTL expires, export a class extending the table and call \`this.invalidate(target)\` in a \`post\` handler. Remove \`@export\` from the schema when using this pattern — the exported class provides the endpoint.
78
+
79
+ \`\`\`javascript
80
+ export class JokeCache extends tables.JokeCache {
81
+ static async post(target, data) {
82
+ const body = await data;
83
+ if (body?.action === 'invalidate') {
84
+ this.invalidate(target);
85
+ return { status: 200, data: { message: 'invalidated' } };
86
+ }
87
+ }
88
+ }
89
+ \`\`\`
90
+
91
+ Update the schema to remove \`@export\`:
92
+
93
+ \`\`\`graphql
94
+ type JokeCache @table(expiration: 60) {
95
+ id: ID @primaryKey
96
+ setup: String
97
+ punchline: String
98
+ }
99
+ \`\`\`
100
+
101
+ ## Examples
102
+
103
+ **Complete \`resources.js\`**:
104
+
105
+ \`\`\`javascript
106
+ // resources.js
107
+
108
+ const jokeAPI = {
109
+ async get(id) {
110
+ const response = await fetch(\`https://official-joke-api.appspot.com/jokes/\${id}\`);
111
+ return response.json();
112
+ },
113
+ };
114
+
115
+ tables.JokeCache.sourcedFrom(jokeAPI);
116
+
117
+ export class JokeCache extends tables.JokeCache {
118
+ static async post(target, data) {
119
+ const body = await data;
120
+ if (body?.action === 'invalidate') {
121
+ this.invalidate(target);
122
+ return { status: 200, data: { message: 'invalidated' } };
123
+ }
124
+ }
125
+ }
126
+ \`\`\`
127
+
128
+ **Complete \`schema.graphql\`**:
129
+
130
+ \`\`\`graphql
131
+ type JokeCache @table(expiration: 60) {
132
+ id: ID @primaryKey
133
+ setup: String
134
+ punchline: String
135
+ }
136
+ \`\`\`
137
+
138
+ **Fetch a cached record**:
139
+
140
+ \`\`\`javascript
141
+ const response = await fetch('http://localhost:9926/JokeCache/1');
142
+ console.log(response.status); // 200
143
+ const etag = response.headers.get('etag'); // e.g. "abCDefGHij"
144
+ const joke = await response.json();
145
+ \`\`\`
146
+
147
+ **Use ETag for conditional requests** (returns \`304 Not Modified\` if unchanged):
148
+
149
+ \`\`\`javascript
150
+ const second = await fetch('http://localhost:9926/JokeCache/1', {
151
+ headers: { 'If-None-Match': etag },
152
+ });
153
+ console.log(second.status); // 304
154
+ \`\`\`
155
+
156
+ **Bypass the cache with \`Cache-Control: no-cache\`**:
157
+
158
+ \`\`\`javascript
159
+ const response = await fetch('http://localhost:9926/JokeCache/1', {
160
+ headers: { 'Cache-Control': 'no-cache' },
161
+ });
162
+ \`\`\`
163
+
164
+ **Trigger invalidation via POST**:
165
+
166
+ \`\`\`javascript
167
+ await fetch('http://localhost:9926/JokeCache/1', {
168
+ method: 'POST',
169
+ headers: { 'Content-Type': 'application/json' },
170
+ body: JSON.stringify({ action: 'invalidate' }),
171
+ });
172
+ \`\`\`
173
+
174
+ ## Notes
175
+
176
+ - \`expiration\` is measured in seconds. Harper also supports separate \`eviction\` and \`scanInterval\` arguments on \`@table\` for fine-grained control over physical record removal.
177
+ - ETags are automatically computed from a record's last-modified timestamp. Include the double quotes when passing an ETag back in \`If-None-Match\` — they are part of the value.
178
+ - Exporting a class with the same name as a table (e.g., \`export class JokeCache extends tables.JokeCache\`) registers it as the HTTP endpoint for that table; \`@export\` in the schema is not required separately.
179
+ - For defining custom upstream source behavior beyond a simple \`get\`, see [custom-resources.md](custom-resources.md).
180
+ - For details on how \`@table\` and \`@export\` expose REST endpoints automatically, see [automatic-apis.md](automatic-apis.md).
181
+ `,"checking-authentication":`---
182
+ name: checking-authentication
183
+ description: How to handle user authentication and sessions in Harper Resources.
184
+ metadata:
185
+ mode: generate
186
+ sources:
187
+ - >-
188
+ reference/v5/resources/resource-api.md#\`getCurrentUser(): User |
189
+ undefined\`
190
+ - 'reference/v5/resources/resource-api.md#\`getContext(): Context\`'
191
+ - reference/v5/resources/resource-api.md#Session and Login from a Resource
192
+ - reference/v5/security/jwt-authentication.md#Create Authentication Tokens
193
+ - reference/v5/security/jwt-authentication.md#Using the Operation Token
194
+ - reference/v5/security/jwt-authentication.md#Refreshing the Operation Token
195
+ - reference/v5/security/jwt-authentication.md#Scoped Tokens (Inline Role)
196
+ - >-
197
+ reference/v5/security/jwt-authentication.md#Issuing Tokens from a Custom
198
+ Resource
199
+ - reference/v5/security/jwt-authentication.md#Token Expiry Configuration
200
+ - reference/v5/security/jwt-authentication.md#When to Use JWT Auth
201
+ - reference/v5/security/jwt-authentication.md#Security Notes
202
+ sourceCommit: 9e6ecf87dd25bb488ad44dc2876ca6439ccd682e
203
+ inputHash: 1837f39fdfe06fac
204
+ ---
205
+
206
+ # Checking Authentication
207
+
208
+ Instructions for the agent to handle user authentication, sessions, and JWT token issuance in Harper Resources.
209
+
210
+ ## When to Use
211
+
212
+ Apply this rule when implementing login/logout flows, reading the current authenticated user, issuing or refreshing JWT tokens, or minting scoped tokens from a custom Resource. Use it whenever a Resource must gate behavior on identity or credentials. See [custom-resources.md](custom-resources.md) for the broader Resource authoring model.
213
+
214
+ ## How It Works
215
+
216
+ ### 1. Reading the Current Authenticated User
217
+
218
+ **In an instance method**, call \`getCurrentUser()\` to get the user associated with the current request, or \`undefined\` if unauthenticated. The returned object exposes \`username\`, \`role\`, and \`role.permission\` flags.
219
+
220
+ **In a static verb**, read the user from the \`context\` argument instead — \`context.user\`:
221
+
222
+ \`\`\`javascript
223
+ static async get(_target, context) {
224
+ const user = context?.user;
225
+ if (!user) return new Response(null, { status: 401 });
226
+ return { username: user.username, role: user.role };
227
+ }
228
+ \`\`\`
229
+
230
+ ### 2. Reading the Full Context
231
+
232
+ **In an instance method**, call \`getContext()\` to retrieve the current context, which includes:
233
+
234
+ - \`user\` — User object with username, role, and authorization information
235
+ - \`transaction\` — The current transaction
236
+
237
+ **In a static verb**, the context is the trailing argument:
238
+
239
+ - \`(target, context)\` for \`get\`/\`delete\`
240
+ - \`(target, data, context)\` for \`put\`/\`patch\`/\`post\`
241
+
242
+ When triggered by HTTP, the context also exposes \`url\`, \`method\`, \`headers\`, \`responseHeaders\`, \`pathname\`, \`host\`, \`ip\`, \`body\`, \`data\`, \`lastModified\`, and \`requestContext\`.
243
+
244
+ ### 3. Handling Sessions and Login
245
+
246
+ Enable sessions in \`harper-config.yaml\`:
247
+
248
+ \`\`\`yaml
249
+ authentication:
250
+ enableSessions: true
251
+ \`\`\`
252
+
253
+ Use \`context.login\` in a static \`post\` verb to verify credentials and establish a session cookie:
254
+
255
+ \`\`\`javascript
256
+ export class SignIn extends Resource {
257
+ static async post(_target, data, context) {
258
+ const { username, password } = (await data) ?? {};
259
+ try {
260
+ await context.login(username, password);
261
+ } catch {
262
+ return new Response('Invalid credentials', { status: 403 });
263
+ }
264
+ return new Response('Logged in', { status: 200 });
265
+ }
266
+ }
267
+
268
+ export class SignOut extends Resource {
269
+ static async post(_target, _data, context) {
270
+ if (!context?.session?.user) return new Response(null, { status: 401 });
271
+ await context.session.update({ user: null });
272
+ return new Response('Logged out', { status: 200 });
273
+ }
274
+ }
275
+ \`\`\`
276
+
277
+ - \`context.login(username, password)\` verifies credentials and sets the session cookie on success.
278
+ - To end a session, call \`context.session.update({ user: null })\` — \`update\` is the session's only mutator.
279
+ - \`context.session\` is an empty object (not \`undefined\`) when sessions are enabled and no session cookie is present; test \`context?.session?.user\` to detect an established session.
280
+ - Cookie-based sessions are for browser clients. For non-browser clients, use JWT issuance.
281
+
282
+ ### 4. Creating Authentication Tokens
283
+
284
+ Call \`create_authentication_tokens\` with credentials in the body — no \`Authorization\` header is required when \`username\` and \`password\` are supplied:
285
+
286
+ \`\`\`json
287
+ {
288
+ "operation": "create_authentication_tokens",
289
+ "username": "username",
290
+ "password": "password"
291
+ }
292
+ \`\`\`
293
+
294
+ Response:
295
+
296
+ \`\`\`json
297
+ {
298
+ "operation_token": "<jwt-operation-token>",
299
+ "refresh_token": "<jwt-refresh-token>"
300
+ }
301
+ \`\`\`
302
+
303
+ Pass the \`operation_token\` as a \`Bearer\` token on subsequent requests:
304
+
305
+ \`\`\`bash
306
+ curl --location --request POST 'http://localhost:9925' \\
307
+ --header 'Content-Type: application/json' \\
308
+ --header 'Authorization: Bearer <operation_token>' \\
309
+ --data-raw '{
310
+ "operation": "search_by_hash",
311
+ "schema": "dev",
312
+ "table": "dog",
313
+ "hash_values": [1],
314
+ "get_attributes": ["*"]
315
+ }'
316
+ \`\`\`
317
+
318
+ ### 5. Refreshing the Operation Token
319
+
320
+ When the \`operation_token\` expires, pass the \`refresh_token\` as \`Bearer <refresh_token>\` and call \`refresh_operation_token\`:
321
+
322
+ \`\`\`bash
323
+ curl --location --request POST 'http://localhost:9925' \\
324
+ --header 'Content-Type: application/json' \\
325
+ --header 'Authorization: Bearer <refresh_token>' \\
326
+ --data-raw '{
327
+ "operation": "refresh_operation_token"
328
+ }'
329
+ \`\`\`
330
+
331
+ Response:
332
+
333
+ \`\`\`json
334
+ {
335
+ "operation_token": "<new-jwt-operation-token>"
336
+ }
337
+ \`\`\`
338
+
339
+ When both tokens have expired, call \`create_authentication_tokens\` again with username and password.
340
+
341
+ ### 6. Minting Scoped Tokens
342
+
343
+ A \`super_user\` can mint a scoped token with embedded permissions using \`create_authentication_tokens\` with an inline \`role\`. The minter must be authenticated; no \`password\` may be included in the body. Use \`add_role\`-style \`permission\` structure:
344
+
345
+ \`\`\`json
346
+ {
347
+ "operation": "create_authentication_tokens",
348
+ "username": "reporting-service",
349
+ "role": {
350
+ "permission": {
351
+ "operations": ["read_only"],
352
+ "dev": {
353
+ "tables": {
354
+ "dog": {
355
+ "read": true,
356
+ "insert": false,
357
+ "update": false,
358
+ "delete": false,
359
+ "attribute_permissions": []
360
+ }
361
+ }
362
+ }
363
+ }
364
+ },
365
+ "expires_in": "7d"
366
+ }
367
+ \`\`\`
368
+
369
+ Authenticate the mint request with Basic Authentication or an existing \`super_user\` \`operation_token\`:
370
+
371
+ \`\`\`bash
372
+ curl --location --request POST 'http://localhost:9925' \\
373
+ --header 'Content-Type: application/json' \\
374
+ --header 'Authorization: Basic <base64 of super_user:password>' \\
375
+ --data-raw '{
376
+ "operation": "create_authentication_tokens",
377
+ "username": "reporting-service",
378
+ "role": { "permission": { "operations": ["read_only"] } },
379
+ "expires_in": "7d"
380
+ }'
381
+ \`\`\`
382
+
383
+ Key constraints for scoped tokens:
384
+
385
+ - \`username\` is attribution only and must not name an existing user.
386
+ - No refresh token is issued; no user record is created.
387
+ - Scoped tokens cannot be revoked before expiry — choose \`expires_in\` carefully and prefer short lifetimes.
388
+ - \`super_user\` and \`cluster_user\` are always forced to \`false\` in the embedded role.
389
+ - In **mixed-version** clusters, only nodes with scoped-token support accept these tokens; older nodes reject them with 401.
390
+
391
+ ### 7. Issuing Tokens from a Custom Resource
392
+
393
+ Use \`server.operation()\` to mint tokens programmatically inside a Resource. Pass \`authorize: true\` as the **third argument** when the operation should run as the current authenticated user:
394
+
395
+ \`\`\`javascript
396
+ import { Resource, server } from 'harper';
397
+
398
+ export class IssueTokens extends Resource {
399
+ static async get(_target, context) {
400
+ // Issue tokens for the current authenticated user
401
+ const { operation_token, refresh_token } = await server.operation(
402
+ { operation: 'create_authentication_tokens' },
403
+ context,
404
+ true, // third argument: authorize as current user
405
+ );
406
+ return { operation_token, refresh_token };
407
+ }
408
+
409
+ static async post(_target, data) {
410
+ // Issue tokens from credentials supplied in the body
411
+ const { username, password } = await data;
412
+ if (!username || !password) {
413
+ return new Response('username and password required', { status: 400 });
414
+ }
415
+ const { operation_token, refresh_token } = await server.operation({
416
+ operation: 'create_authentication_tokens',
417
+ username,
418
+ password,
419
+ });
420
+ return { operation_token, refresh_token };
421
+ }
422
+ }
423
+
424
+ export class RefreshJWT extends Resource {
425
+ static async post(_target, data) {
426
+ const { refresh_token } = await data;
427
+ if (!refresh_token) {
428
+ return new Response('refresh_token required', { status: 400 });
429
+ }
430
+ const { operation_token } = await server.operation({
431
+ operation: 'refresh_operation_token',
432
+ refresh_token,
433
+ });
434
+ return { operation_token };
435
+ }
436
+ }
437
+ \`\`\`
438
+
439
+ Omit the third argument (or pass \`false\`) when the operation supplies its own credentials.
440
+
441
+ ### 8. Configuring Token Expiry
442
+
443
+ Set timeouts in \`harper-config.yaml\` under the \`authentication\` section:
444
+
445
+ \`\`\`yaml
446
+ authentication:
447
+ operationTokenTimeout: 1d # Default: 1 day
448
+ refreshTokenTimeout: 30d # Default: 30 days
449
+ \`\`\`
450
+
451
+ Valid duration strings follow the \`jsonwebtoken\` package format (e.g., \`1d\`, \`12h\`, \`60m\`). The \`expires_in\` field on scoped token minting accepts the same format.
452
+
453
+ ## Examples
454
+
455
+ ### Full Sign-In / Sign-Out Resource
456
+
457
+ \`\`\`javascript
458
+ export class SignIn extends Resource {
459
+ static async post(_target, data, context) {
460
+ const { username, password } = (await data) ?? {};
461
+ try {
462
+ await context.login(username, password);
463
+ } catch {
464
+ return new Response('Invalid credentials', { status: 403 });
465
+ }
466
+ return new Response('Logged in', { status: 200 });
467
+ }
468
+ }
469
+
470
+ export class SignOut extends Resource {
471
+ static async post(_target, _data, context) {
472
+ if (!context?.session?.user) return new Response(null, { status: 401 });
473
+ await context.session.update({ user: null });
474
+ return new Response('Logged out', { status: 200 });
475
+ }
476
+ }
477
+ \`\`\`
478
+
479
+ ### Reading the Current Authenticated User in a Static Verb
480
+
481
+ \`\`\`javascript
482
+ static async get(_target, context) {
483
+ const user = context?.user;
484
+ if (!user) return new Response(null, { status: 401 });
485
+ return { username: user.username, role: user.role };
486
+ }
487
+ \`\`\`
488
+
489
+ ### Minting Tokens via cURL
490
+
491
+ \`\`\`bash
492
+ curl --location --request POST 'http://localhost:9925' \\
493
+ --header 'Content-Type: application/json' \\
494
+ --data-raw '{
495
+ "operation": "create_authentication_tokens",
496
+ "username": "username",
497
+ "password": "password"
498
+ }'
499
+ \`\`\`
500
+
501
+ ## Notes
502
+
503
+ - JWT authentication is **preferred over Basic Auth** when you want to avoid sending credentials on every request, your client can store tokens, or you have multiple sequential requests. For simple or **server-to-server** scenarios, use **Basic Authentication**.
504
+ - Always use **HTTPS** in production to protect tokens in transit.
505
+ - Treat tokens like passwords. If a token is compromised, it remains valid until expiry — use shorter \`operationTokenTimeout\` values in high-security environments.
506
+ - \`context.login\` and \`context.session\` require \`enableSessions: true\` in \`harperdb-config.yaml\`; they are for browser clients only.
507
+ - The \`server.operation()\` third argument (\`authorize: true\`) attributes the operation to — and permission-checks against — the current authenticated user. Omit it when supplying credentials directly in the operation body.
508
+ - In **mixed-version** clusters, scoped tokens are only accepted by nodes that support them; older nodes return 401.
509
+ `,"creating-a-fabric-account-and-cluster":`---
510
+ name: creating-a-fabric-account-and-cluster
511
+ description: How to create a Harper Fabric account, organization, and cluster.
512
+ metadata:
513
+ mode: synthesized
514
+ ---
515
+
516
+ # Creating a Harper Fabric Account and Cluster
517
+
518
+ Follow these steps to set up your Harper Fabric environment for deployment.
519
+
520
+ ## How It Works
521
+
522
+ 1. **Sign Up/In**: Go to [https://fabric.harper.fast/](https://fabric.harper.fast/) and sign up or sign in.
523
+ 2. **Create an Organization**: Create an organization (org) to manage your projects.
524
+ 3. **Create a Cluster**: Create a new cluster. This can be on the free tier, no credit card required.
525
+ 4. **Set Credentials**: During setup, set the cluster username and password to finish configuring it.
526
+ 5. **Get Application URL**: Navigate to the **Config** tab and copy the **Application URL**.
527
+ 6. **Configure Environment**: Update your \`.env\` file or GitHub Actions secrets with cluster-specific credentials.
528
+ 7. **Next Steps**: See the [deploying-to-harper-fabric](deploying-to-harper-fabric.md) rule for detailed instructions on deploying your application successfully.
529
+
530
+ ## Examples
531
+
532
+ ### Environment Configuration
533
+
534
+ \`\`\`bash
535
+ CLI_TARGET_USERNAME='YOUR_CLUSTER_USERNAME'
536
+ CLI_TARGET_PASSWORD='YOUR_CLUSTER_PASSWORD'
537
+ CLI_TARGET='YOUR_CLUSTER_URL'
538
+ \`\`\`
539
+ `,"creating-harper-apps":`---
540
+ name: creating-harper-apps
541
+ description: How to initialize a new Harper application using the CLI.
542
+ metadata:
543
+ mode: synthesized
544
+ ---
545
+
546
+ # Creating Harper Applications
547
+
548
+ The fastest way to start a new Harper project is using the \`create-harper\` CLI tool. This command
549
+ initializes a project with a standard folder structure, essential configuration files, and basic
550
+ schema definitions.
551
+
552
+ ## When to Use
553
+
554
+ Use this command when starting a new Harper application or adding a new Harper microservice to an
555
+ existing architecture.
556
+
557
+ ## Commands
558
+
559
+ Initialize a project using your preferred package manager:
560
+
561
+ ### NPM
562
+
563
+ \`\`\`bash
564
+ npm create harper@latest
565
+ \`\`\`
566
+
567
+ ### PNPM
568
+
569
+ \`\`\`bash
570
+ pnpm create harper@latest
571
+ \`\`\`
572
+
573
+ ### Bun
574
+
575
+ \`\`\`bash
576
+ bun create harper@latest
577
+ \`\`\`
578
+
579
+ ## Options
580
+
581
+ You can specify the project name and template directly:
582
+
583
+ \`\`\`bash
584
+ npm create harper@latest my-app --template default
585
+ \`\`\`
586
+
587
+ ## Next Steps
588
+
589
+ 1. **Configure Environment**: Set up your \`.env\` file with local or cloud credentials.
590
+ 2. **Define Schema**: Modify \`schema.graphql\` to fit your application's data model.
591
+ 3. **Start Development**: Run \`npm run dev\` to start the local Harper instance.
592
+ 4. **Deploy**: Use \`npm run deploy\` to push your application to Harper Fabric.
593
+ `,"custom-resources":`---
594
+ name: custom-resources
595
+ description: How to define custom REST endpoints with JavaScript or TypeScript in Harper.
596
+ metadata:
597
+ mode: generate
598
+ sources:
599
+ - reference/v5/resources/overview.md#Custom External Data Source
600
+ - reference/v5/resources/overview.md#Exporting Resources as Endpoints
601
+ - reference/v5/components/javascript-environment.md#Module Formats
602
+ sourceCommit: 9e6ecf87dd25bb488ad44dc2876ca6439ccd682e
603
+ inputHash: 86858206e33925f0
604
+ ---
605
+
606
+ # Custom Resources
607
+
608
+ Instructions for the agent to follow when defining custom REST endpoints with JavaScript or TypeScript in Harper.
609
+
610
+ ## When to Use
611
+
612
+ Apply this rule when creating custom HTTP endpoints, wrapping external APIs, or registering routes programmatically in a Harper application. Use it any time business logic must live outside a table-backed resource or when a specific URL shape is required.
613
+
614
+ ## How It Works
615
+
616
+ 1. **Import \`Resource\` from the \`harper\` package**: Always import explicitly rather than relying on globals.
617
+
618
+ \`\`\`javascript
619
+ import { tables, Resource } from 'harper';
620
+ \`\`\`
621
+
622
+ 2. **Define a class that \`extends Resource\`**: Implement HTTP methods as \`static\` methods. Each method receives a \`target\` object.
623
+
624
+ \`\`\`javascript
625
+ export class CustomEndpoint extends Resource {
626
+ static get(target) {
627
+ return {
628
+ data: doSomething(),
629
+ };
630
+ }
631
+ }
632
+ \`\`\`
633
+
634
+ 3. **Use \`async\` static methods to call external services**: Return or forward the response directly.
635
+
636
+ \`\`\`javascript
637
+ export class MyExternalData extends Resource {
638
+ static async get(target) {
639
+ const response = await fetch(\`https://api.example.com/\${target.id}\`);
640
+ return response.json();
641
+ }
642
+
643
+ static async put(target, data) {
644
+ return fetch(\`https://api.example.com/\${target.id}\`, {
645
+ method: 'PUT',
646
+ body: JSON.stringify(await data),
647
+ });
648
+ }
649
+ }
650
+ \`\`\`
651
+
652
+ 4. **Export the class to expose it as an endpoint**: The export form controls the resulting URL. Choose the form that matches the URL shape you need.
653
+
654
+ | Export form | URL | Notes |
655
+ | ------------------------------------------- | --------------- | --------------------------------------------------------------- |
656
+ | \`export class Foo extends Resource {}\` | \`/Foo/\` | Class name becomes the path segment. Case-sensitive. |
657
+ | \`export const Bar = { Foo };\` | \`/Bar/Foo/\` | Nest under an object to add a path prefix. |
658
+ | \`export const bar = { 'foo-baz': Foo };\` | \`/bar/foo-baz/\` | Use object keys for lowercase, hyphens, or non-identifier URLs. |
659
+ | \`export { Foo as '/widget/:id' }\` | \`/widget/:id\` | Rename the export to set the path directly. |
660
+ | \`static path = '/widget/:id'\` (class field) | \`/widget/:id\` | Declare path on the class; overrides the export name. |
661
+ | \`server.resources.set('my-path', Foo);\` | \`/my-path/\` | Programmatic registration for dynamic paths. |
662
+
663
+ 5. **Register programmatically when the path is dynamic**: Use \`server.resources.set(\` with a path string and the resource class.
664
+
665
+ \`\`\`javascript
666
+ server.resources.set('my-path', Foo);
667
+ \`\`\`
668
+
669
+ 6. **Declare dynamic path segments with \`static path\`**: Use \`:name\` for a single segment and \`*name\` as a catch-all. Matched values are bound onto \`target.<name>\`.
670
+
671
+ \`\`\`javascript
672
+ export class Widget extends Resource {
673
+ // GET /widget/10/action/jump -> target.id === '10', target.action === 'jump'
674
+ static path = '/widget/:id/action/:action';
675
+ static get(target) {
676
+ return { id: target.id, action: target.action };
677
+ }
678
+ }
679
+ \`\`\`
680
+
681
+ 7. **Resolve path precedence correctly**: Exact and static paths always win over parameterized ones. Among parameterized routes, more specific paths win: a literal segment beats a \`:param\`, which beats a \`*\` wildcard, compared left to right.
682
+
683
+ ## Examples
684
+
685
+ **Wrapping an external API and using it as a cache source:**
686
+
687
+ \`\`\`javascript
688
+ import { tables, Resource } from 'harper';
689
+
690
+ export class MyExternalData extends Resource {
691
+ static async get(target) {
692
+ const response = await fetch(\`https://api.example.com/\${target.id}\`);
693
+ return response.json();
694
+ }
695
+
696
+ static async put(target, data) {
697
+ return fetch(\`https://api.example.com/\${target.id}\`, {
698
+ method: 'PUT',
699
+ body: JSON.stringify(await data),
700
+ });
701
+ }
702
+ }
703
+
704
+ // Use as a cache source for a local table
705
+ tables.MyCache.sourcedFrom(MyExternalData);
706
+ \`\`\`
707
+
708
+ **Catch-all wildcard path:**
709
+
710
+ \`\`\`javascript
711
+ export class Files extends Resource {
712
+ // GET /files/a/b/c.txt -> target.rest === 'a/b/c.txt'
713
+ static path = '/files/*rest';
714
+ static get(target) {
715
+ return { path: target.rest };
716
+ }
717
+ }
718
+ \`\`\`
719
+
720
+ **Root-relative fixed route:**
721
+
722
+ \`\`\`javascript
723
+ export class AcmeChallenge extends Resource {
724
+ static path = '/.well-known/acme-challenge/:token';
725
+ static get(target) {
726
+ return { token: target.token };
727
+ }
728
+ }
729
+ \`\`\`
730
+
731
+ ## Notes
732
+
733
+ - URL path matching is case-sensitive — \`/Foo/\` and \`/foo/\` are different endpoints.
734
+ - A leading \`/\` in \`static path\` makes the path root-relative (top-level), independent of the file's location.
735
+ - A leading \`./\` or a bare name in \`static path\` resolves relative to the component directory.
736
+ - A bare \`*\` (no name) binds under \`target.wildcard\`. A wildcard must be the final segment of the path.
737
+ - \`static path\` takes precedence over the export name when both are present.
738
+ - Parameterized routes appear in the generated OpenAPI document as templated paths (e.g. \`/widget/{id}/action/{action}\`) and in MCP \`resources/templates/list\` as URI templates.
739
+ - When a resource \`extends\` an existing table, avoid conflicting exports between the schema and the JavaScript implementation.
740
+ - Link the \`harper\` package in your component directory to ensure correct typings: \`npm link harper\`. All installed components have \`harper\` automatically linked.
741
+ `,"defining-relationships":`---
742
+ name: defining-relationships
743
+ description: How to define and use relationships between tables in Harper using GraphQL.
744
+ metadata:
745
+ mode: generate
746
+ sources:
747
+ - reference/v5/database/schema.md#Relationships
748
+ - reference/v5/rest/querying.md#Relationships and Joins
749
+ sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
750
+ inputHash: fd399fd81a88f13e
751
+ ---
752
+
753
+ # Defining Relationships Between Tables in Harper
754
+
755
+ Instructions for the agent to follow when defining and querying relationships between tables in Harper using the \`@relationship\` directive.
756
+
757
+ ## When to Use
758
+
759
+ Apply this rule when adding foreign key relationships between schema tables, enabling join queries, or returning nested related records in query results. Use it any time a schema type needs to reference records in another table via a foreign key attribute.
760
+
761
+ ## How It Works
762
+
763
+ 1. **Use \`@relationship(from: attribute)\` for many-to-one or many-to-many**: Place this on the field in the table that holds the foreign key. The \`from\` parameter names the attribute on this table that stores the foreign key referencing the target table's primary key.
764
+
765
+ \`\`\`graphql
766
+ type RealityShow @table @export {
767
+ id: Long @primaryKey
768
+ networkId: Long @indexed
769
+ network: Network @relationship(from: networkId) # many-to-one
770
+ title: String @indexed
771
+ }
772
+
773
+ type Network @table @export {
774
+ id: Long @primaryKey
775
+ name: String @indexed
776
+ }
777
+ \`\`\`
778
+
779
+ If the foreign key attribute is an array, the relationship becomes many-to-many:
780
+
781
+ \`\`\`graphql
782
+ type RealityShow @table @export {
783
+ id: Long @primaryKey
784
+ networkIds: [Long] @indexed
785
+ networks: [Network] @relationship(from: networkIds)
786
+ }
787
+ \`\`\`
788
+
789
+ 2. **Use \`@relationship(to: attribute)\` for one-to-many or many-to-many**: Place this on the table whose primary key is referenced by the foreign key in the target table. The \`to\` parameter names the attribute on the target table that holds the foreign key. The result type **must** be an array.
790
+
791
+ \`\`\`graphql
792
+ type Network @table @export {
793
+ id: Long @primaryKey
794
+ name: String @indexed
795
+ shows: [RealityShow] @relationship(to: networkId) # one-to-many
796
+ }
797
+ \`\`\`
798
+
799
+ 3. **Use \`@relationship(from: attribute, to: attribute)\` for foreign key to foreign key joins**: Specify both \`from\` and \`to\` when neither side uses the primary key. Harper resolves the relationship by searching the target table's \`to\` attribute for matches using this record's \`from\` attribute value. The result type must be an array.
800
+
801
+ \`\`\`graphql
802
+ type OrderItem @table @export {
803
+ id: Long @primaryKey
804
+ orderId: Long @indexed
805
+ productSku: Long @indexed
806
+ products: [Product] @relationship(from: productSku, to: sku)
807
+ }
808
+
809
+ type Product @table @export {
810
+ id: Long @primaryKey
811
+ sku: Long @indexed
812
+ name: String
813
+ }
814
+ \`\`\`
815
+
816
+ 4. **Query across relationships using dot-syntax**: Filter records by related table attributes using chained dot notation. This behaves as an INNER JOIN — only records with a matching related record are returned.
817
+
818
+ \`\`\`
819
+ GET /Product/?brand.name=Microsoft
820
+ GET /Brand/?products.name=Keyboard
821
+ \`\`\`
822
+
823
+ 5. **Include relationship fields in results using \`select()\`**: Relationship attributes are not returned by default. Use \`select()\` to include them, optionally specifying nested fields with \`{}\`.
824
+
825
+ \`\`\`
826
+ GET /Product/?brand.name=Microsoft&select(name,brand)
827
+ GET /Product/?brand.name=Microsoft&select(name,brand{name})
828
+ GET /Product/?name=Keyboard&select(name,brand{name,id})
829
+ \`\`\`
830
+
831
+ When selecting a relationship without filtering on it, Harper performs a LEFT JOIN — the relationship property is omitted if the foreign key is null or references a non-existent record.
832
+
833
+ 6. **Model many-to-many without a junction table**: Store an array of foreign key values and use \`@relationship(from: ...)\` pointing to that array attribute. The array order of the foreign key values is preserved when resolving the relationship.
834
+
835
+ \`\`\`graphql
836
+ type Product @table @export {
837
+ id: Long @primaryKey
838
+ name: String
839
+ resellerIds: [Long] @indexed
840
+ resellers: [Reseller] @relationship(from: "resellerIds")
841
+ }
842
+ \`\`\`
843
+
844
+ 7. **Define self-referential relationships** for parent-child hierarchies by pointing \`@relationship\` back at the same table type.
845
+
846
+ ## Examples
847
+
848
+ **Full schema with bidirectional relationships:**
849
+
850
+ \`\`\`graphql
851
+ type Product @table @export {
852
+ id: Long @primaryKey
853
+ name: String
854
+ brandId: Long @indexed
855
+ brand: Brand @relationship(from: "brandId")
856
+ }
857
+
858
+ type Brand @table @export {
859
+ id: Long @primaryKey
860
+ name: String
861
+ products: [Product] @relationship(to: "brandId")
862
+ }
863
+ \`\`\`
864
+
865
+ **Querying with joins and nested select:**
866
+
867
+ \`\`\`
868
+ GET /Product/?brand.name=Microsoft&select(name,brand{name,id})
869
+ GET /Brand/?products.name=Keyboard
870
+ \`\`\`
871
+
872
+ **Many-to-many query with nested select:**
873
+
874
+ \`\`\`
875
+ GET /Product/?resellers.name=Cool Shop&select(id,name,resellers{name,id})
876
+ \`\`\`
877
+
878
+ ## Notes
879
+
880
+ - Every attribute named in \`from\` or \`to\` must exist on the respective table and be annotated with \`@indexed\` to support join queries.
881
+ - The \`to\`-only and \`from\`+\`to\` forms both require the result field type to be an array (e.g., \`[RealityShow]\`).
882
+ - The \`from\`-only form on a non-array attribute produces a many-to-one relationship; on an array attribute it produces many-to-many.
883
+ - Self-referential relationships are supported for hierarchical data within a single table.
884
+ `,"delegating-to-the-built-in-agent":`---
885
+ name: delegating-to-the-built-in-agent
886
+ description: How to delegate tasks to Harper's built-in agent via the CLI and the agent operations API.
887
+ metadata:
888
+ mode: synthesized
889
+ ---
890
+
891
+ # Delegating to the Built-in Agent
892
+
893
+ Harper 5.2+ ships with a **built-in agent** that runs _inside_ the server, on the main thread
894
+ adjacent to the operations API. Because it runs in-process, it can do things a remote client
895
+ cannot: call the operations API as RBAC-filtered tools, read and write component files under the
896
+ instance's components root, attach the V8 inspector to worker threads to debug and profile them,
897
+ schedule follow-up work, and consult the Harper best-practices skill. You send it a natural-language
898
+ task; it runs a tool-using loop under a super_user identity and reports back.
899
+
900
+ ## When to Use
901
+
902
+ Delegate to the built-in agent when the work is best done **on the instance itself** rather than
903
+ from your local client:
904
+
905
+ - Operating on a deployed instance in place — inspect the schema, build or adjust a component,
906
+ restart, run an operation.
907
+ - Debugging or profiling a running instance — attach to a worker thread, capture a CPU profile,
908
+ set a logpoint.
909
+ - Handing off a larger, multi-step task to an agent that already has the instance's tools,
910
+ filesystem, and credentials in context.
911
+
912
+ Do the work in your own client instead when it's purely local (editing source before deploy) or
913
+ when you don't want a server-side agent making changes.
914
+
915
+ **Prerequisites:** the target instance must have the agent enabled (an \`agent:\` config block with
916
+ \`enabled: true\`) and a configured generative model backend. All agent operations require
917
+ **super_user**.
918
+
919
+ ## How It Works
920
+
921
+ The lifecycle assumes you have already deployed to and authenticated with the target instance (see
922
+ [deploying-to-harper-fabric.md](deploying-to-harper-fabric.md) — \`harper login\` stores a token so
923
+ you don't repeat credentials). Delegation reuses that same target and credentials.
924
+
925
+ There are two equivalent ways to drive the agent.
926
+
927
+ ### Option A — the \`harper agent\` CLI (simplest)
928
+
929
+ A thin client over the agent operations API that reuses your stored \`harper login\` credentials, so
930
+ no connector setup is needed:
931
+
932
+ \`\`\`bash
933
+ # One-shot: send a task, print the reply, exit
934
+ harper agent "Describe the schema, then add a price index to the Product table."
935
+
936
+ # Interactive session (REPL)
937
+ harper agent
938
+
939
+ # Against a specific remote instead of the logged-in default
940
+ harper agent --target <Application URL> "List the databases and tables."
941
+ \`\`\`
942
+
943
+ The CLI polls the run to completion and renders the transcript (tool calls, results, and the
944
+ agent's reply). When a run needs approval for a destructive action, it prompts you inline.
945
+
946
+ ### Option B — the agent operations API (programmatic)
947
+
948
+ Call the operations API directly (HTTP POST to the ops endpoint, super_user auth). This is the path
949
+ to use from scripts and services.
950
+
951
+ 1. **Start a task** with \`agent_prompt\`. Returns a \`session_id\` and a \`status\`.
952
+
953
+ \`\`\`bash
954
+ curl -s -u <user>:<pass> <ops-endpoint> \\
955
+ -H 'Content-Type: application/json' \\
956
+ -d '{"operation":"agent_prompt","message":"Build a Customer table (id, email, name) exported over REST."}'
957
+ \`\`\`
958
+
959
+ 2. **Poll for progress** with \`get_agent_session\`, passing the \`session_id\`. The returned session
960
+ carries the \`status\`, the \`messages\` transcript, and any \`pendingApprovals\`.
961
+
962
+ \`\`\`bash
963
+ curl -s -u <user>:<pass> <ops-endpoint> \\
964
+ -H 'Content-Type: application/json' \\
965
+ -d '{"operation":"get_agent_session","session_id":"<id>"}'
966
+ \`\`\`
967
+
968
+ Poll until \`status\` leaves \`running\` — terminal states are \`completed\`, \`aborted\`, and \`error\`;
969
+ \`awaiting_approval\` means it is paused for an approval decision (see step 3).
970
+
971
+ 3. **Approve or deny a paused action.** When the agent enabled configuration has \`autoApprove:false\`,
972
+ a destructive tool call pauses the run with a \`pendingApprovals[]\` entry. Resolve it with
973
+ \`approve_agent_action\`, then poll again — approval executes the saved call, denial hands the
974
+ rejection back to the agent so it can adjust.
975
+
976
+ \`\`\`bash
977
+ curl -s -u <user>:<pass> <ops-endpoint> \\
978
+ -H 'Content-Type: application/json' \\
979
+ -d '{"operation":"approve_agent_action","session_id":"<id>","approval_id":"<approval-id>","approved":true}'
980
+ \`\`\`
981
+
982
+ 4. **Continue the conversation** by passing the same \`session_id\` back into \`agent_prompt\` with a
983
+ new \`message\`. Omit \`session_id\` to start a fresh session.
984
+
985
+ Supporting operations: \`list_agent_sessions\` (recent sessions), \`cancel_agent_run\` (terminate a
986
+ running or paused session), and \`set_agent_config\` (adjust \`autoApprove\`, \`allowDestructive\`,
987
+ \`model\`, and related settings on a running instance).
988
+
989
+ ## Examples
990
+
991
+ **Delegate a build to a deployed Fabric instance and wait for the result:**
992
+
993
+ \`\`\`bash
994
+ harper login <Application URL>
995
+ harper agent --target <Application URL> \\
996
+ "Create a Product table (id, name, price) exported over REST, then confirm the endpoint responds."
997
+ \`\`\`
998
+
999
+ **Programmatic start-and-poll loop:**
1000
+
1001
+ \`\`\`bash
1002
+ SID=$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \\
1003
+ -d '{"operation":"agent_prompt","message":"Add a vector index to the Document.embedding field."}' \\
1004
+ | jq -r .session_id)
1005
+
1006
+ while [ "$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \\
1007
+ -d "{\\"operation\\":\\"get_agent_session\\",\\"session_id\\":\\"$SID\\"}" | jq -r .status)" = "running" ]; do
1008
+ sleep 3
1009
+ done
1010
+ \`\`\`
1011
+
1012
+ ## Notes
1013
+
1014
+ - **All agent operations require super_user.** Authenticate with \`harper login\`, which stores a
1015
+ short-lived JWT (operation token) plus a refresh token rather than your password — prefer that
1016
+ over passing credentials inline, and never embed a raw password in scripts or client config.
1017
+ - **Approvals are your safety gate.** With \`autoApprove:false\`, the agent pauses before destructive
1018
+ tools (writing files, deploying, restarting) so an operator decides. Set \`autoApprove:true\` only
1019
+ when you want unattended runs.
1020
+ - **Sessions are single-active.** A session that is \`running\` or \`awaiting_approval\` rejects a new
1021
+ \`agent_prompt\`; resolve the approval or cancel the run first.
1022
+ - **MCP alternative.** For MCP-native clients, an instance with MCP enabled exposes the agent as
1023
+ curated MCP tools (\`agent_prompt\`, \`get_agent_session\`, \`list_agent_sessions\`) at the ops API's
1024
+ \`/mcp\` endpoint — the same delegation loop over the MCP transport instead of raw operations.
1025
+ `,"deploying-to-harper-fabric":`---
1026
+ name: deploying-to-harper-fabric
1027
+ description: How to deploy a Harper application to the Harper Fabric cloud.
1028
+ metadata:
1029
+ mode: generate
1030
+ sources:
1031
+ - reference/v5/components/applications.md#Remote Management
1032
+ - >-
1033
+ fabric/cluster-creation-management.md#Connecting the Harper CLI to a
1034
+ Cluster
1035
+ sourceCommit: 2cfb81318f17e0aca2d600109e6ad813d2d0443e
1036
+ inputHash: c821d15a19e2370f
1037
+ ---
1038
+
1039
+ # Deploying to Harper Fabric
1040
+
1041
+ Instructions for the agent to follow when deploying a Harper application to a remote Harper Fabric cloud cluster.
1042
+
1043
+ ## When to Use
1044
+
1045
+ Apply this rule when deploying a Harper application to a remote Harper instance or Fabric cluster, including first-time deploys, redeployments, rollbacks, and CI/CD pipeline deployments. Also apply it when provisioning credentials for private repository deploys. See [creating-a-fabric-account-and-cluster.md](creating-a-fabric-account-and-cluster.md) for setting up the cluster before deploying.
1046
+
1047
+ ## How It Works
1048
+
1049
+ 1. **Authenticate against the remote cluster**: Run \`harper login\` once, pointing at the cluster's Application URL (found on the cluster's **Config → Overview** page). The CLI stores the token and writes \`HARPER_CLI_TARGET\` to a local \`.env\`.
1050
+
1051
+ \`\`\`bash
1052
+ harper login <Application URL>
1053
+ # Provide cluster username and password when prompted
1054
+ \`\`\`
1055
+
1056
+ 2. **Deploy the application**: After login, run \`harper deploy\` without repeating credentials. Use \`restart=true\` and \`replicated=true\` for production deploys.
1057
+
1058
+ \`\`\`bash
1059
+ harper deploy \\
1060
+ project=<name> \\
1061
+ package=<package> \\
1062
+ target=<remote> \\
1063
+ restart=true \\
1064
+ replicated=true
1065
+ \`\`\`
1066
+
1067
+ 3. **Choose a package source**: Set the \`package\` parameter to any valid npm dependency value. Options:
1068
+
1069
+ | Source | Example value |
1070
+ | ----------------------- | ------------------------------------------------------------ |
1071
+ | Current local directory | Omit \`package\` |
1072
+ | npm package | \`package="@harperdb/status-check"\` |
1073
+ | GitHub (shorthand) | \`package="HarperFast/status-check"\` |
1074
+ | GitHub (URL) | \`package="https://github.com/HarperFast/status-check"\` |
1075
+ | Private repo (SSH) | \`package="git+ssh://git@github.com:HarperDB/secret-app.git"\` |
1076
+ | Tarball | \`package="https://example.com/application.tar.gz"\` |
1077
+
1078
+ When using git tags, use the \`semver\` directive:
1079
+
1080
+ \`\`\`
1081
+ HarperFast/application-template#semver:v1.0.0
1082
+ \`\`\`
1083
+
1084
+ 4. **Deploy by reference (pinned commit)**: Use \`by_ref=true\` to send a pinned git reference instead of uploading a snapshot. The cluster fetches and builds from that exact commit SHA.
1085
+
1086
+ \`\`\`bash
1087
+ harper deploy by_ref=true restart=true replicated=true
1088
+ \`\`\`
1089
+
1090
+ - \`by_ref\` — Build the package reference from the local repository.
1091
+ - \`ref\` _(optional)_ — Deploy a specific commit, tag, or branch instead of \`HEAD\`. Implies \`by_ref\`. Tags and branches in \`refs/tags\` and \`refs/heads\` namespaces are resolved to a full commit SHA before the deploy is sent.
1092
+ - \`credential\` _(optional)_ — Set to \`true\` to authenticate the clone with the stored credential for the repository's host. Omit for public repositories.
1093
+
1094
+ \`\`\`bash
1095
+ # Deploy a specific tag
1096
+ harper deploy ref=v1.2.0 restart=true replicated=true
1097
+
1098
+ # Roll back by deploying an older commit
1099
+ harper deploy ref=9f8c2a1 restart=true replicated=true
1100
+ \`\`\`
1101
+
1102
+ **Important constraints on refs:**
1103
+ - A full commit SHA is accepted directly with no resolution.
1104
+ - Every other \`ref\` must resolve to something a clone can fetch: \`refs/tags/*\` or \`refs/heads/*\`, or a bare branch or tag name.
1105
+ - Qualified refs outside those two namespaces (e.g., \`refs/pull/123/head\`) are rejected.
1106
+ - Commit and push before deploying — the cluster clones from the remote and only sees pushed commits.
1107
+ - Run \`git fetch\` if a ref can't be resolved, or pass a full commit SHA.
1108
+
1109
+ 5. **Handle private repositories**: Pass \`credential=true\` so the CLI attaches a credentials reference that the cluster resolves in memory at clone time. No token travels in the operation body or lands on disk.
1110
+
1111
+ \`\`\`bash
1112
+ harper deploy by_ref=true credential=true restart=true replicated=true
1113
+ \`\`\`
1114
+
1115
+ Provision the credential once with \`setup=true\` before using \`credential=true\`.
1116
+
1117
+ 6. **Provision a deploy credential**: Run \`harper deploy setup=true\` once per component and source to provision credentials for private deploys. This operation requires **super_user** — run it with an administrative credential, not the CI identity.
1118
+
1119
+ \`\`\`bash
1120
+ harper deploy setup=true
1121
+ \`\`\`
1122
+
1123
+ This interactive command:
1124
+ 1. Fetches the cluster's public key with \`get_secrets_public_key\`.
1125
+ 2. Encrypts the token locally into an \`enc:v1:\` envelope.
1126
+ 3. Stores only the ciphertext with \`set_secret\`, in the component-scoped tier.
1127
+ 4. Grants the component permission to resolve it with \`grant_secret\`.
1128
+ 5. Prints the \`credentials\` reference for the deploy to use.
1129
+
1130
+ **Use a fine-grained PAT** for GitHub repositories. The prompt defaults to a fine-grained personal access token with **Contents: Read-only** on that one repository. If you use the \`gh\` CLI session token instead, it typically carries \`read:org\`, \`repo\`, \`gist\`, and \`workflow\` scopes across your whole account — the CLI prints a warning if you choose it. Use the narrowest credential that does the job, because the stored token is replayed on every cold deploy and rollback.
1131
+
1132
+ 7. **Use environment variables for CI/CD**: Instead of \`harper login\`, export credentials as environment variables.
1133
+
1134
+ \`\`\`bash
1135
+ export HARPER_CLI_USERNAME=<username>
1136
+ export HARPER_CLI_PASSWORD=<password>
1137
+ harper deploy \\
1138
+ project=<name> \\
1139
+ package=<package> \\
1140
+ target=<remote> \\
1141
+ restart=true \\
1142
+ replicated=true
1143
+ \`\`\`
1144
+
1145
+ 8. **Use dedicated auth parameters for one-off commands** (not recommended for production): Pass \`auth_username\` and \`auth_password\` directly. These take precedence over environment variables and saved login tokens.
1146
+
1147
+ \`\`\`bash
1148
+ harper deploy \\
1149
+ project=<name> \\
1150
+ package=<package> \\
1151
+ auth_username=<username> \\
1152
+ auth_password=<password> \\
1153
+ target=<remote> \\
1154
+ restart=true \\
1155
+ replicated=true
1156
+ \`\`\`
1157
+
1158
+ ## Examples
1159
+
1160
+ **Standard deploy after login:**
1161
+
1162
+ \`\`\`bash
1163
+ harper login https://my-cluster.harperdbcloud.com
1164
+ harper deploy \\
1165
+ project=my-app \\
1166
+ package="HarperFast/my-app" \\
1167
+ target=https://my-cluster.harperdbcloud.com \\
1168
+ restart=true \\
1169
+ replicated=true
1170
+ \`\`\`
1171
+
1172
+ **Deploy by reference with a tag:**
1173
+
1174
+ \`\`\`bash
1175
+ harper deploy ref=v1.2.0 restart=true replicated=true
1176
+ \`\`\`
1177
+
1178
+ **Deploy a private GitHub repo by reference:**
1179
+
1180
+ \`\`\`bash
1181
+ # Provision credential once (requires super_user)
1182
+ harper deploy setup=true
1183
+
1184
+ # Deploy using stored credential
1185
+ harper deploy by_ref=true credential=true restart=true replicated=true
1186
+ \`\`\`
1187
+
1188
+ **GitHub Actions — pull request deploy:**
1189
+
1190
+ \`\`\`bash
1191
+ harper deploy ref=\${{ github.event.pull_request.head.sha }} restart=true replicated=true
1192
+ \`\`\`
1193
+
1194
+ **CI/CD deploy using environment variables:**
1195
+
1196
+ \`\`\`bash
1197
+ export HARPER_CLI_USERNAME=<username>
1198
+ export HARPER_CLI_PASSWORD=<password>
1199
+ harper deploy \\
1200
+ project=my-app \\
1201
+ package="@myorg/my-app" \\
1202
+ target=https://my-cluster.harperdbcloud.com \\
1203
+ restart=true \\
1204
+ replicated=true
1205
+ \`\`\`
1206
+
1207
+ ## Notes
1208
+
1209
+ - The cluster's Application URL is found on the **Config → Overview** page of the Fabric dashboard.
1210
+ - \`harper deploy setup=true\` calls \`get_secrets_public_key\`, \`set_secret\`, and \`grant_secret\`, all of which require **super_user**. Do not run it with the CI identity.
1211
+ - The \`enc:v1:\` envelope means the plaintext token never leaves your machine — only ciphertext is stored and replicated.
1212
+ - Secrets are stored scoped to the component, not in the global \`processEnv\` tier. If a global secret already exists at the derived name, it is converted to the scoped tier.
1213
+ - For SSH-based private repos, use the \`add_ssh_key\` operation to register keys before deploying.
1214
+ - If your application requires a build step that cannot run on the cluster node, deploy a built payload (omit \`by_ref\`) instead of deploying by reference.
1215
+ - The unpushed-commit check is skipped under GitHub Actions; the dirty-tree warning still applies.
1216
+ - Annotated tags in \`refs/tags\` resolve to the commit they point at, not the tag object.
1217
+ `,"extending-tables":`---
1218
+ name: extending-tables
1219
+ description: How to add custom logic to automatically generated table resources in Harper.
1220
+ metadata:
1221
+ mode: generate
1222
+ sources:
1223
+ - reference/v5/resources/overview.md#Extending a Table
1224
+ - reference/v5/resources/resource-api.md#Throwing Errors
1225
+ sourceCommit: 2cfb81318f17e0aca2d600109e6ad813d2d0443e
1226
+ inputHash: 925aa3c817b6c9f1
1227
+ ---
1228
+
1229
+ # Extending Tables
1230
+
1231
+ Instructions for the agent to follow when adding custom logic to automatically generated table resources in Harper.
1232
+
1233
+ ## When to Use
1234
+
1235
+ Apply this rule when you need to override or augment the default HTTP handler behavior (GET, POST, PUT, PATCH, DELETE) for a Harper table resource. Use it when a table needs computed properties, input transformation, authorization checks, or custom error responses.
1236
+
1237
+ ## How It Works
1238
+
1239
+ 1. **Define the table schema without \`@export\`**: In \`schema.graphql\`, declare the table type but omit the \`@export\` directive. Leaving \`@export\` on the schema while also exporting a subclass with the same name produces conflicting endpoints.
1240
+
1241
+ \`\`\`graphql
1242
+ # Omit the \`@export\` directive
1243
+ type MyTable @table {
1244
+ id: Long @primaryKey
1245
+ # ...
1246
+ }
1247
+ \`\`\`
1248
+
1249
+ 2. **Extend the generated table class in \`resources.js\`**: Use \`extends tables.<TableName>\` to subclass the auto-generated resource. The exported JavaScript class owns the URL instead of the schema type.
1250
+
1251
+ \`\`\`javascript
1252
+ export class MyTable extends tables.MyTable {
1253
+ static async get(target) {
1254
+ const record = await super.get(target);
1255
+ return { ...record, computedField: 'value' };
1256
+ }
1257
+
1258
+ static async post(target, data) {
1259
+ return this.create({ ...(await data), status: 'pending' });
1260
+ }
1261
+ }
1262
+ \`\`\`
1263
+
1264
+ 3. **Call \`super\` with the correct argument form per operation**: When delegating to the default behavior, match arguments to the operation type:
1265
+ - Reads and deletes: \`super.get(target)\` / \`super.delete(target)\`
1266
+ - Collection create: \`super.post(target, record)\` — target carries no id
1267
+ - Updates: \`super.put(target, data)\` / \`super.patch(target, data)\`
1268
+
1269
+ 4. **Set \`statusCode\` on thrown errors to control HTTP status**: A plain \`.status\` property is ignored. Use \`.statusCode\` on the error object:
1270
+
1271
+ \`\`\`javascript
1272
+ const error = new Error('Name is required');
1273
+ error.statusCode = 400; // use statusCode, NOT status
1274
+ throw error;
1275
+ \`\`\`
1276
+
1277
+ Uncaught errors are caught by the protocol handler and produce error responses for REST.
1278
+
1279
+ 5. **Configure Harper to load both files**: Ensure your configuration references the schema and resource files:
1280
+
1281
+ \`\`\`yaml
1282
+ rest: true
1283
+ graphqlSchema:
1284
+ files: schema.graphql
1285
+ jsResource:
1286
+ files: resources.js
1287
+ \`\`\`
1288
+
1289
+ ## Examples
1290
+
1291
+ Full example extending a table with a computed GET response, a custom POST, and a guarded handler that throws a typed error:
1292
+
1293
+ \`\`\`javascript
1294
+ export class MyTable extends tables.MyTable {
1295
+ static async get(target) {
1296
+ // get the record from the database
1297
+ const record = await super.get(target);
1298
+ // add a computed property before returning
1299
+ return { ...record, computedField: 'value' };
1300
+ }
1301
+
1302
+ static async post(target, data) {
1303
+ // custom action on POST; return the write so the response waits for the commit
1304
+ return this.create({ ...(await data), status: 'pending' });
1305
+ }
1306
+
1307
+ static async delete(target) {
1308
+ if (!authorized) {
1309
+ const error = new Error('Forbidden');
1310
+ error.statusCode = 403;
1311
+ throw error;
1312
+ }
1313
+ return super.delete(target);
1314
+ }
1315
+ }
1316
+ \`\`\`
1317
+
1318
+ ## Notes
1319
+
1320
+ - Always omit \`@export\` from the schema type when you export a subclass with the same name in JavaScript. The exported class owns the endpoint registration.
1321
+ - Call \`super.get/post/put/patch/delete\` to preserve Harper's default behavior unless you intend to replace it entirely.
1322
+ - \`statusCode\` on an error object controls the HTTP response status for REST. A \`.status\` property is ignored.
1323
+ - See [\`Database / Schema\`](/reference/v5/database/schema.md) for full schema API details.
1324
+ `,"handling-binary-data":`---
1325
+ name: handling-binary-data
1326
+ description: How to store and serve binary data like images or audio in Harper.
1327
+ metadata:
1328
+ mode: generate
1329
+ sources:
1330
+ - reference/v5/database/api.md#Accepting Binary in JSON Requests
1331
+ - reference/v5/database/api.md#Serving Binary from a Resource
1332
+ - reference/v5/rest/content-types.md#Storing Arbitrary Content Types
1333
+ sourceCommit: 2cfb81318f17e0aca2d600109e6ad813d2d0443e
1334
+ inputHash: 8be9e31f009e2d27
1335
+ ---
1336
+
1337
+ # Handling Binary Data
1338
+
1339
+ Instructions for the agent to follow when storing and serving binary data (images, audio, arbitrary content types) in Harper.
1340
+
1341
+ ## When to Use
1342
+
1343
+ Apply this rule when a Harper resource needs to accept, store, or serve binary payloads such as images, audio files, or calendar data. Use it when clients send raw binary via \`PUT\`/\`POST\`, or when they send \`base64\`-encoded data inside a JSON body.
1344
+
1345
+ ## How It Works
1346
+
1347
+ 1. **Accept base64-encoded binary from JSON clients**: REST clients that cannot post raw binary send \`base64\` inside a JSON body. In the resource override, \`await\` the \`record\` promise before reading any fields — reading fields off the unresolved promise silently yields \`undefined\` and stores the raw base64 string. Decode the field with \`Buffer.from\` and wrap it with \`createBlob\`, recording the MIME type.
1348
+
1349
+ \`\`\`typescript
1350
+ import { type RequestTargetOrId, tables, createBlob } from 'harper';
1351
+
1352
+ export class Photo extends tables.Photo {
1353
+ static async post(target: RequestTargetOrId, record: any) {
1354
+ const body = await record;
1355
+ if (!body) return new Response('A JSON body is required', { status: 400 });
1356
+ if (body.data) {
1357
+ body.data = createBlob(Buffer.from(body.data, body.encoding || 'base64'), {
1358
+ type: body.contentType || 'application/octet-stream',
1359
+ });
1360
+ }
1361
+ return super.post(target, body);
1362
+ }
1363
+ }
1364
+ \`\`\`
1365
+
1366
+ 2. **Serve binary from a resource**: Override \`get\` to return a response object with the blob's MIME type in the \`Content-Type\` header and the blob as the body. Harper streams it to the client.
1367
+
1368
+ \`\`\`typescript
1369
+ export class Photo extends tables.Photo {
1370
+ static async get(target: RequestTargetOrId) {
1371
+ const record = await super.get(target);
1372
+ if (record?.data) {
1373
+ return {
1374
+ status: 200,
1375
+ headers: { 'Content-Type': record.data.type || 'application/octet-stream' },
1376
+ body: record.data,
1377
+ };
1378
+ }
1379
+ return record;
1380
+ }
1381
+ }
1382
+ \`\`\`
1383
+
1384
+ 3. **Store arbitrary content types via raw PUT/POST**: When a \`PUT\` or \`POST\` arrives with a non-standard \`Content-Type\` (e.g., \`text/calendar\`, \`image/gif\`), Harper automatically stores the content as a record with \`contentType\` and \`data\` properties. Retrieving that record returns the response with the original \`Content-Type\` and body. If the content type is not from the \`text\` family, the data is treated as binary (a Node.js \`Buffer\`).
1385
+
1386
+ Use \`application/octet-stream\` for generic binary data, or target a specific property:
1387
+
1388
+ \`\`\`
1389
+ PUT /my-resource/33/image
1390
+ Content-Type: image/gif
1391
+
1392
+ ...image data...
1393
+ \`\`\`
1394
+
1395
+ ## Examples
1396
+
1397
+ **Storing a calendar entry with a raw PUT:**
1398
+
1399
+ \`\`\`
1400
+ PUT /my-resource/33
1401
+ Content-Type: text/calendar
1402
+
1403
+ BEGIN:VCALENDAR
1404
+ VERSION:2.0
1405
+ ...
1406
+ \`\`\`
1407
+
1408
+ Harper stores this as:
1409
+
1410
+ \`\`\`json
1411
+ { "contentType": "text/calendar", "data": "BEGIN:VCALENDAR\\nVERSION:2.0\\n..." }
1412
+ \`\`\`
1413
+
1414
+ **Full resource class handling base64 JSON upload and binary serving:**
1415
+
1416
+ \`\`\`typescript
1417
+ import { type RequestTargetOrId, tables, createBlob } from 'harper';
1418
+
1419
+ export class Photo extends tables.Photo {
1420
+ static async post(target: RequestTargetOrId, record: any) {
1421
+ const body = await record;
1422
+ if (!body) return new Response('A JSON body is required', { status: 400 });
1423
+ if (body.data) {
1424
+ body.data = createBlob(Buffer.from(body.data, body.encoding || 'base64'), {
1425
+ type: body.contentType || 'application/octet-stream',
1426
+ });
1427
+ }
1428
+ return super.post(target, body);
1429
+ }
1430
+
1431
+ static async get(target: RequestTargetOrId) {
1432
+ const record = await super.get(target);
1433
+ if (record?.data) {
1434
+ return {
1435
+ status: 200,
1436
+ headers: { 'Content-Type': record.data.type || 'application/octet-stream' },
1437
+ body: record.data,
1438
+ };
1439
+ }
1440
+ return record;
1441
+ }
1442
+ }
1443
+ \`\`\`
1444
+
1445
+ ## Notes
1446
+
1447
+ - Always \`await\` the \`record\` parameter before accessing its properties; accessing fields on the unresolved promise yields \`undefined\`.
1448
+ - \`createBlob\` accepts a \`Buffer\` and an options object with a \`type\` property for the MIME type. Fall back to \`application/octet-stream\` when no MIME type is provided.
1449
+ - For schema-level blob field definitions, see [using-blob-datatype.md](using-blob-datatype.md).
1450
+ `,"load-env":`---
1451
+ name: load-env
1452
+ description: >-
1453
+ How to load environment variables from .env files into a Harper application
1454
+ using the loadEnv plugin.
1455
+ metadata:
1456
+ mode: generate
1457
+ sources:
1458
+ - reference/v5/environment-variables/overview.md
1459
+ sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
1460
+ inputHash: b4db5ede6b93d426
1461
+ ---
1462
+
1463
+ # Load Environment Variables with loadEnv
1464
+
1465
+ Instructions for the agent to follow when loading environment variables from \`.env\` files into a Harper application using the \`loadEnv\` plugin.
1466
+
1467
+ ## When to Use
1468
+
1469
+ Apply this rule when a Harper application needs to supply secrets, API endpoints, or other configuration values to component code via \`process.env\` without hardcoding them. Use \`loadEnv\` any time you need to load one or more \`.env\` files at application startup.
1470
+
1471
+ ## How It Works
1472
+
1473
+ 1. **Declare \`loadEnv\` in \`config.yaml\`**: Add \`loadEnv\` as the first entry in \`config.yaml\`. It is built into Harper and requires no installation.
1474
+
1475
+ \`\`\`yaml
1476
+ loadEnv:
1477
+ files: '.env'
1478
+ \`\`\`
1479
+
1480
+ 2. **Place \`loadEnv\` first**: Harper is a single-process application. List \`loadEnv\` before all other components so that environment variables are available on \`process.env\` before dependent components start.
1481
+
1482
+ \`\`\`yaml
1483
+ # config.yaml — loadEnv must come first
1484
+ loadEnv:
1485
+ files: '.env'
1486
+
1487
+ rest: true
1488
+
1489
+ myApp:
1490
+ files: './src/*.js'
1491
+ \`\`\`
1492
+
1493
+ 3. **Access loaded values in component code**: After \`loadEnv\` runs, all loaded values are available on \`process.env\` and shared across all components.
1494
+
1495
+ 4. **Control override behavior**: By default, existing environment variables take precedence over values in \`.env\` files. Set \`override: true\` to make loaded values win instead.
1496
+
1497
+ \`\`\`yaml
1498
+ loadEnv:
1499
+ files: '.env'
1500
+ override: true
1501
+ \`\`\`
1502
+
1503
+ 5. **Load multiple files**: Pass an array of paths or a glob pattern to \`files\`. Files are loaded in the order specified.
1504
+
1505
+ \`\`\`yaml
1506
+ loadEnv:
1507
+ files:
1508
+ - '.env'
1509
+ - '.env.local'
1510
+ \`\`\`
1511
+
1512
+ or with a glob:
1513
+
1514
+ \`\`\`yaml
1515
+ loadEnv:
1516
+ files: 'env-vars/*'
1517
+ \`\`\`
1518
+
1519
+ ### Configuration Options
1520
+
1521
+ | Option | Type | Required | Description |
1522
+ | ---------- | -------------------- | -------- | -------------------------------------------------------------------------------------- |
1523
+ | \`files\` | \`string \\| string[]\` | **Yes** | Path(s) or glob pattern(s) to the env file(s) to load. |
1524
+ | \`override\` | \`boolean\` | No | If \`true\`, loaded values override existing environment variables. Defaults to \`false\`. |
1525
+
1526
+ ## Examples
1527
+
1528
+ **Single file, default behavior:**
1529
+
1530
+ \`\`\`yaml
1531
+ # config.yaml
1532
+ loadEnv:
1533
+ files: '.env'
1534
+
1535
+ rest: true
1536
+
1537
+ myApp:
1538
+ files: './src/*.js'
1539
+ \`\`\`
1540
+
1541
+ **Multiple files with override:**
1542
+
1543
+ \`\`\`yaml
1544
+ # config.yaml
1545
+ loadEnv:
1546
+ files:
1547
+ - '.env'
1548
+ - '.env.local'
1549
+ override: true
1550
+
1551
+ rest: true
1552
+
1553
+ myApp:
1554
+ files: './src/*.js'
1555
+ \`\`\`
1556
+
1557
+ ## Notes
1558
+
1559
+ - \`loadEnv\` loads values into \`process.env\` for **application** code only — it does not configure Harper itself.
1560
+ - Harper's own instance-wide configuration is composed at startup **before** any component's \`loadEnv\` runs. Variables such as \`HARPER_CONFIG\`, \`HARPER_SET_CONFIG\`, and \`HARPER_DEFAULT_CONFIG\` delivered through a \`.env\` file are read too late and are ignored. Set Harper configuration directly in the configuration file or export variables in the real process/container environment before Harper starts.
1561
+ - For production credentials, prefer the encrypted secrets store over a committed \`.env\` file. Secrets are also delivered to components via \`process.env\`.
1562
+ `,logging:`---
1563
+ name: logging
1564
+ description: >-
1565
+ Best practices for logging in Harper, including console capture, the granular
1566
+ logger interface, and programmatic log retrieval.
1567
+ metadata:
1568
+ mode: generate
1569
+ sources:
1570
+ - reference/v5/logging/overview.md
1571
+ - reference/v5/logging/api.md
1572
+ sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
1573
+ inputHash: 46cd384598304e3b
1574
+ ---
1575
+
1576
+ # Harper Logging
1577
+
1578
+ Instructions for the agent to follow when implementing logging in Harper applications, including direct logger usage, tagged loggers, and console capture behavior.
1579
+
1580
+ ## When to Use
1581
+
1582
+ Apply this rule when writing any JavaScript component, plugin, or resource that needs to emit structured log entries, filter logs by component, or capture existing \`console.log\` output into Harper's log system. Use it whenever you need to understand log levels, log entry format, or the \`logger\` global API.
1583
+
1584
+ ## How It Works
1585
+
1586
+ 1. **Use the \`logger\` global directly** — \`logger\` is available in all JavaScript components without any imports. Call the method matching the desired severity level:
1587
+
1588
+ \`\`\`javascript
1589
+ logger.trace('detailed trace message');
1590
+ logger.debug('debug info', { someContext: 'value' });
1591
+ logger.info('informational message');
1592
+ logger.warn('potential issue');
1593
+ logger.error('error occurred', error);
1594
+ logger.fatal('fatal error');
1595
+ logger.notify('server is ready');
1596
+ \`\`\`
1597
+
1598
+ Only entries at or above the configured \`logging.level\` (or \`logging.external.level\`) are written to \`hdb.log\`.
1599
+
1600
+ 2. **Create a tagged logger with \`withTag(\`** — Call \`logger.withTag(tag)\` once per module or class to get a \`TaggedLogger\` scoped to that tag. This prefixes every log entry with the tag, making log output filterable by component.
1601
+
1602
+ \`\`\`javascript
1603
+ const log = logger.withTag('my-resource');
1604
+ \`\`\`
1605
+
1606
+ Because \`TaggedLogger\` methods for disabled levels are \`null\`, always use optional chaining (\`?.\`) when calling them:
1607
+
1608
+ \`\`\`javascript
1609
+ log.debug?.('Fetching record', { id });
1610
+ log.warn?.('Record not found', { id });
1611
+ log.error?.('Failed to update record', err);
1612
+ \`\`\`
1613
+
1614
+ \`TaggedLogger\` does not have a \`withTag()\` method.
1615
+
1616
+ 3. **Understand the interface contracts** — \`MainLogger\` always has all methods defined:
1617
+
1618
+ \`\`\`typescript
1619
+ interface MainLogger {
1620
+ trace(...messages: any[]): void;
1621
+ debug(...messages: any[]): void;
1622
+ info(...messages: any[]): void;
1623
+ warn(...messages: any[]): void;
1624
+ error(...messages: any[]): void;
1625
+ fatal(...messages: any[]): void;
1626
+ notify(...messages: any[]): void;
1627
+ withTag(tag: string): TaggedLogger;
1628
+ }
1629
+ \`\`\`
1630
+
1631
+ \`TaggedLogger\` methods may be \`null\`:
1632
+
1633
+ \`\`\`typescript
1634
+ interface TaggedLogger {
1635
+ trace: ((...messages: any[]) => void) | null;
1636
+ debug: ((...messages: any[]) => void) | null;
1637
+ info: ((...messages: any[]) => void) | null;
1638
+ warn: ((...messages: any[]) => void) | null;
1639
+ error: ((...messages: any[]) => void) | null;
1640
+ fatal: ((...messages: any[]) => void) | null;
1641
+ notify: ((...messages: any[]) => void) | null;
1642
+ }
1643
+ \`\`\`
1644
+
1645
+ 4. **Know the log levels** — From least to most severe:
1646
+
1647
+ | Level | Description |
1648
+ | -------- | -------------------------------------------------------------------- |
1649
+ | \`trace\` | Highly detailed internal execution tracing. |
1650
+ | \`debug\` | Diagnostic information useful during development. |
1651
+ | \`info\` | General operational events. |
1652
+ | \`warn\` | Potential issues that don't prevent normal operation. |
1653
+ | \`error\` | Errors that affect specific operations. |
1654
+ | \`fatal\` | Critical errors causing process termination. |
1655
+ | \`notify\` | Important operational milestones. Always logged regardless of level. |
1656
+
1657
+ The default log level is \`warn\`. Setting a level includes that level and all more-severe levels.
1658
+
1659
+ 5. **Enable console capture when porting existing code** — When \`logging.console: true\` is set, writes via \`console.log\`, \`console.warn\`, \`console.error\`, etc. are appended verbatim to \`hdb.log\`. Captured lines do **not** pass through \`logger\`'s level filter. Prefer \`logger\` directly in production code so that level filtering and tagging apply. Console capture is intended as a convenience for porting existing code and for debugging.
1660
+
1661
+ 6. **Know where logs are written** — All standard log output goes to \`<ROOTPATH>/log/hdb.log\` (default: \`~/hdb/log/hdb.log\`). To also log to \`stdout\`/\`stderr\`, set \`logging.stdStreams: true\`.
1662
+
1663
+ ## Examples
1664
+
1665
+ ### Basic logging in a resource
1666
+
1667
+ \`\`\`javascript
1668
+ export class MyResource extends Resource {
1669
+ async get(id) {
1670
+ logger.debug('Fetching record', { id });
1671
+ const record = await super.get(id);
1672
+ if (!record) {
1673
+ logger.warn('Record not found', { id });
1674
+ }
1675
+ return record;
1676
+ }
1677
+
1678
+ async put(record) {
1679
+ logger.info('Updating record', { id: record.id });
1680
+ try {
1681
+ return await super.put(record);
1682
+ } catch (err) {
1683
+ logger.error('Failed to update record', err);
1684
+ throw err;
1685
+ }
1686
+ }
1687
+ }
1688
+ \`\`\`
1689
+
1690
+ ### Tagged logging with \`withTag()\`
1691
+
1692
+ \`\`\`javascript
1693
+ const log = logger.withTag('my-resource');
1694
+
1695
+ export class MyResource extends Resource {
1696
+ async get(id) {
1697
+ log.debug?.('Fetching record', { id });
1698
+ const record = await super.get(id);
1699
+ if (!record) {
1700
+ log.warn?.('Record not found', { id });
1701
+ }
1702
+ return record;
1703
+ }
1704
+
1705
+ async put(record) {
1706
+ log.info?.('Updating record', { id: record.id });
1707
+ try {
1708
+ return await super.put(record);
1709
+ } catch (err) {
1710
+ log.error?.('Failed to update record', err);
1711
+ throw err;
1712
+ }
1713
+ }
1714
+ }
1715
+ \`\`\`
1716
+
1717
+ Tagged entries appear in \`hdb.log\` with the tag in the header:
1718
+
1719
+ \`\`\`
1720
+ 2023-03-09T14:25:05.269Z [info] [my-resource]: Updating record
1721
+ \`\`\`
1722
+
1723
+ ## Notes
1724
+
1725
+ - All log output is written to \`<ROOTPATH>/log/hdb.log\`. The \`logger\` global writes to this file at the configured \`logging.external\` level.
1726
+ - Log entry format for \`logger\`: \`<timestamp> [<level>] [<thread>/<id>]: <message>\`
1727
+ - Log entry format for \`TaggedLogger\`: \`<timestamp> [<level>] [<tag>]: <message>\`
1728
+ - \`console.log\` output is only forwarded to \`hdb.log\` when \`logging.console: true\` is explicitly set; it is not forwarded by default.
1729
+ - When logging to standard streams, run Harper in the foreground (\`harper\`, not \`harper start\`).
1730
+ - \`TaggedLogger\` is bound to the configured log level at creation time — always use \`?.\` on its methods.
1731
+ `,"programmatic-table-requests":"---\nname: programmatic-table-requests\ndescription: How to interact with Harper tables programmatically using the `tables` object.\nmetadata:\n mode: generate\n sources:\n - reference/v5/database/api.md#`tables`\n - reference/v5/resources/resource-api.md#Query Object\n - 'reference/v5/database/api.md#`transaction(context?, callback)`'\n - >-\n reference/v5/resources/resource-api.md#`update(target: RequestTarget | Id,\n updates?: object): Promise<Resource>`\n - >-\n reference/v5/resources/resource-api.md#`addTo(property: string, value:\n number)`\n - reference/v5/components/javascript-environment.md#Module Formats\n sourceCommit: 9e6ecf87dd25bb488ad44dc2876ca6439ccd682e\n inputHash: f5629f73cde74764\n---\n\n# Programmatic Table Requests\n\nInstructions for the agent to interact with Harper tables programmatically using the `tables` object, the Query API, and transactions.\n\n## When to Use\n\nApply this rule when writing server-side Harper component code that reads from or writes to tables directly — for example, in HTTP handlers, background jobs, timers, or SSR render functions. Use it whenever you need to construct queries with `conditions`, `sort`, `select`, `limit`, or `offset`, or when you need explicit transaction control via `transaction()`.\n\n## How It Works\n\n1. **Import `tables` (and other APIs) from `harper`**: Access every table defined in `schema.graphql` as a named property of `tables`. Each property is the table class implementing the Resource API.\n\n ```javascript\n import { tables, transaction } from 'harper';\n const { Product } = tables;\n // equivalent to: databases.data.Product\n ```\n\n For standalone components, run `npm link harper` so imports resolve to the live runtime.\n\n2. **Define your schema with `@table`**: Tables must be declared in `schema.graphql`. Mark attributes you intend to sort or filter on with `@indexed`.\n\n ```graphql\n type Product @table {\n id: Long @primaryKey\n name: String\n price: Float\n }\n ```\n\n3. **Query records with `search(`**: Pass a Query object to `search(`. Iterate results with `for await`.\n\n ```javascript\n const query = {\n conditions: [{ attribute: 'price', comparator: 'less_than', value: 8.0 }],\n };\n for await (const record of Product.search(query)) {\n // process record\n }\n ```\n\n4. **Build `conditions`**: Each condition object supports these properties:\n\n | Property | Description |\n | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |\n | `attribute` | Property name, or array for chained/joined properties (e.g. `['brand', 'name']`) |\n | `value` | The value to match |\n | `comparator` | `equals` (default), `greater_than`, `greater_than_equal`, `less_than`, `less_than_equal`, `starts_with`, `contains`, `ends_with`, `between`, `not_equal` |\n | `conditions` | Nested conditions array |\n | `operator` | `and` (default) or `or` for the nested `conditions` |\n\n5. **Control result shape with `select`**: Pass an array of property names, a single string, or nested objects for relationships.\n\n ```javascript\n // Scalar fields only\n Product.search({ select: ['name', 'price'] });\n\n // Partial related record\n Book.get({ id: 42, select: ['id', 'title', { name: 'author', select: ['name'] }] });\n ```\n\n Special `select` values: `$id`, `$updatedtime`, `$distance`.\n\n6. **Paginate with `limit` and `offset`**:\n\n ```javascript\n Product.search({ conditions: [...], limit: 20, offset: 40 });\n ```\n\n7. **Sort with `sort`**: The `sort` object accepts `attribute`, `descending` (default `false`), and `next` for tie-breaking. Harper uses an index for sort order — the sort `attribute` must be `@indexed`, **or** at least one entry in `conditions` must be present.\n\n Sorting by a non-indexed attribute with zero conditions raises:\n\n > `HdbError: <attribute> is not indexed and not combined with any other conditions`\n\n Note: `@primaryKey` alone is treated as not indexed for sort purposes. To scan the whole table in primary-key order, add an open-ended condition:\n\n ```javascript\n Product.search({\n conditions: [{ attribute: 'id', comparator: 'greater_than', value: '' }],\n sort: { attribute: 'id' },\n });\n ```\n\n Alternatively, pass `allowFullScan: true` to permit an unconditional ordered scan, or omit `sort` entirely to iterate without an index requirement.\n\n8. **Debug query planning with `explain` and `enforceExecutionOrder`**:\n - `explain: true` — returns conditions reordered as Harper will execute them.\n - `enforceExecutionOrder: true` — forces conditions to execute in the order supplied, disabling automatic re-ordering.\n\n9. **Use `addTo` for concurrent-safe numeric updates**: `addTo` uses CRDT incrementation, safe across threads and nodes.\n\n ```javascript\n const record = await Product.update(32);\n record.addTo('quantity', -1);\n ```\n\n10. **Wrap background work in `transaction()`**: HTTP handlers get a transaction automatically. Use `transaction()` explicitly for timers, background jobs, or any code outside a request context. Always `await` the call and `catch` errors.\n\n ```javascript\n await transaction(async (txn) => {\n for (let item of data) {\n await MyTable.put(item, txn);\n }\n });\n ```\n\n The `txn` object exposes:\n\n | Member | Description |\n | --------------------- | ------------------------------------------------------ |\n | `commit()` | Commits the current transaction |\n | `abort()` | Aborts and resets the transaction |\n | `resetReadSnapshot()` | Resets the read snapshot to the latest committed state |\n | `timestamp` | Timestamp associated with the current transaction |\n\n **Atomicity**: all tables in the same database share one transactional context — writes across multiple tables commit atomically. Tables in different databases each get their own transaction with no cross-database atomicity guarantee.\n\n If `transaction()` is called with a context that already has an active transaction, it reuses that transaction — safe to call defensively.\n\n11. **Keep `harper` external when bundling for SSR**: In `vite.config`, mark `harper` as external so it resolves to the runtime rather than being bundled.\n\n ```javascript\n // vite.config\n ssr: {\n external: ['harper'];\n }\n ```\n\n## Examples\n\n### Full CRUD sequence\n\n```javascript\nimport { tables } from 'harper';\nconst { Product } = tables;\n\n// Create\nconst created = await Product.create({ name: 'Shirt', price: 9.5 });\n\n// Patch\nawait Product.patch(created.id, { price: Math.round(created.price * 0.8 * 100) / 100 });\n\n// Retrieve by primary key\nconst record = await Product.get(created.id);\n\n// Query with conditions\nconst query = {\n conditions: [{ attribute: 'price', comparator: 'less_than', value: 8.0 }],\n};\nfor await (const record of Product.search(query)) {\n // process record\n}\n```\n\n### Nested conditions with `or`\n\n```javascript\nProduct.search({\n conditions: [\n { attribute: 'price', comparator: 'less_than', value: 100 },\n {\n operator: 'or',\n conditions: [\n { attribute: 'rating', comparator: 'greater_than', value: 4 },\n { attribute: 'featured', value: true },\n ],\n },\n ],\n});\n```\n\n### Chained attribute reference (join/relationship)\n\n```javascript\nProduct.search({ conditions: [{ attribute: ['brand', 'name'], value: 'Harper' }] });\n```\n\n### Background job with `transaction()`\n\n```javascript\nimport { isMainThread } from 'node:worker_threads';\nimport { tables, transaction } from 'harper';\nconst { MyTable } = tables;\n\nif (isMainThread) {\n let running = false;\n setInterval(async () => {\n if (running) return;\n running = true;\n try {\n let data = await (await fetch('https://example.com/data')).json();\n await transaction(async (txn) => {\n for (let item of data) {\n await MyTable.put(item, txn);\n }\n });\n } catch (error) {\n logger.error('hourly import failed', error);\n } finally {\n running = false;\n }\n }, 3600000);\n}\n```\n\n### SSR render with `tables`\n\n```typescript\nimport { tables } from 'harper';\n\nexport async function render(url: string): Promise<string> {\n const product = await tables.Product.get(idFromUrl(url));\n return renderToString(/* <App product={product} /> */);\n}\n```\n\n## Notes\n\n- `tables` and `databases` do **not** automatically apply role permissions — calls run in a trusted server-side context. Apply your own authorization controls before exposing results.\n- Destructive operations (`update`, `patch`, `delete`) act on live data and are not easily reversible. Scope them with specific `conditions` and validate the affected set before writing.\n- `tables` is the same live, process-wide object whether accessed as a global or via `import { tables } from 'harper'`. A record written through one component is immediately visible to every other.\n- Run `npm link harper` for components in their own directory to ensure typings match the running installation.\n","querying-rest-apis":"---\nname: querying-rest-apis\ndescription: 'How to use query parameters to filter, sort, and paginate Harper REST APIs.'\nmetadata:\n mode: generate\n sources:\n - reference/v5/rest/querying.md\n sourceCommit: 2cfb81318f17e0aca2d600109e6ad813d2d0443e\n inputHash: 25b9daabc0a28fd5\n---\n\n# Querying REST APIs\n\nInstructions for the agent to filter, sort, select, and paginate records through Harper's URL-based REST query language.\n\n## When to Use\n\nApply this rule whenever you need to construct or handle GET requests against Harper collection endpoints that require filtering by attribute value, comparison operators, sorting, field selection, or paginated results. This rule also covers type coercion syntax and relationship joins via dot notation. See [automatic-apis.md](automatic-apis.md) for how REST endpoints are generated from schemas.\n\n## How It Works\n\n1. **Filter by attribute**: Add query parameters matching attribute names and values. The queried attribute must be indexed.\n\n ```\n GET /Product/?category=software\n GET /Product/?category=software&inStock=true\n ```\n\n To filter for null values:\n\n ```\n GET /Product/?discount=null\n ```\n\n2. **Apply comparison operators (FIQL syntax)**: Use FIQL operators in the query string for range and pattern matching.\n\n | Operator | Meaning |\n | -------------------- | -------------------------------------- |\n | `==` | Equal |\n | `=lt=` | Less than |\n | `=le=` | Less than or equal |\n | `=gt=` | Greater than |\n | `=ge=` | Greater than or equal |\n | `=ne=`, `!=` | Not equal |\n | `=ct=` | Contains (strings) |\n | `=sw=`, `==<value>*` | Starts with (strings) |\n | `=ew=` | Ends with (strings) |\n | `=`, `===` | Strict equality (no type conversion) |\n | `!==` | Strict inequality (no type conversion) |\n\n ```\n GET /Product/?price=gt=100\n GET /Product/?price=le=20\n GET /Product/?name==Keyboard*\n GET /Product/?category=software&price=gt=100&price=lt=200\n ```\n\n For date fields, URL-encode colons as `%3A`:\n\n ```\n GET /Product/?listDate=gt=2017-03-08T09%3A30%3A00.000Z\n ```\n\n3. **Chain conditions for range queries**: Omit the attribute name on the second condition to apply it to the same attribute. Only `gt`/`ge` combined with `lt`/`le` is supported.\n\n ```\n GET /Product/?price=gt=100&lt=200\n ```\n\n4. **Apply type conversion**: For FIQL comparators (`==`, `!=`, `=gt=`, etc.), Harper converts values automatically. Use explicit type prefixes to force a specific type.\n\n | Syntax | Behavior |\n | ----------------------------------------- | ------------------------------------------- |\n | `name==null` | Converts to `null` |\n | `name==123` | Converts to number if attribute is untyped |\n | `name==true` | Converts to boolean if attribute is untyped |\n | `name==number:123` | Explicit number conversion |\n | `name==boolean:true` | Explicit boolean conversion |\n | `name==string:some%20text` | Keep as string with URL decode |\n | `name==date:2024-01-05T20%3A07%3A27.955Z` | Explicit Date conversion |\n\n For strict operators (`=`, `===`, `!==`), no automatic type conversion is applied.\n\n5. **Combine conditions with OR logic**: Use `|` instead of `&`.\n\n ```\n GET /Product/?rating=5|featured=true\n ```\n\n6. **Group conditions**: Use parentheses or square brackets to control evaluation order. Prefer square brackets when building queries from user input, since standard URI encoding safely encodes `[` and `]`.\n\n ```\n GET /Product/?rating=5|(price=gt=100&price=lt=200)\n GET /Product/?rating=5&[tag=fast|tag=scalable|tag=efficient]\n ```\n\n Build grouped queries in JavaScript:\n\n ```javascript\n let url = `/Product/?rating=5&[${tags.map(encodeURIComponent).join('|')}]`;\n ```\n\n7. **Select specific properties with `select()`**: Append `select()` as a query function separated by `&`.\n\n | Syntax | Returns |\n | -------------------------------------- | ------------------------------------------- |\n | `?select(property)` | Values of a single property directly |\n | `?select(property1,property2)` | Objects with only the specified properties |\n | `?select([property1,property2])` | Arrays of property values |\n | `?select(property1,)` | Objects with a single specified property |\n | `?select(property{subProp1,subProp2})` | Nested objects with specific sub-properties |\n\n ```\n GET /Product/?category=software&select(name)\n GET /Product/?brand.name=Microsoft&select(name,brand{name})\n ```\n\n8. **Limit results with `limit(end)` or `limit(start,end)`**: Append as a query function.\n\n ```\n GET /Product/?rating=gt=3&inStock=true&select(rating,name)&limit(20)\n GET /Product/?rating=gt=3&limit(10,30)\n ```\n\n9. **Sort results with `sort(property)` or `sort(+property,-property,...)`**: Prefix `+` or no prefix = ascending; `-` = descending. List multiple properties to break ties in order.\n\n ```\n GET /Product/?rating=gt=3&sort(+name)\n GET /Product/?sort(+rating,-price)\n ```\n\n10. **Paginate with a total count**: Use `limit(start,end)` for paging and send a `Prefer` request header to receive a total match count.\n\n | `Prefer` value | Meaning |\n | ----------------- | ---------------------------------------- |\n | `count=exact` | Exact count of matching records (opt-in) |\n | `count=estimated` | Fast approximate count |\n\n ```\n GET /Product/?category=software&limit(0,25)\n Prefer: count=exact\n ```\n\n The server responds with:\n\n | Header | Example | Description |\n | -------------------- | ----------------- | ------------------------------------------------------ |\n | `Content-Range` | `items 0-24/1234` | 0-based inclusive range out of total matching records |\n | `Range-Unit` | `items` | Unit used by `Content-Range` |\n | `Preference-Applied` | `count=exact` | Count mode the server applied (`exact` or `estimated`) |\n\n The response status is always `200`. When the total cannot be produced, it is reported as `*` (e.g., `Content-Range: items 0-24/*`).\n\n Enable exact counts in the application's REST configuration:\n\n ```yaml\n rest:\n exactCount: true\n ```\n\n Without this, a `count=exact` request is served as an estimate.\n\n11. **Query across relationships using dot syntax**: Define relationships in the schema with `@relationship`, then filter and select across them.\n\n ```\n GET /Product/?brand.name=Microsoft\n GET /Brand/?products.name=Keyboard\n GET /Product/?brand.name=Microsoft&select(name,brand{name})\n ```\n\n Filtering on a related attribute produces INNER JOIN behavior. Selecting a relationship without filtering produces LEFT JOIN behavior — the property is omitted if the foreign key is null or references a non-existent record.\n\n12. **Access a specific property by URL**: Append the property name with dot syntax to the record ID.\n\n ```\n GET /MyTable/123.propertyName\n ```\n\n This only works for declared schema properties. The suffixes `.json`, `.cbor`, `.msgpack`, and `.csv` are reserved as content-type selectors.\n\n## Examples\n\n**Range filter with select and sort:**\n\n```\nGET /Product/?category=software&price=gt=100&price=lt=200&select(name,price)&sort(+price)\n```\n\n**Paginated request with exact count:**\n\n```\nGET /Product/?category=software&limit(0,25)\nPrefer: count=exact\n```\n\nResponse headers:\n\n```\nHTTP/1.1 200 OK\nContent-Range: items 0-24/1234\nRange-Unit: items\nPreference-Applied: count=exact\n```\n\n**Relationship schema and join query:**\n\n```graphql\ntype Product @table @export {\n id: Long @primaryKey\n name: String\n brandId: Long @indexed\n brand: Brand @relationship(from: \"brandId\")\n}\ntype Brand @table @export {\n id: Long @primaryKey\n name: String\n products: [Product] @relationship(to: \"brandId\")\n}\n```\n\n```\nGET /Product/?brand.name=Microsoft&select(name,brand{name,id})\n```\n\n**Many-to-many relationship:**\n\n```graphql\ntype Product @table @export {\n id: Long @primaryKey\n name: String\n resellerIds: [Long] @indexed\n resellers: [Reseller] @relationship(from: \"resellerIds\")\n}\n```\n\n```\nGET /Product/?resellers.name=Cool Shop&select(id,name,resellers{name,id})\n```\n\n**OR grouping with user-supplied tags:**\n\n```javascript\nlet url = `/Product/?rating=5&[${tags.map(encodeURIComponent).join('|')}]`;\n```\n\n## Notes\n\n- The queried attribute must be indexed for basic attribute filtering to execute. For multi-attribute queries, only one attribute needs to be indexed.\n- Null indexing requires indexes created after the feature was introduced. Rebuild existing indexes (remove and re-add) to support `name==null` queries.\n- `limit()` must be a non-negative integer no larger than **10,000**, and the requested window (offset + limit) no larger than **1,000,000**, for count headers to be returned.\n- A `HEAD` request with a `Prefer: count=exact` header returns count headers with no body — it saves bandwidth but still scans the matched set.\n- When CORS is enabled, `Content-Range`, `Range-Unit`, and `Preference-Applied` are added to `Access-Control-Expose-Headers` automatically.\n- Operators `!=` and `=ct=` (contains) do not produce cardinality estimates; the total is reported as `*` when these are used with `count=exact`.\n","real-time-apps":`---
1732
+ name: real-time-apps
1733
+ description: How to build real-time features in Harper using WebSockets and Pub/Sub.
1734
+ metadata:
1735
+ mode: generate
1736
+ sources:
1737
+ - reference/v5/rest/websockets.md
1738
+ sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
1739
+ inputHash: a8afd4d3a52f77ba
1740
+ ---
1741
+
1742
+ # Real-Time Apps with WebSockets and Pub/Sub
1743
+
1744
+ Instructions for the agent to follow when building real-time features in Harper using WebSockets and Pub/Sub.
1745
+
1746
+ ## When to Use
1747
+
1748
+ Apply this rule when implementing any feature that requires real-time bidirectional communication, live data streaming, or push-based updates in a Harper application. This includes chat, live dashboards, sensor feeds, and any scenario where clients must receive resource changes as they happen.
1749
+
1750
+ ## How It Works
1751
+
1752
+ 1. **Enable WebSocket support**: WebSocket support is enabled automatically when the \`rest\` plugin is enabled. To explicitly disable it, set the following in your config:
1753
+
1754
+ \`\`\`yaml
1755
+ rest:
1756
+ webSocket: false
1757
+ \`\`\`
1758
+
1759
+ 2. **Connect a client to a resource**: A WebSocket connection to a resource URL automatically subscribes to that resource. When the record changes or a message is published to it, the connection receives the update.
1760
+
1761
+ \`\`\`javascript
1762
+ let ws = new WebSocket('wss://server/my-resource/341');
1763
+ ws.onmessage = (event) => {
1764
+ let data = JSON.parse(event.data);
1765
+ };
1766
+ \`\`\`
1767
+
1768
+ \`new WebSocket('wss://server/my-resource/341')\` accesses the resource defined for \`my-resource\` with record id \`341\` and subscribes to it.
1769
+
1770
+ 3. **Implement a custom \`connect()\` handler**: Override the \`connect(incomingMessages)\` method on a resource class to control WebSocket behavior. The method must return an async iterable (or generator) that produces messages to send to the client. See [automatic-apis.md](automatic-apis.md) for more on defining resource classes.
1771
+
1772
+ 4. **Use the default \`connect()\` for event-style access**: Call \`super.connect()\` to get a streaming iterable that provides:
1773
+ - A \`send(message)\` method for pushing outgoing messages
1774
+ - A \`close\` event for cleanup on disconnect
1775
+
1776
+ 5. **Handle message ordering in distributed environments**: Harper delivers messages to local subscribers immediately without inter-node coordination delay.
1777
+
1778
+ | Message Type | Behavior |
1779
+ | -------------------------------------------------------- | ----------------------------------------------------------------------- |
1780
+ | Non-retained (no \`retain\` flag) | Every message delivered in order received; suitable for chat |
1781
+ | Retained (published with \`retain\`, or PUT/updated in DB) | Only the latest-timestamp message is kept; suitable for sensor readings |
1782
+
1783
+ 6. **Use MQTT over WebSockets** when needed by setting the sub-protocol header:
1784
+ \`\`\`
1785
+ Sec-WebSocket-Protocol: mqtt
1786
+ \`\`\`
1787
+
1788
+ ## Examples
1789
+
1790
+ **Simple echo server** — override \`connect(incomingMessages)\` to yield each incoming message back to the client:
1791
+
1792
+ \`\`\`javascript
1793
+ export class Echo extends Resource {
1794
+ async *connect(incomingMessages) {
1795
+ for await (let message of incomingMessages) {
1796
+ yield message; // echo each message back
1797
+ }
1798
+ }
1799
+ }
1800
+ \`\`\`
1801
+
1802
+ **Custom connect with timer and event-style access** — use \`super.connect()\` to get the outgoing stream, push periodic messages, echo incoming messages, and clean up on disconnect:
1803
+
1804
+ \`\`\`javascript
1805
+ export class Example extends Resource {
1806
+ connect(incomingMessages) {
1807
+ let outgoingMessages = super.connect();
1808
+
1809
+ let timer = setInterval(() => {
1810
+ outgoingMessages.send({ greeting: 'hi again!' });
1811
+ }, 1000);
1812
+
1813
+ incomingMessages.on('data', (message) => {
1814
+ outgoingMessages.send(message); // echo incoming messages
1815
+ });
1816
+
1817
+ outgoingMessages.on('close', () => {
1818
+ clearInterval(timer);
1819
+ });
1820
+
1821
+ return outgoingMessages;
1822
+ }
1823
+ }
1824
+ \`\`\`
1825
+
1826
+ ## Notes
1827
+
1828
+ - WebSocket connections target a resource URL path. By default, connecting to a resource subscribes to changes for that resource.
1829
+ - The \`connect(incomingMessages)\` method **must** return an async iterable or generator; returning a plain value will not work.
1830
+ - \`super.connect()\` returns a streaming iterable with \`send(message)\` and a \`close\` event — use this when you need to push messages outside of the incoming message loop.
1831
+ - For one-way real-time streaming without bidirectional communication, consider Server-Sent Events instead.
1832
+ - For full pub/sub capabilities, Harper also supports MQTT; set \`Sec-WebSocket-Protocol: mqtt\` to use MQTT over WebSockets.
1833
+ `,"schema-design-tooling":'---\nname: schema-design-tooling\ndescription: >-\n Best practices for Harper schema design, including core directives and GraphQL\n tooling configuration.\nmetadata:\n mode: generate\n sources:\n - reference/v5/database/schema.md#Overview\n - reference/v5/database/schema.md#Type Directives\n - reference/v5/database/schema.md#Field Directives\n sourceCommit: 2cfb81318f17e0aca2d600109e6ad813d2d0443e\n inputHash: a644f4ea2f289f77\n---\n\n# Schema Design Tooling\n\nInstructions for the agent to follow when designing Harper schemas, applying core directives, and configuring GraphQL tooling.\n\n## When to Use\n\nApply this rule when creating or modifying Harper schema files (`.graphql`), configuring `graphqlSchema` in `config.yaml`, or deciding which directives to apply to tables and fields. Use it whenever a task involves defining tables, primary keys, indexes, or export behavior.\n\n## How It Works\n\n1. **Register the schema file** in the component\'s `config.yaml` using the `graphqlSchema` plugin key:\n\n ```yaml\n graphqlSchema:\n files: \'schema.graphql\'\n ```\n\n Both plugins and applications can specify schemas.\n\n2. **Mark types as tables** using `@table`. The type name becomes the table name by default:\n\n ```graphql\n type Dog @table {\n id: Long @primaryKey\n name: String\n breed: String\n age: Int\n }\n ```\n\n3. **Designate a primary key** on every table using `@primaryKey`. Primary keys must be unique; duplicate inserts are rejected. If no primary key is provided on insert, Harper auto-generates one:\n - `String` or `ID` → UUID string\n - `Int`, `Long`, or `Any` → auto-incrementing integer\n\n Use `Long` or `Any` for auto-generated numeric keys; `Int` is 32-bit and may be insufficient for large tables.\n\n4. **Index fields for querying** using `@indexed`. Required for filtering by an attribute in REST queries, SQL, or NoSQL operations:\n\n ```graphql\n type Breed @table {\n id: Long @primaryKey\n name: String @indexed\n }\n ```\n\n If the field value is an array, each element is individually indexed. Null values are indexed by default.\n\n5. **Expose tables as REST/MQTT endpoints** using `@export`. The optional `name` parameter sets the URL path segment; without it, the type name is used:\n\n ```graphql\n type MyTable @table @export(name: "my-table") {\n id: Long @primaryKey\n }\n ```\n\n `@export` alone does not serve HTTP traffic — REST must also be enabled for the application via `rest: true` in `config.yaml` or Harper\'s built-in default. `@export` is a routing directive, not access control; omitting it returns 404 but does not protect the data.\n\n6. **Configure `@table` arguments** to control database placement, expiration, replication, and caching behavior. Key arguments:\n\n | Argument | Type | Default | Description |\n | -------------------- | --------- | ----------------------------- | -------------------------------------------------------------- |\n | `table` | `String` | type name | Override the table name |\n | `database` | `String` | `"data"` | Database to place the table in |\n | `expiration` | `Int` | — | Seconds until a record goes stale |\n | `eviction` | `Int` | `0` | Additional seconds after `expiration` before physical removal |\n | `scanInterval` | `Int` | `(expiration + eviction) / 4` | Seconds between eviction scans |\n | `replicate` | `Boolean` | `true` | Enable replication of this table |\n | `cacheControl` | `String` | — | `Cache-Control` header on anonymous GET/HEAD 200/304 responses |\n | `randomAccessFields` | `Boolean` | `storage.randomAccessFields` | Pin this table\'s record encoding |\n\n7. **Seal a type** with `@sealed` to prevent records from including properties beyond those declared:\n\n ```graphql\n type StrictRecord @table @sealed {\n id: Long @primaryKey\n name: String\n }\n ```\n\n8. **Use timestamp directives** for automatic record lifecycle tracking:\n - `@createdTime` — assigns Unix epoch milliseconds on record creation\n - `@updatedTime` — assigns Unix epoch milliseconds on each update\n - `@expiresAt` — marks a field as the record\'s absolute expiration time (Unix epoch milliseconds); authoritative over the table-level `expiration` default\n\n9. **Use `@embed`** to automatically compute an embedding vector for an attribute whenever the source field is written (requires `source` and `model` arguments; field type must be `[Float]`):\n\n ```graphql\n type Document @table {\n id: Long @primaryKey\n text: String\n embedding: [Float] @embed(source: "text", model: "default")\n }\n ```\n\n10. **Use `@hidden`** on types or fields to suppress them from MCP tool descriptors and the OpenAPI document. This is a metadata-visibility directive only — it does not restrict data access. Use `attribute_permissions` on roles for field-level access control.\n\n## Examples\n\n**Minimal two-table schema:**\n\n```graphql\ntype Dog @table {\n id: Long @primaryKey\n name: String\n breed: String\n age: Int\n}\n\ntype Breed @table {\n id: Long @primaryKey\n name: String @indexed\n}\n```\n\n**Table with expiration, eviction, and scan tuning:**\n\n```graphql\n# Expire after 5 minutes, evict after 1 hour, scan every 10 minutes\ntype WeatherCache @table(expiration: 300, eviction: 3300, scanInterval: 600) {\n id: ID @primaryKey\n temperature: Float\n}\n```\n\n**Exported table with public cache control:**\n\n```graphql\ntype Product @table(cacheControl: "public, max-age=60") @export {\n id: Long @primaryKey\n name: String\n price: Float\n}\n```\n\n**Table with multiple `@table` arguments combined:**\n\n```graphql\ntype Event @table(database: "analytics", expiration: 86400) {\n id: Long @primaryKey\n name: String @indexed\n}\n```\n\n**Session table with per-record expiration:**\n\n```graphql\ntype Session @table {\n id: ID @primaryKey\n token: String\n expiresAt: Long @expiresAt\n}\n```\n\n**Table with timestamp tracking and indexed fields:**\n\n```graphql\ntype Order @table(database: "commerce") @export {\n id: Long @primaryKey\n userId: String @indexed\n status: String @indexed\n createdAt: Long @createdTime\n updatedAt: Long @updatedTime\n}\n```\n\n**`config.yaml` schema registration:**\n\n```yaml\ngraphqlSchema:\n files: \'schema.graphql\'\n```\n\n## Notes\n\n- Use unique `database` names in plugins and applications to avoid table naming collisions, since all tables default to the `"data"` database.\n- Disabling replication (`replicate: false`) and re-enabling it later will not catch up on writes made while replication was disabled.\n- `cacheControl` emits the header only on anonymous (unauthenticated) GET/HEAD `200`/`304` responses. Authenticated responses receive `Cache-Control: private, no-cache` regardless of the declaration. The header is never emitted on `401` responses.\n- `@expiresAt` requires an absolute Unix epoch millisecond timestamp, not a duration. Negative values are ignored and fall back to the table default. A full-record `put` that omits the field clears it; a `patch` preserves it.\n- Eviction removes non-indexed record data but does not remove a record from its secondary indexes; indexes remain functional for evicted records, with full records fetched on demand.\n- `scanInterval` is clock-aligned to the server\'s local timezone, not startup-aligned — the server\'s startup time does not affect when eviction runs.\n- `randomAccessFields` pins the table\'s encoding at creation time. Editing the argument later does not repin an existing table. Omit it to follow the global `storage.randomAccessFields` setting.\n',"serving-web-content":"---\nname: serving-web-content\ndescription: How to serve static files and integrated Vite/React applications in Harper.\nmetadata:\n mode: synthesized\n---\n\n# Serving Web Content\n\nInstructions for the agent to follow when serving web content from Harper.\n\n## When to Use\n\nUse this skill when you need to serve a frontend (HTML, CSS, JS, or a React/Vue app) directly from your Harper instance — either plain static files or an integrated Vite app with hot module replacement (HMR) in development and a real production build when deployed.\n\n## How It Works\n\nThere are two building blocks. Harper's built-in `static` plugin **serves** files; the `@harperfast/vite` plugin **builds** (and, for SSR, **renders**) a Vite app. For a Vite app they work **together** — the plugin builds into a directory and `static` serves that same directory.\n\n### Option A: Static plugin only (simple, pre-built assets)\n\nFor a plain static site or already-built assets, use `static` on its own:\n\n```yaml\nstatic:\n files: 'web/*'\n```\n\n- Place files in a `web/` folder in the project root; they are served from the root URL (e.g. `http://localhost:9926/index.html`).\n- Static files are matched first; if none matches, Harper falls through to your resource and table APIs.\n\n### Option B: Vite plugin + static plugin (integrated Vite app)\n\n> **Renamed in v1:** the plugin was previously `@harperfast/vite-plugin`. From `1.0.0` on it is **`@harperfast/vite`** (same key and `package`). It now pairs with the `static` plugin instead of building into `web/` itself.\n\n`@harperfast/vite` **builds** your app — in `harper dev` it runs Vite in middleware mode with HMR; in `harper run` it runs `vite build` and rebuilds when watched files change (and renders HTML for SSR). The `static` plugin **serves** the built output. Point both at the same directory (`output`, default `dist`) — that shared directory is the only contract between them.\n\n**SPA `config.yaml`** — list the plugin first so its dev server wins in `harper dev`; `notFound` + `fallthrough: false` makes client-side routing work:\n\n```yaml\n'@harperfast/vite':\n package: '@harperfast/vite'\n files: 'src/**/*'\n output: 'dist'\n\nstatic:\n files: 'dist/**'\n notFound:\n file: 'index.html'\n statusCode: 200\n fallthrough: false\n```\n\n**SSR `config.yaml`** — add an `ssr` entry so the plugin renders `index.html`, and set `index: false` on `static` so it serves assets only:\n\n```yaml\n'@harperfast/vite':\n package: '@harperfast/vite'\n files: 'src/**/*'\n output: 'dist'\n ssr: 'src/entry-server.tsx'\n\nstatic:\n files: 'dist/**'\n index: false\n```\n\n- Install dependencies: `npm install --save-dev vite @harperfast/vite @vitejs/plugin-react` (swap in your framework's Vite plugin, e.g. `@vitejs/plugin-vue`).\n- Then `harper dev .` runs the app with HMR and `harper run .` runs the production build. Vite does _not_ need to be executed separately.\n\n## Reading Harper Data During SSR\n\nThe render entry (`src/entry-server.tsx`) runs **inside Harper**, so it can read straight from the database and render the data into the HTML — no client-side fetch/XHR. `tables` is the same live, process-wide registry available everywhere (see [Programmatic Table Requests](programmatic-table-requests.md)); import it and query a table in an async `render`:\n\n```tsx\nimport { tables } from 'harper';\n\nexport async function render(url: string): Promise<string> {\n const product = await tables.Product.get(idFromUrl(url));\n return renderToString(\n <StrictMode>\n <App product={product} />\n </StrictMode>,\n );\n}\n```\n\nKeep `harper` external in `vite.config.ts` so this import resolves to Harper's running runtime instead of being bundled. `node_modules/harper` is symlinked to the running install, and symlinked deps aren't reliably auto-externalized for SSR:\n\n```typescript\nexport default defineConfig({\n ssr: { external: ['harper'] },\n // ...plugins, resolve, build\n});\n```\n\nTo hydrate on the client without re-fetching, embed the rendered data in the HTML (e.g. an inline `<script type=\"application/json\">`) and read it back before hydration — so the page needs no XHR at all.\n\n## Deploying to Production\n\nBecause `@harperfast/vite` builds on the node and `static` serves the output, deploy the component as-is — no manual build-and-move step is needed:\n\n```json\n{\n \"scripts\": {\n \"dev\": \"harper dev .\",\n \"start\": \"harper run .\",\n \"deploy\": \"harper deploy_component . restart=true replicated=true\"\n }\n}\n```\n\nOn deploy the plugin runs `vite build` at startup (and rebuilds when `files` change) while `static` serves the result. If you prefer to build in CI, commit the build output, point `static` at it, and omit `files` so the plugin stays idle while `static` serves the prebuilt assets. Either way, `npm create harper@latest` scaffolds a working setup for you.\n","typescript-type-stripping":`---
1834
+ name: typescript-type-stripping
1835
+ description: How to run TypeScript files directly in Harper without a build step.
1836
+ metadata:
1837
+ mode: generate
1838
+ sources:
1839
+ - reference/v5/components/javascript-environment.md#TypeScript Support
1840
+ sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
1841
+ inputHash: 4e6bd8b610edd595
1842
+ ---
1843
+
1844
+ # TypeScript Type Stripping in Harper
1845
+
1846
+ Instructions for the agent to run \`.ts\` files directly in Harper without a build step using Node.js's built-in type stripping.
1847
+
1848
+ ## When to Use
1849
+
1850
+ Apply this rule when writing Harper resource files in TypeScript. Use it any time you need to reference \`.ts\` source files from \`config.yaml\` or import between local TypeScript modules in a Harper project.
1851
+
1852
+ ## How It Works
1853
+
1854
+ 1. **Ensure Node.js version**: Require Node.js 22.6 or later. Type stripping is unavailable on earlier versions.
1855
+
1856
+ 2. **Point \`jsResource\` at \`.ts\` files**: The \`jsResource\` plugin loads both \`.js\` and \`.ts\` files. Set its \`files\` glob in \`config.yaml\` to target your \`.ts\` source files:
1857
+
1858
+ \`\`\`yaml
1859
+ jsResource:
1860
+ files: 'resources/*.ts'
1861
+ \`\`\`
1862
+
1863
+ 3. **Use explicit \`.ts\` extensions in local imports**: Node's loader does not resolve \`'./helper'\` to \`'./helper.ts'\`, so always include the full extension:
1864
+
1865
+ \`\`\`typescript
1866
+ import { helper } from './helper.ts';
1867
+ \`\`\`
1868
+
1869
+ 4. **Stay within type-stripping limits**: Only type annotations and declarations are removed. Do not use enums with runtime values, namespaces with runtime semantics, or any other features that require code transformation beyond type stripping.
1870
+
1871
+ ## Examples
1872
+
1873
+ A complete Harper resource written in TypeScript, using imports from the \`harper\` package:
1874
+
1875
+ \`\`\`typescript
1876
+ import { type RequestTargetOrId, Resource, tables } from 'harper';
1877
+
1878
+ export class MyResource extends Resource {
1879
+ async get(target?: RequestTargetOrId): Promise<{ message: string }> {
1880
+ return { message: 'Hello from TS' };
1881
+ }
1882
+ }
1883
+ \`\`\`
1884
+
1885
+ Paired \`config.yaml\` entry loading the file via \`jsResource\`:
1886
+
1887
+ \`\`\`yaml
1888
+ jsResource:
1889
+ files: 'resources/*.ts'
1890
+ \`\`\`
1891
+
1892
+ ## Notes
1893
+
1894
+ - No build step or transpiler is required — Harper runs \`.ts\` files directly.
1895
+ - Type imports (e.g., \`import { type RequestTargetOrId }\`) from the \`harper\` package work as usual.
1896
+ - Unsupported TypeScript features include: enums with runtime values, namespaces with runtime semantics, and anything requiring code transformation beyond simple type stripping.
1897
+ `,"using-blob-datatype":"---\nname: using-blob-datatype\ndescription: How to use the Blob data type for efficient binary storage in Harper.\nmetadata:\n mode: generate\n sources:\n - reference/v5/database/schema.md#Blob Type\n - reference/v5/database/api.md#Streaming\n - reference/v5/database/api.md#`BlobOptions`\n - reference/v5/database/api.md#Blob Coercion\n sourceCommit: 2cfb81318f17e0aca2d600109e6ad813d2d0443e\n inputHash: 753b85cbdcef9188\n---\n\n# Using the Blob Data Type\n\nInstructions for the agent to follow when storing and retrieving large binary content using the `Blob` data type in Harper.\n\n## When to Use\n\nApply this rule when a schema field needs to store large binary content such as images, video, audio, or large HTML — typically content larger than 20KB. Use `Blob` instead of `Bytes` when streaming support and out-of-record storage are required. See [handling-binary-data](handling-binary-data.md) for broader binary data guidance.\n\n## How It Works\n\n1. **Declare a `Blob` field in your schema**: Add a field typed as `Blob` to your table definition.\n\n ```graphql\n type MyTable @table {\n id: Any! @primaryKey\n data: Blob\n }\n ```\n\n2. **Create and store a blob with `createBlob()`**: Pass a buffer, string, or stream to `createBlob()`, then `put` the record.\n\n ```javascript\n let blob = createBlob(largeBuffer);\n await MyTable.put({ id: 'my-record', data: blob });\n ```\n\n3. **Retrieve blob data using standard Web API methods**: Use `.bytes()`, `.text()`, or `.stream()` on the retrieved field.\n\n ```javascript\n let record = await MyTable.get('my-record');\n let buffer = await record.data.bytes(); // ArrayBuffer\n let text = await record.data.text(); // string\n let stream = record.data.stream(); // ReadableStream\n ```\n\n4. **Use `saveBeforeCommit` when full write must complete before commit**: By default, blobs are not ACID-compliant — a record can reference a blob before it is fully written. Set `saveBeforeCommit: true` to block the transaction until the blob is fully saved.\n\n ```javascript\n let blob = createBlob(stream, { saveBeforeCommit: true });\n await MyTable.put({ id: 'my-record', data: blob });\n // put() resolves only after blob is fully written and record is committed\n ```\n\n5. **Register an error handler when returning a blob via REST**: Handle interrupted streams to avoid stale references.\n\n ```javascript\n export class MyEndpoint extends MyTable {\n static async get(target) {\n const record = await super.get(target);\n let blob = record?.data;\n if (!blob) return record;\n blob.on('error', () => {\n MyTable.invalidate(target);\n });\n return { status: 200, headers: {}, body: blob };\n }\n }\n ```\n\n6. **Pass `BlobOptions` to control storage behavior**: `createBlob()` accepts an options object as its second argument.\n\n | Option | Type | Default | Description |\n | ------------------ | --------- | ----------- | ----------------------------------------------------------------------------------- |\n | `type` | `string` | `undefined` | MIME type (e.g., `image/jpeg`). Used when serving HTTP and readable via `blob.type` |\n | `size` | `number` | `undefined` | Size in bytes if known ahead of time; otherwise inferred |\n | `saveBeforeCommit` | `boolean` | `false` | Wait until blob is fully written before the transaction commits |\n | `compress` | `boolean` | `false` | Compress stored data with deflate |\n | `flush` | `boolean` | `false` | Flush file to disk after writing, before the `createBlob` promise chain resolves |\n\n## Examples\n\n**Store an image with a MIME type:**\n\n```javascript\nlet blob = createBlob(imageBuffer, { type: 'image/jpeg' });\nawait Photo.put({ id, data: blob });\n```\n\n**Stream a blob in as it streams out (low-latency passthrough):**\n\n```javascript\nlet blob = createBlob(incomingStream);\nawait MyTable.put({ id: 'my-record', data: blob });\n\nlet record = await MyTable.get('my-record');\nlet outgoingStream = record.data.stream();\n```\n\n**Wait for full write before commit:**\n\n```javascript\nlet blob = createBlob(stream, { saveBeforeCommit: true });\nawait MyTable.put({ id: 'my-record', data: blob });\n```\n\n## Notes\n\n- `Blob` implements the Web API `Blob` interface. All standard methods — `.text()`, `.arrayBuffer()`, `.stream()`, `.slice()`, and `.bytes()` — are available on retrieved blob fields.\n- Blobs are stored **separately from the record**, not within it. If you need the binary data to be a true ACID-committed part of the record, use a `Bytes` field instead.\n- Any string or buffer assigned to a `Blob`-typed field in a `put`, `patch`, or `publish` is automatically coerced to a `Blob` — manual `createBlob()` calls are not required in those cases.\n- Use `saveBeforeCommit: true` whenever downstream consumers must not read a partially written blob.\n","v5-upgrade":"---\nname: v5-upgrade\ndescription: >-\n Breaking changes and recommended updates when migrating a Harper application\n to v5.\nmetadata:\n mode: generate\n sources:\n - release-notes/v5-lincoln/v5-migration.md\n sourceCommit: 2cfb81318f17e0aca2d600109e6ad813d2d0443e\n inputHash: 66ca7a9e265d006e\n---\n\n# v5 Upgrade: Breaking Changes and Migration Guide\n\nInstructions for the agent to follow when migrating a Harper application to v5, covering all breaking changes and required code updates.\n\n## When to Use\n\nApply this rule when upgrading an existing Harper application from v4 to v5, when encountering runtime errors after upgrading, or when reviewing application code for v5 compatibility. Every breaking change listed here must be addressed before the application will behave correctly under v5.\n\n## How It Works\n\n1. **Update the package import**: Replace all imports from `'harperdb'` with `from 'harper'`.\n\n ```javascript\n import { tables } from 'harper';\n ```\n\n2. **Enable install scripts if needed**: Harper v5 runs `npm install` with `--ignore-scripts` by default. If your application requires install scripts (e.g., to compile native binaries), set `allowInstallScripts` in your deployment options.\n\n3. **Update `Table.get` usage**: `Table.get` now returns a plain frozen record object, not a table class instance. The `wasLoadedFromSource()` method no longer exists on the returned object. Replace it with `loadedFromSource` on the `RequestTarget`:\n\n ```javascript\n // Before\n const record = await Table.get(id);\n if (record.wasLoadedFromSource()) {\n // record was loaded from origin (not cache)\n }\n ```\n\n ```javascript\n // After\n import { getContext } from 'harper';\n const target = new RequestTarget();\n target.id = id;\n const record = await Table.get(target);\n if (target.loadedFromSource) {\n // record was loaded from origin (not cache)\n }\n ```\n\n The record objects do have `getUpdatedTime` and `getExpiresAt` methods available.\n\n4. **Handle frozen records**: The record object returned by `Table.get` is frozen — you cannot mutate it directly. Copy it before modifying:\n\n ```javascript\n // Before\n const record = await Table.get(id);\n record.property = 'changed';\n ```\n\n ```javascript\n // After\n let record = await Table.get(id);\n record = { ...record, property: 'changed' };\n ```\n\n5. **Register allowed spawn commands via `allowedSpawnCommands`**: `spawn` and `execFile` may only launch executables listed in `applications.allowedSpawnCommands` in `harper-config.yaml`. Only the first token of the command is matched. `exec` is not usable through the substituted module, and `execSync` always throws.\n\n ```yaml\n applications:\n allowedSpawnCommands:\n - npm\n - node\n ```\n\n Additionally, `spawn`, `execFile`, and `fork` now require a `name` property in the `options` argument to prevent process multiplication across threads.\n\n6. **Update transaction and context usage**: Harper v5 uses asynchronous context tracking. `Table.get` and other calls now automatically inherit the current transaction. Code that previously omitted context to bypass a transaction will no longer work as expected. Explicitly commit the transaction or wrap calls in a new transaction to read updated data:\n\n ```javascript\n import { setTimeout as delay } from 'node:timers/promises';\n import { getContext, transaction } from 'harper';\n\n class MyResource {\n static async get(target) {\n await getContext().transaction.commit();\n while ((await transaction(() => Table.get(target))).status !== 'ready') {\n await delay(100);\n }\n return Table.get(target);\n }\n }\n ```\n\n Use `getContext` (exported from `'harper'`) to access the current transaction anywhere without passing context explicitly.\n\n7. **Replace `blob.save()`**: The `blob.save()` method has been removed. Use the `saveBeforeCommit` flag in the options passed to the `Blob` constructor instead.\n\n8. **Handle response `headers`**: If you return an object from a REST method with a `headers` property, Harper v5 will use it as the response headers. Ensure any object you return that incidentally has a `headers` property is intentional.\n\n9. **Configure the module loader**: Harper v5 loads application modules through Node.js's VM module API. Control this with the `moduleLoader` setting:\n\n | Value | Behavior |\n | -------------------- | ------------------------------------------------ |\n | `vm-current-context` | Default. Shares intrinsics with Harper. |\n | `vm` | Separate context and intrinsics per application. |\n | `native` | Standard `import()`, no application context. |\n | `compartment` | SES Compartments; advanced and heavier. |\n\n The default (`vm-current-context`) avoids `instanceof` failures for values crossing the application/Harper boundary. Choose `vm` only if you need separate per-application intrinsics. Use `native` if the VM loader causes compatibility problems you cannot otherwise resolve.\n\n To disable the VM loader entirely and restore pre-v5 behavior:\n\n ```yaml\n applications:\n moduleLoader: native\n ```\n\n Note: `logger` and per-app `config` are not available in `native` mode.\n\n If the goal is only to fix package compatibility while keeping application context for first-party code, `dependencyLoader: native` is a narrower option — it uses native loading only for npm packages while keeping the VM loader for application source files.\n\n10. **Handle intrinsic `lockdown`**: The default `lockdown` mode (`freeze-after-load`) freezes JavaScript intrinsics (`Object`, `Array`, `Promise`, `Map`, `Set`, etc.) after all application code loads. Any code or dependency that modifies intrinsic prototypes at runtime will throw a `TypeError`. If a dependency requires a temporary workaround, set:\n\n ```yaml\n applications:\n lockdown: none\n ```\n\n11. **Check `allowedDirectory` in production**: In production, applications can only load modules from within their own directory tree (`allowedDirectory: app`). If your application loads files from outside its directory, set:\n\n ```yaml\n applications:\n allowedDirectory: any\n ```\n\n## Examples\n\n### Full `harper-config.yaml` module loading block\n\n```yaml\napplications:\n lockdown: freeze-after-load\n moduleLoader: vm-current-context\n dependencyLoader: auto\n allowedDirectory: app\n allowedSpawnCommands:\n - npm\n - node\n```\n\n### Updated import and context access\n\n```javascript\nimport { tables, getContext, transaction } from 'harper';\n```\n\n### Checking `loadedFromSource` after `Table.get`\n\n```javascript\nconst target = new RequestTarget();\ntarget.id = id;\nconst record = await Table.get(target);\nif (target.loadedFromSource) {\n // record was loaded from origin (not cache)\n}\n```\n\n### Copying a frozen record before mutation\n\n```javascript\nlet record = await Table.get(id);\nrecord = { ...record, property: 'changed' };\n```\n\n## Notes\n\n- The `logger` exported from `'harper'` is tagged with the application name when using the VM module loader. It is not available in `native` mode.\n- `instanceof` checks for values crossing the application/Harper boundary will fail when using `moduleLoader: vm` (separate intrinsics). Use `vm-current-context` (the default) to avoid this.\n- `import()` behavior depends on the `moduleLoader` setting. In `native` mode, standard Node.js `import()` is used with no application context.\n- Automatic context tracking simplifies code but requires explicit `commit()` or new `transaction()` calls when you need to observe data written within the same transaction.\n- Use `getContext` from `'harper'` rather than passing context manually through every call — this is the recommended pattern in v5.\n","vector-indexing":`---
1898
+ name: vector-indexing
1899
+ description: How to enable and query vector indexes for similarity search in Harper.
1900
+ metadata:
1901
+ mode: generate
1902
+ sources:
1903
+ - reference/v5/database/schema.md#Vector Indexing
1904
+ sourceCommit: d4cbc1a7dd400462e4a3243f944b3a75d89b29ca
1905
+ inputHash: 1dae788bc850ea90
1906
+ ---
1907
+
1908
+ # Vector Indexing
1909
+
1910
+ Instructions for the agent to enable HNSW vector indexes on table fields and query them for similarity search in Harper.
1911
+
1912
+ ## When to Use
1913
+
1914
+ Apply this rule when adding a vector similarity search capability to a Harper table — for example, storing text embeddings and querying for nearest neighbors, filtering by distance threshold, or combining vector search with record-level access control. See [adding-tables-with-schemas.md](adding-tables-with-schemas.md) for how to define the surrounding table schema.
1915
+
1916
+ ## How It Works
1917
+
1918
+ 1. **Declare the vector index** on a \`[Float]\` field using \`@indexed(type: "HNSW")\`:
1919
+
1920
+ \`\`\`graphql
1921
+ type Document @table {
1922
+ id: Long @primaryKey
1923
+ textEmbeddings: [Float] @indexed(type: "HNSW")
1924
+ }
1925
+ \`\`\`
1926
+
1927
+ 2. **Query nearest neighbors** using \`Document.search()\` with the \`sort\` parameter. Set \`attribute\` to the indexed field and \`target\` to the query vector:
1928
+
1929
+ \`\`\`javascript
1930
+ let results = Document.search({
1931
+ sort: { attribute: 'textEmbeddings', target: searchVector },
1932
+ limit: 5,
1933
+ });
1934
+ \`\`\`
1935
+
1936
+ 3. **Combine with filter conditions** to narrow results before or during graph traversal. Selective conditions are automatically diverted to an exact-scan strategy:
1937
+
1938
+ \`\`\`javascript
1939
+ let results = Document.search({
1940
+ conditions: [{ attribute: 'price', comparator: 'lt', value: 50 }],
1941
+ sort: { attribute: 'textEmbeddings', target: searchVector },
1942
+ limit: 5,
1943
+ });
1944
+ \`\`\`
1945
+
1946
+ 4. **Apply a function predicate during traversal** using \`vectorFilter\` (JavaScript API only). The function receives each candidate record and must return a synchronous boolean. It must be side-effect free and fast:
1947
+
1948
+ \`\`\`javascript
1949
+ let results = Document.search(
1950
+ {
1951
+ sort: { attribute: 'textEmbeddings', target: searchVector },
1952
+ vectorFilter: (record) =>
1953
+ record.tenantId === context.user.tenantId && record.status === 'published',
1954
+ limit: 10,
1955
+ },
1956
+ context,
1957
+ );
1958
+ \`\`\`
1959
+
1960
+ 5. **Filter by distance threshold** using \`target\` directly on a condition alongside \`comparator\` and \`value\`. This returns matches within the threshold without using \`sort\`:
1961
+
1962
+ \`\`\`javascript
1963
+ let results = Document.search({
1964
+ conditions: {
1965
+ attribute: 'textEmbeddings',
1966
+ comparator: 'lt',
1967
+ value: 0.1,
1968
+ target: searchVector,
1969
+ },
1970
+ });
1971
+ \`\`\`
1972
+
1973
+ 6. **Include computed distance in results** by adding \`$distance\` to \`select\`. Works with both \`sort\`-based and threshold queries:
1974
+
1975
+ \`\`\`javascript
1976
+ let results = Document.search({
1977
+ select: ['name', '$distance'],
1978
+ sort: { attribute: 'textEmbeddings', target: searchVector },
1979
+ limit: 5,
1980
+ });
1981
+ \`\`\`
1982
+
1983
+ 7. **Tune per-query search options** on the \`sort\` descriptor using \`distance\` and \`ef\`:
1984
+
1985
+ \`\`\`javascript
1986
+ let results = Document.search({
1987
+ sort: { attribute: 'textEmbeddings', target: searchVector, distance: 'dotProduct', ef: 200 },
1988
+ limit: 5,
1989
+ });
1990
+ \`\`\`
1991
+
1992
+ 8. **Tune filtered traversal** with \`ef\` and \`filterExpansion\` when a \`vectorFilter\` is very selective. The visit budget is \`ef * filterExpansion\` nodes (\`filterExpansion\` defaults to \`24\`):
1993
+
1994
+ \`\`\`javascript
1995
+ let results = Document.search(
1996
+ {
1997
+ sort: { attribute: 'textEmbeddings', target: searchVector, ef: 200, filterExpansion: 40 },
1998
+ vectorFilter: (record) => record.category === 'rare',
1999
+ limit: 10,
2000
+ },
2001
+ context,
2002
+ );
2003
+ \`\`\`
2004
+
2005
+ 9. **Enforce row-level access control** using \`rowFilter\` on search and subscription targets (JavaScript API only). Attach it in an operation override. For vector queries, \`rowFilter\` participates in HNSW traversal so callers receive the k nearest _matching_ records:
2006
+
2007
+ \`\`\`javascript
2008
+ function canReadReport(record, context) {
2009
+ const user = context.user;
2010
+ if (user?.role?.permission?.super_user) return true;
2011
+ return user?.username != null && record.ownerId != null && record.ownerId === user.username;
2012
+ }
2013
+
2014
+ export class Reports extends tables.Reports {
2015
+ search(target) {
2016
+ target.rowFilter = canReadReport;
2017
+ return super.search(target);
2018
+ }
2019
+ }
2020
+ \`\`\`
2021
+
2022
+ ### HNSW Index Parameters
2023
+
2024
+ Configure parameters directly on \`@indexed(type: "HNSW", ...)\`:
2025
+
2026
+ | Parameter | Default | Description |
2027
+ | ---------------------- | ----------------- | ------------------------------------------------------------------------------------------------ |
2028
+ | \`distance\` | \`"cosine"\` | Distance function: \`"cosine"\`, \`"euclidean"\`, or \`"dotProduct"\` |
2029
+ | \`efConstruction\` | \`100\` | Max nodes explored during index construction. Higher = better recall, lower = better performance |
2030
+ | \`M\` | \`16\` | Preferred connections per graph layer |
2031
+ | \`optimizeRouting\` | \`0.5\` | Heuristic aggressiveness for omitting redundant connections (0 = off, 1 = most aggressive) |
2032
+ | \`mL\` | computed from \`M\` | Normalization factor for level generation |
2033
+ | \`efConstructionSearch\` | auto-scaled | Max nodes explored during search. When unset, auto-scales with index size |
2034
+ | \`quantization\` | — | \`"int8"\` stores vectors quantized to int8 |
2035
+ | \`filterExpansion\` | \`24\` | Visit-budget multiplier for filtered search: visits at most \`ef * filterExpansion\` nodes |
2036
+
2037
+ Per-query \`sort\` descriptor options:
2038
+
2039
+ | Option | Values | Description |
2040
+ | ---------- | ----------------------------------------- | ------------------------------------------------------ |
2041
+ | \`distance\` | \`"cosine"\`, \`"euclidean"\`, \`"dotProduct"\` | Overrides the index's distance function for this query |
2042
+ | \`ef\` | integer | Overrides the search exploration budget for this query |
2043
+
2044
+ ## Examples
2045
+
2046
+ **Index with custom HNSW parameters:**
2047
+
2048
+ \`\`\`graphql
2049
+ type Document @table {
2050
+ id: Long @primaryKey
2051
+ textEmbeddings: [Float]
2052
+ @indexed(type: "HNSW", distance: "euclidean", optimizeRouting: 0, efConstructionSearch: 100)
2053
+ }
2054
+ \`\`\`
2055
+
2056
+ **Index with int8 quantization:**
2057
+
2058
+ \`\`\`graphql
2059
+ type Document @table {
2060
+ id: Long @primaryKey
2061
+ textEmbeddings: [Float] @indexed(type: "HNSW", quantization: "int8")
2062
+ }
2063
+ \`\`\`
2064
+
2065
+ **Nearest-neighbor search with distance included:**
2066
+
2067
+ \`\`\`javascript
2068
+ let results = Document.search({
2069
+ select: ['name', '$distance'],
2070
+ sort: { attribute: 'textEmbeddings', target: searchVector },
2071
+ limit: 5,
2072
+ });
2073
+ \`\`\`
2074
+
2075
+ **Filtered traversal with tuned budget:**
2076
+
2077
+ \`\`\`javascript
2078
+ let results = Document.search(
2079
+ {
2080
+ sort: { attribute: 'textEmbeddings', target: searchVector, ef: 200, filterExpansion: 40 },
2081
+ vectorFilter: (record) => record.category === 'rare',
2082
+ limit: 10,
2083
+ },
2084
+ context,
2085
+ );
2086
+ \`\`\`
2087
+
2088
+ ## Notes
2089
+
2090
+ - \`vectorFilter\` and \`rowFilter\` are available from the JavaScript API only; they cannot be set through REST or QUERY request data.
2091
+ - \`vectorFilter\` functions must be synchronous, side-effect free, and fast — they can run once per candidate record visited during traversal; verdicts are memoized per query. Records passed to them are frozen.
2092
+ - \`rowFilter\` does not apply to a direct primary-key \`get\`.
2093
+ - Changing \`efConstructionSearch\` on an existing index does not trigger a rebuild. Structural parameters (\`distance\`, \`M\`, \`efConstruction\`, \`quantization\`) do rebuild the index when changed.
2094
+ - With \`quantization: "int8"\`, nearest-neighbor \`sort\` queries re-rank results against full-precision vectors, restoring exact ordering and exact \`$distance\` values. Distance-threshold (\`lt\`/\`le\`) queries filter on the approximate distance.
2095
+ - The correct parameter name is \`efConstruction\` (seeds the construction budget) and \`efConstructionSearch\` (controls search budget). The name \`efSearchConstruction\` is a previous documentation error.
2096
+ - When no \`ef\` is passed and \`efConstructionSearch\` (or \`efConstruction\`) is not explicitly set, the search budget auto-scales with index size.
2097
+ - \`cosine\` is the default distance function when \`distance\` is not specified.
2098
+ `},ya={name:`readHarperSkill`,description:`Returns documentation for a Harper skill or best practice. Skills provide guidance on developing Harper applications.`,inputSchema:i({skill:v(_a)})};async function ba({input:{skill:e}}){return{success:!!va[e],message:va[e]||`No skill found with the name ${e}`}}var xa={...ya,icon:st,execute:ba},Sa={name:`readLogs`,description:`Returns the matching logs from the server.`,inputSchema:i({log_name:v([`hdb.log`,`system.log`]).default(`hdb.log`),limit:o().or(g()).optional(),level:v([`notify`,`error`,`warn`,`info`,`debug`,`trace`,`undefined`]).or(g()).optional(),from:o().or(g()).optional(),until:o().or(g()).optional()})};async function Ca({input:e,instanceClientParams:t}){try{return{success:!0,data:await Be({...t,logFilters:e,replicated:t.entityType===`cluster`})}}catch(e){return{success:!1,message:`Error: ${e}`}}}var wa={...Sa,icon:ht,execute:Ca},Ta={name:`readTableRecords`,description:`Retrieves some or all table records from a database on the server.`,inputSchema:i({database:o().trim(),table:o().trim(),pageIndex:l().default(0),pageSize:l().default(10),primaryKey:o(),conditions:m(i({search_attribute:o(),search_type:v([`between`,`eq`,`equals`,`greater_than`,`greater_than_equal`,`less_than`,`less_than_equal`,`ne`,`not_equal`,`starts_with`]),search_value:a()})),sort:i({attribute:o(),descending:_()})})};async function Ea({input:{database:e,table:t,conditions:n,primaryKey:r,...i},instanceClientParams:a}){try{if(!n.length){let{data:n}=await Ye({...a,databaseName:e,tableName:t,onlyIfCached:!0,searchAttribute:r,...i});return{success:!0,data:n}}let{data:o}=await Te({...a,databaseName:e,tableName:t,onlyIfCached:!0,conditions:n,...i});return{success:!0,data:o}}catch(e){return{success:!1,message:`Error: ${e}`}}}var Da={...Ta,icon:Fe,execute:Ea},Oa={name:`restartHTTPService`,description:`Restarts the HTTP service on the server to allow schema and resource changes to be applied.`,inputSchema:i({})};async function ka({instanceClientParams:e,baseURL:t}){let n=T.loading(`Restarting HTTP service...`,{description:`This may take a bit.`,duration:3e5});try{await je({...e,operation:`restart_service`,replicated:e.entityType===`cluster`})}catch(e){return Li(n,e),e instanceof be?{success:!0,message:`Harper accepted the restart, but Studio couldn't confirm the HTTP service came back, so it may still be starting. Don't restart it again until its status shows it is back, or it goes down a second time.`,webURL:t}:{success:!1,message:`Error: ${Q(e)}`}}return T.success(`Done!`,{description:`HTTP Service restarted!`,id:n,duration:5e3}),{success:!0,message:`HTTP Service restarted!`,webURL:t}}var Aa={...Oa,icon:Ke,execute:ka,requiresApproval:!0},ja={name:`setComponentFile`,description:`Returns the contents of a component file by its full path (which was returned by getComponents)`,inputSchema:i({path:o().trim(),payload:o(),encoding:v([`utf8`,`ASCII`,`binary`,`hex`,`base64`,`utf16le`,`latin1`,`ucs2`])})};async function Ma({input:{path:e,encoding:t,payload:n},instanceClientParams:r}){try{let i=e.split(`/`),a=i.shift(),o=i.join(`/`),s=await ge({...r,file:o,project:a,payload:n,encoding:t});return await D.invalidateQueries({queryKey:[r.entityId,`get_component_file`,a,o]}),ke(`ReloadApplicationRootEntries`,!0),{success:!0,data:s}}catch(e){return{success:!1,message:`Error: ${Q(e)}`}}}var Na={...ja,icon:pt,execute:Ma,requiresApproval:!0},Pa={name:`updateTableRecords`,description:`Updates records in a particular table in a particular database on the server.`,inputSchema:i({database:o().trim(),table:o().trim(),records:m(a())})};async function Fa({input:{database:e,table:t,records:n},instanceClientParams:r,params:i}){try{let a=await Ge({...r,databaseName:e,tableName:t,records:n}),{databaseName:o,tableName:s}=i;return await D.invalidateQueries({queryKey:[r.entityId,o,s]}),{success:!0,data:a}}catch(e){return{success:!1,message:`Error: ${Q(e)}`}}}var Ia={readHarperSkill:xa,createApp:zi,readLogs:wa,getAnalytics:Ji,listAnalyticsMetrics:ga,restartHTTPService:Aa,collectFeedback:Fi,getUserContext:la,getComponentFile:Zi,getComponents:ea,setComponentFile:Na,dropComponentFile:Gi,getDescribeAll:ra,getDescribeTable:oa,insertTableRecords:fa,readTableRecords:Da,updateTableRecords:{...Pa,icon:at,execute:Fa,requiresApproval:!0},deleteTableRecords:Hi};function $(e){return Ia[e]}function La(e){return e.state===`input-available`&&!!$(oi(e))?.requiresApproval}function Ra(e){let t=[];for(let[n,r]of(e??[]).entries()){if(Z(r)){if(La(r)){t.push({kind:`part`,part:r,index:n});continue}let e=t.at(-1);e?.kind===`tool-group`?e.parts.push(r):t.push({kind:`tool-group`,parts:[r],index:n});continue}ni(r)&&r.text.length>0&&t.push({kind:`part`,part:r,index:n})}return t}function za({part:e,onApprove:t,onDeny:n,onAlwaysApprove:r,isApproving:i}){let[a,o]=(0,O.useState)(!1),[s,c]=(0,O.useState)(!1),l=oi(e),u=$(l),d=u?.icon||Ne,f=u?.requiresApproval,p=(0,O.useMemo)(()=>!e.input||typeof e.input==`object`&&Object.keys(e.input).length===0,[e.input]),m=(0,O.useMemo)(()=>{let t=JSON.stringify(e.input,null,` `);return{json:t,lines:t?t.split(`
2099
+ `).length:0}},[e.input]),h=(0,O.useMemo)(()=>{let t=JSON.stringify(e.output,null,` `);return{json:t,lines:t?t.split(`
2100
+ `).length:0}},[e.output]);return(0,k.jsxs)(`div`,{className:`tool-invocation ${e.state}`,children:[(0,k.jsxs)(`div`,{className:`tool-info`,children:[(0,k.jsxs)(`div`,{className:`tool-name`,children:[(0,k.jsx)(d,{size:14}),(0,k.jsx)(`span`,{children:l})]}),(0,k.jsxs)(`div`,{className:`tool-status`,children:[e.state===`input-streaming`&&(0,k.jsx)(`span`,{children:`Thinking...`}),e.state===`input-available`&&(0,k.jsx)(`span`,{children:i?`Executing...`:f?`Awaiting Approval...`:`Executing...`}),e.state===`output-available`&&(e.output?.error?(0,k.jsx)(dt,{size:14,className:`text-destructive`}):(0,k.jsx)(oe,{size:14}))]})]}),e.state!==`input-streaming`&&(0,k.jsxs)(`div`,{className:`tool-io`,children:[!p&&(0,k.jsxs)(`div`,{className:`tool-args`,children:[(0,k.jsxs)(`div`,{className:`flex items-center justify-between gap-2 mb-1`,children:[(0,k.jsx)(`strong`,{children:`Input:`}),m.lines>3&&(0,k.jsx)(w,{type:`button`,variant:`ghost`,size:`sm`,className:`h-6 px-2 text-[10px] uppercase tracking-wider text-muted-foreground hover:text-foreground`,onClick:()=>o(!a),children:a?(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(se,{size:12}),`Hide`]}):(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(ae,{size:12}),`Show`]})})]}),(0,k.jsx)(`div`,{className:a?`whitespace-pre-wrap`:`line-clamp-3 overflow-hidden whitespace-pre-wrap`,children:m.json})]}),e.state===`input-available`&&f&&(0,k.jsxs)(`div`,{className:`flex gap-2 mt-3 pt-3 border-t`,children:[(0,k.jsxs)(w,{size:`sm`,className:`h-8 text-xs bg-green-600 hover:bg-green-700 text-white`,onClick:()=>t?.(e.toolCallId),disabled:i,children:[i?(0,k.jsx)(De,{className:`mr-2 h-3 w-3 animate-spin`}):null,`Approve`]}),(0,k.jsx)(w,{type:`button`,size:`sm`,variant:`outline`,className:`h-8 text-xs approval-outline`,onClick:()=>r?.(e.toolCallId),disabled:i,children:`Always Approve`}),(0,k.jsx)(w,{type:`button`,size:`sm`,variant:`outline`,className:`h-8 text-xs approval-outline`,onClick:()=>n?.(e.toolCallId),disabled:i,children:`Deny`})]}),e.state===`output-available`&&(0,k.jsx)(k.Fragment,{children:u?.render?u.render(e):(0,k.jsxs)(`div`,{className:`tool-result`,children:[(0,k.jsxs)(`div`,{className:`flex items-center justify-between gap-2 mb-1`,children:[(0,k.jsx)(`strong`,{children:`Result:`}),h.lines>3&&(0,k.jsx)(w,{type:`button`,variant:`ghost`,size:`sm`,className:`h-6 px-2 text-[10px] uppercase tracking-wider text-muted-foreground hover:text-foreground`,onClick:()=>c(!s),children:s?(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(se,{size:12}),`Hide`]}):(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(ae,{size:12}),`Show`]})})]}),(0,k.jsx)(`div`,{className:s?`whitespace-pre-wrap`:`line-clamp-3 overflow-hidden whitespace-pre-wrap`,children:h.json})]})})]})]})}function Ba({parts:e,onApprove:t,onDeny:n,onAlwaysApprove:r,approvingToolCallIds:i}){let[a,o]=(0,O.useState)(!1),s=e.some(e=>e.state!==`output-available`&&e.state!==`output-error`),c=e.some(e=>e.state===`output-error`||e.state===`output-available`&&e.output?.error),l=e.length===1?oi(e[0]):void 0,u=l&&$(l)?.icon||Ct,d=l??`${e.length} tools`;return(0,k.jsxs)(`div`,{className:`tool-group`,children:[(0,k.jsxs)(`button`,{type:`button`,className:`tool-group-summary`,"aria-expanded":a,onClick:()=>o(!a),children:[a?(0,k.jsx)(ae,{size:14}):(0,k.jsx)(Oe,{size:14}),(0,k.jsx)(u,{size:14}),(0,k.jsx)(`span`,{children:s?`Using ${d}...`:`Used ${d}`}),(0,k.jsx)(`span`,{className:`tool-group-status`,children:s?(0,k.jsx)(De,{size:14,className:`animate-spin`}):c?(0,k.jsx)(dt,{size:14,className:`text-destructive`}):(0,k.jsx)(oe,{size:14})})]}),a&&e.map(e=>(0,k.jsx)(za,{part:e,onApprove:t,onDeny:n,onAlwaysApprove:r,isApproving:i?.has(e.toolCallId)},e.toolCallId))]})}function Va({message:e,onApprove:t,onDeny:n,onAlwaysApprove:r,approvingToolCallIds:i}){return e.parts?.some(e=>ni(e)&&e.text.length>0||Z(e))?(0,k.jsxs)(nt.div,{initial:{opacity:0,y:10},animate:{opacity:1,y:0},className:`message-bubble ${e.role===`user`?`user`:`assistant`}`,children:[(0,k.jsx)(`div`,{className:`avatar`,children:e.role===`user`?(0,k.jsx)(xt,{size:18}):(0,k.jsx)(tt,{size:18})}),(0,k.jsx)(`div`,{className:`content`,children:Ra(e.parts).map(e=>{if(e.kind===`tool-group`)return(0,k.jsx)(Ba,{parts:e.parts,onApprove:t,onDeny:n,onAlwaysApprove:r,approvingToolCallIds:i},e.parts[0].toolCallId);let{part:a,index:o}=e;return ni(a)?(0,k.jsx)(`div`,{className:`text-block`,children:a.text},o):Z(a)?(0,k.jsx)(za,{part:a,onApprove:t,onDeny:n,onAlwaysApprove:r,isApproving:i?.has(a.toolCallId)},o):null})})]},e.id):null}function Ha(e,t){if(e!==`submitted`&&e!==`streaming`)return!1;if(t?.role!==`assistant`)return!0;let n=t.parts?.at(-1);return n?ni(n)?n.state!==`streaming`||n.text.length===0:!Z(n)||n.state===`output-available`||n.state===`output-error`:!0}function Ua(){return(0,k.jsxs)(nt.div,{initial:{opacity:0,y:10},animate:{opacity:1,y:0},transition:{delay:.2},className:`message-bubble assistant`,children:[(0,k.jsx)(`div`,{className:`avatar`,children:(0,k.jsx)(tt,{size:18})}),(0,k.jsxs)(`div`,{className:`content thinking-indicator`,role:`status`,"aria-label":`Harper Agent is thinking`,children:[(0,k.jsx)(`span`,{className:`thinking-dot`}),(0,k.jsx)(`span`,{className:`thinking-dot`}),(0,k.jsx)(`span`,{className:`thinking-dot`})]})]})}function Wa(e){return re({queryKey:[`getMyUsage`,e],queryFn:async()=>{let{data:t}=await S.get(`/Chat/Usage/${e}`);return t}})}function Ga(){let{organizationId:e}=ne({strict:!1});return te(Wa(e))}function Ka(){let{data:e,isLoading:t,error:n}=Ga();if(t||n||!e)return null;let{usageUSD:r,monthlyLimitUSD:i,usageBarPercent:a}=e,o=e=>new Intl.NumberFormat(`en-US`,{style:`currency`,currency:`USD`}).format(e);return(0,k.jsxs)(`div`,{className:`usage-container`,children:[(0,k.jsxs)(`div`,{className:`usage-info`,children:[(0,k.jsx)(`span`,{children:`Monthly Org Usage`}),(0,k.jsxs)(`span`,{children:[o(r),` / `,o(i)]}),(0,k.jsxs)(`span`,{children:[Math.round(a),`%`]})]}),(0,k.jsx)(`div`,{className:`usage-bar-bg`,children:(0,k.jsx)(`div`,{className:`usage-bar-fill`,style:{width:`${a}%`}})})]})}function qa({autoFocus:e,closeChat:t}){let n=ne({strict:!1}),{organizationId:r}=n,[i,a]=qe(`ApplicationChat`,``),[o,s]=(0,O.useState)(!0),[c,l]=(0,O.useState)({}),[u,d]=(0,O.useState)(new Set),[f,p]=ze(Se.ChatAlwaysApprovedTools,[]),m=new Set(f),h=et(),g=Ae(),_=ie(),{messages:v,sendMessage:y,status:b,addToolOutput:x,setMessages:S}=Ai({transport:bi(r),generateId:P(),sendAutomaticallyWhen:yi,onFinish(){_.invalidateQueries({queryKey:[`getMyUsage`]})},async onToolCall({toolCall:e}){if(e.dynamic)return;let t=$(e.toolName);if(t){if(t.requiresApproval&&!m.has(e.toolName)){let t={type:`tool-call`,toolCallId:e.toolCallId,toolName:e.toolName,input:e.input};l(n=>({...n,[e.toolCallId]:t}));return}let r=await t.execute({input:e.input,instanceClientParams:g,baseURL:h,params:n});x({tool:e.toolName,toolCallId:e.toolCallId,output:r})}}}),C=(0,O.useCallback)(async e=>{let t=c[e];if(t){d(t=>{let n=new Set(t);return n.add(e),n});try{let r=$(t.toolName);if(r){let i=await r.execute({input:t.input,instanceClientParams:g,baseURL:h,params:n});x({tool:t.toolName,toolCallId:t.toolCallId,output:i}),l(t=>{let n={...t};return delete n[e],n})}}finally{d(t=>{let n=new Set(t);return n.delete(e),n})}}},[c,g,h,x,n]),ee=(0,O.useCallback)(e=>{let t=c[e];t&&(x({tool:t.toolName,toolCallId:t.toolCallId,output:{error:`User denied the tool execution.`}}),l(t=>{let n={...t};return delete n[e],n}))},[c,x]),te=(0,O.useCallback)(async e=>{let t=c[e];t&&(p(e=>He([...e,t.toolName])),await C(e))},[c,p,C]);(0,O.useEffect)(()=>{(async()=>{try{let e=await Et();Array.isArray(e)&&S(e)}catch(e){console.error(`Failed to fetch initial messages:`,e)}finally{s(!1)}})()},[S]);let re=b===`streaming`||b===`submitted`,w=(0,O.useRef)(null);return(0,O.useEffect)(()=>{w.current?.scrollIntoView({behavior:`smooth`})},[v]),(0,k.jsxs)(`div`,{className:`flex flex-col h-full`,children:[(0,k.jsxs)(`div`,{className:`flex items-start justify-between gap-6 px-4 py-2.5 border-b border-border bg-card`,children:[(0,k.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0 flex-1`,children:[(0,k.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,k.jsx)(tt,{className:`text-primary`,size:20}),(0,k.jsx)(`span`,{className:`font-semibold text-foreground`,children:`Harper Agent`})]}),(0,k.jsx)(Ka,{})]}),(0,k.jsxs)(`div`,{className:`flex items-center gap-2 shrink-0`,children:[(0,k.jsx)(Tt,{setMessages:S}),(0,k.jsx)(`button`,{onClick:t,className:`p-1 hover:bg-accent rounded-md transition-colors text-muted-foreground hover:text-foreground`,title:`Close chat`,children:(0,k.jsx)(We,{size:20})})]})]}),(0,k.jsx)(`div`,{className:`flex-1 overflow-hidden`,children:(0,k.jsxs)(`div`,{className:`chat-interface h-full w-full`,children:[(0,k.jsxs)(`div`,{className:`messages-area`,children:[o&&(0,k.jsx)(Mi,{}),!o&&v.length===0&&(0,k.jsxs)(`div`,{className:`empty-state`,children:[(0,k.jsx)(tt,{size:48}),(0,k.jsx)(`p`,{children:`Ask me to create a Harper app!`})]}),v.map(e=>(0,k.jsx)(Va,{message:e,onApprove:C,onDeny:ee,onAlwaysApprove:te,approvingToolCallIds:u},e.id)),Ha(b,v.at(-1))&&(0,k.jsx)(Ua,{}),(0,k.jsx)(`div`,{ref:w})]}),(0,k.jsx)(ji,{input:i,setInput:a,onSubmit:e=>{e.preventDefault(),i.trim()&&!re&&!o&&(y({text:i}),a(``))},disabled:o,autoFocus:e})]})})]})}export{qa as Chat};