@harperfast/harper 5.2.9 → 5.2.12

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 (177) hide show
  1. package/dist/resources/DatabaseTransaction.d.ts +3 -0
  2. package/dist/resources/DatabaseTransaction.js +90 -2
  3. package/dist/resources/DatabaseTransaction.js.map +1 -1
  4. package/dist/resources/PrimaryRocksDatabase.js +2 -1
  5. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  6. package/dist/resources/RocksIndexStore.js +2 -1
  7. package/dist/resources/RocksIndexStore.js.map +1 -1
  8. package/dist/resources/Table.js +13 -5
  9. package/dist/resources/Table.js.map +1 -1
  10. package/dist/resources/auditStore.js +142 -53
  11. package/dist/resources/auditStore.js.map +1 -1
  12. package/dist/resources/databases.d.ts +10 -0
  13. package/dist/resources/databases.js +369 -97
  14. package/dist/resources/databases.js.map +1 -1
  15. package/dist/resources/search.js +33 -7
  16. package/dist/resources/search.js.map +1 -1
  17. package/dist/server/storageReclamation.d.ts +7 -0
  18. package/dist/server/storageReclamation.js +10 -0
  19. package/dist/server/storageReclamation.js.map +1 -1
  20. package/dist/server/threads/manageThreads.d.ts +1 -0
  21. package/dist/server/threads/manageThreads.js +7 -0
  22. package/dist/server/threads/manageThreads.js.map +1 -1
  23. package/npm-shrinkwrap.json +41 -41
  24. package/package.json +2 -2
  25. package/resources/DatabaseTransaction.ts +88 -2
  26. package/resources/PrimaryRocksDatabase.ts +2 -1
  27. package/resources/RocksIndexStore.ts +2 -1
  28. package/resources/Table.ts +13 -5
  29. package/resources/auditStore.ts +144 -52
  30. package/resources/databases.ts +351 -93
  31. package/resources/search.ts +32 -7
  32. package/server/storageReclamation.ts +10 -0
  33. package/server/threads/manageThreads.js +7 -0
  34. package/studio/web/assets/Chat-CokJOjyX.js +2267 -0
  35. package/studio/web/assets/FloatingChat-h9BJo6YG.js +23 -0
  36. package/studio/web/assets/{abnfDiagram-VCTEODGH-C0_BAZyO.js → abnfDiagram-VCTEODGH-B0BebmD2.js} +1 -1
  37. package/studio/web/assets/{alertDialog-DIHt7Z0r.js → alertDialog-CQyAJJhl.js} +1 -1
  38. package/studio/web/assets/{apiToken-c3Rd-w6g.js → apiToken-B_g12QrM.js} +1 -1
  39. package/studio/web/assets/applications-Dlq5kJ2O.js +296 -0
  40. package/studio/web/assets/architecture-7GRP2DOG-LB-MLAAb.js +1 -0
  41. package/studio/web/assets/{architectureDiagram-5GKGNRK7-BWzrASgm.js → architectureDiagram-5GKGNRK7-7SW3GD-K.js} +1 -1
  42. package/studio/web/assets/authStore-C3Nfubqr.js +3 -0
  43. package/studio/web/assets/{blockDiagram-NRAW4CY4-BdJX9Khj.js → blockDiagram-I7D4REHJ-BqguiadH.js} +2 -2
  44. package/studio/web/assets/{button-DhiX-njv.js → button-BIsUKRZq.js} +2 -2
  45. package/studio/web/assets/{c4Diagram-UCG6FXSJ-CI6MzGmQ.js → c4Diagram-7LVT6UL2-LBNf8t_X.js} +1 -1
  46. package/studio/web/assets/channel-yictG-U-.js +1 -0
  47. package/studio/web/assets/{chevron-up-Df2c8uoU.js → chevron-up-DtKGqDn3.js} +1 -1
  48. package/studio/web/assets/{chunk-TEH6E4GO-P87k5mNi.js → chunk-4HAMMTFA-DWtTut21.js} +1 -1
  49. package/studio/web/assets/{chunk-75Z2AOVW-BT8tVmks.js → chunk-75Z2AOVW-QGQD6th2.js} +1 -1
  50. package/studio/web/assets/{chunk-DU6HZSFF-9kAOOmI6.js → chunk-DU6HZSFF-Chq20Ba5.js} +1 -1
  51. package/studio/web/assets/{chunk-F27PBJKO-BW7ao8AY.js → chunk-F27PBJKO-BVA5EPhV.js} +1 -1
  52. package/studio/web/assets/{chunk-GMAD6QVW-BNyXpoQO.js → chunk-GMAD6QVW-BeS7S07A.js} +1 -1
  53. package/studio/web/assets/{chunk-OBVCFTLP-D4wWOqDQ.js → chunk-GVQU2GXP-sbwVIQ8i.js} +1 -1
  54. package/studio/web/assets/{chunk-G27WJ6UU-COyLMcgK.js → chunk-IMKFNOWR-Bnh3tAVd.js} +1 -1
  55. package/studio/web/assets/{chunk-JQ64N6SF-Cyz1IeLf.js → chunk-L3NEJ4N5-COfUyKII.js} +1 -1
  56. package/studio/web/assets/chunk-OSK3NFVY-ByciRftO.js +10 -0
  57. package/studio/web/assets/{chunk-P2QGCYS3-DmIFY4d7.js → chunk-P2QGCYS3-CP1VhG_c.js} +1 -1
  58. package/studio/web/assets/{chunk-POPQ4Y6H-BPrvMyKz.js → chunk-POPQ4Y6H-ClWhhkwW.js} +1 -1
  59. package/studio/web/assets/{chunk-PWAF6VOD-2zB6IW9i.js → chunk-PWAF6VOD-1z1THyS5.js} +1 -1
  60. package/studio/web/assets/{chunk-RHFEMEQ7-2FgyI8YU.js → chunk-SHT3W25Y-LpQkMsah.js} +2 -2
  61. package/studio/web/assets/{chunk-SVP7TREG-FwtbH2QC.js → chunk-SVP7TREG-jtdAHw0S.js} +1 -1
  62. package/studio/web/assets/{chunk-LCL6LL3I-HOzK_ppE.js → chunk-TICWLB2K-VOwzetX-.js} +1 -1
  63. package/studio/web/assets/classDiagram-ZZMXUADV-VaEwSy_g.js +1 -0
  64. package/studio/web/assets/classDiagram-v2-VYDZK3BY-VaEwSy_g.js +1 -0
  65. package/studio/web/assets/{createLucideIcon-BKGPfjm2.js → createLucideIcon-CzW9508A.js} +1 -1
  66. package/studio/web/assets/{cssMode-CEN2mzSA.js → cssMode-DAjzPoBf.js} +1 -1
  67. package/studio/web/assets/{cynefin-OW5HDTMX-BRkpLFQV.js → cynefin-OW5HDTMX-BbdbCvub.js} +1 -1
  68. package/studio/web/assets/{cynefinDiagram-5FMLGOSQ-CHT1DaX6.js → cynefinDiagram-5FMLGOSQ-TP-aIqbt.js} +1 -1
  69. package/studio/web/assets/{dagre-3AP2YEHR-DpUXBh63.js → dagre-GXQ25YYZ-DShnGpGo.js} +1 -1
  70. package/studio/web/assets/{diagram-S7CK7UJ4-BuymVFZT.js → diagram-S7CK7UJ4-aoCVTtcy.js} +1 -1
  71. package/studio/web/assets/{diagram-UQ7AKVKN-CyP148RM.js → diagram-UQ7AKVKN-DglXtQ6x.js} +1 -1
  72. package/studio/web/assets/{diagram-VSXAHHWV-CoCAg3M9.js → diagram-VSXAHHWV-fhEdmkwM.js} +1 -1
  73. package/studio/web/assets/{diagram-VX7I27RA-BpOqCFca.js → diagram-VX7I27RA-DccVJet6.js} +1 -1
  74. package/studio/web/assets/{diagram-Z3DM3KII-Bfpw7Vbj.js → diagram-Z3DM3KII-D-RyJJb7.js} +1 -1
  75. package/studio/web/assets/{dialog-CBf0Mr1d.js → dialog-Cn2uWgD4.js} +1 -1
  76. package/studio/web/assets/{dist-lkA3O3eM.js → dist-DP8UjMB_.js} +1 -1
  77. package/studio/web/assets/{download-BtTOBem-.js → download-B5T5r7ss.js} +1 -1
  78. package/studio/web/assets/{ebnfDiagram-PWID7BFC-DS_6aWqL.js → ebnfDiagram-PWID7BFC-DJGpIpz_.js} +1 -1
  79. package/studio/web/assets/{editor-D8oDeCTL.js → editor-VUNQ-5Ye.js} +1 -1
  80. package/studio/web/assets/{erDiagram-SSCWMZ5O-DJNk6Fgw.js → erDiagram-RLTQ6QDP-CIfNlgkC.js} +1 -1
  81. package/studio/web/assets/eventmodeling-NTZA5JFV-CLxnp2CR.js +1 -0
  82. package/studio/web/assets/flowDiagram-HODETNUW-BIbhmz9f.js +1 -0
  83. package/studio/web/assets/{ganttDiagram-EL5Y4UJY-2pOExxMY.js → ganttDiagram-EL5Y4UJY-BxToTzzD.js} +1 -1
  84. package/studio/web/assets/{getAnalytics-D4LKGeVy.js → getAnalytics-GHK8ORfM.js} +1 -1
  85. package/studio/web/assets/{gitGraph-4MIJSDKK-CH5ZxwzF.js → gitGraph-4MIJSDKK-D2s2w8lE.js} +1 -1
  86. package/studio/web/assets/{gitGraphDiagram-WWUBYQGX-DVIsIhbO.js → gitGraphDiagram-WWUBYQGX-Dwntd4-x.js} +1 -1
  87. package/studio/web/assets/{html-u3vOg7LJ.js → html-CFqhtdfa.js} +1 -1
  88. package/studio/web/assets/{htmlMode-DyO31v-P.js → htmlMode-CpgOTfjz.js} +1 -1
  89. package/studio/web/assets/{index-Cxj2_wsl.css → index-7RMEgVG1.css} +1 -1
  90. package/studio/web/assets/index-ZQ1upnh7.js +824 -0
  91. package/studio/web/assets/index.lazy-HDafSRxx.js +2 -0
  92. package/studio/web/assets/{info-A6RAGUB7-CPQfTnaG.js → info-A6RAGUB7-DYjkvb0C.js} +1 -1
  93. package/studio/web/assets/{infoDiagram-RXCK75RN-DlwLYlwm.js → infoDiagram-27XIBGKW-Bnp1FJE5.js} +1 -1
  94. package/studio/web/assets/{ishikawaDiagram-5VMMS53U-BRXRp29U.js → ishikawaDiagram-5VMMS53U-D9Xh2r6X.js} +1 -1
  95. package/studio/web/assets/{javascript-CUvxOyTC.js → javascript-9l5JXJwc.js} +1 -1
  96. package/studio/web/assets/{journeyDiagram-EYS64GPL-B0ou8k0n.js → journeyDiagram-3NMN7TZE-CokIi6ll.js} +2 -2
  97. package/studio/web/assets/{jsonMode-f_IwbF3D.js → jsonMode-BJBH-iR0.js} +1 -1
  98. package/studio/web/assets/{kanban-definition-3QL26DDD-uYg7iYzp.js → kanban-definition-UXKFOSKX-CukSFJfX.js} +1 -1
  99. package/studio/web/assets/{languageServices-DXtZ6rEF.js → languageServices-0ZvgO63b.js} +1 -1
  100. package/studio/web/assets/{lspLanguageFeatures-B4pCF1zO.js → lspLanguageFeatures-sNVDopx3.js} +1 -1
  101. package/studio/web/assets/{mermaid-parser.core-Ck-fC8b7.js → mermaid-parser.core-BlEsOWNO.js} +3 -3
  102. package/studio/web/assets/{mermaid.core-CP8aNNYm.js → mermaid.core-BlkGaMIH.js} +5 -5
  103. package/studio/web/assets/{mindmap-definition-FBJOCRG2-CgTZ-rit.js → mindmap-definition-YA3MSWOX-IprMc_0j.js} +1 -1
  104. package/studio/web/assets/notifications-BukjVuVe.js +1 -0
  105. package/studio/web/assets/{notifications-D3tIQ4sg.js → notifications-DvBJIIO0.js} +1 -1
  106. package/studio/web/assets/{packet-AYTQ26CC-DEyoPtPb.js → packet-AYTQ26CC-Bi3V04Zi.js} +1 -1
  107. package/studio/web/assets/{pegDiagram-XKGWAZYB-DrD-7sD9.js → pegDiagram-XKGWAZYB-BNuPDLZY.js} +1 -1
  108. package/studio/web/assets/{pie-WAS4IAKB-wjj-EI1d.js → pie-WAS4IAKB-_6DoDbng.js} +1 -1
  109. package/studio/web/assets/{pieDiagram-E7YTZNPT-GntqDCzv.js → pieDiagram-E7YTZNPT-DqNb6Ht2.js} +1 -1
  110. package/studio/web/assets/{profile-DZWU7MgT.js → profile-B-jyYdzV.js} +1 -1
  111. package/studio/web/assets/{quadrantDiagram-AXDQQJYC-0UeqXQGd.js → quadrantDiagram-AXDQQJYC-BGH9E2YR.js} +1 -1
  112. package/studio/web/assets/{radar-RG4KPBEZ-DFSA5h7k.js → radar-RG4KPBEZ-DDdVczcL.js} +1 -1
  113. package/studio/web/assets/{railroad-74A4TZTK-CaOUG9wR.js → railroad-74A4TZTK-BJUP4Jds.js} +1 -1
  114. package/studio/web/assets/railroad-abnf-HS5TGJTU-Bm3L1L0h.js +1 -0
  115. package/studio/web/assets/railroad-ebnf-LZEXJU2U-CKzLGlkw.js +1 -0
  116. package/studio/web/assets/railroad-peg-WCYAUIDC-S8xLjslx.js +1 -0
  117. package/studio/web/assets/{railroadDiagram-O6MQD6OU-yHUELZaV.js → railroadDiagram-O6MQD6OU-DGTPh2KZ.js} +1 -1
  118. package/studio/web/assets/{regions-CkyurXzE.js → regions-GN6Mu5aH.js} +1 -1
  119. package/studio/web/assets/{register-BUyhWjBO.js → register-CU-gCfLP.js} +3 -3
  120. package/studio/web/assets/{requirementDiagram-EFPCY7ZU-DNEGFjuW.js → requirementDiagram-BXWQKSXE-BJnO6uLz.js} +1 -1
  121. package/studio/web/assets/{sankeyDiagram-P5KCCOFB-DpyAmSVR.js → sankeyDiagram-P5KCCOFB-0vSOdymH.js} +1 -1
  122. package/studio/web/assets/{sequenceDiagram-WJ2MYXX4-TyaT7xNk.js → sequenceDiagram-WJ2MYXX4-hETizDWE.js} +1 -1
  123. package/studio/web/assets/{setComponentFile-CeyKSZAa.js → setComponentFile-D7Fww0lC.js} +1 -1
  124. package/studio/web/assets/{setup-D2kn7cAA.js → setup-DG3Ws0Gg.js} +2 -2
  125. package/studio/web/assets/{stateDiagram-HBIQ2CUA-CeEdTArZ.js → stateDiagram-D77RDMKH-CdYQ_KtC.js} +1 -1
  126. package/studio/web/assets/stateDiagram-v2-MP3YSRHH-CdKuzQMT.js +1 -0
  127. package/studio/web/assets/status-koJd66XF.js +61 -0
  128. package/studio/web/assets/{swimlanes-XN3QIQJK-B54FmF46.js → swimlanes-42K2YHIH-B8cHIpU4.js} +1 -1
  129. package/studio/web/assets/swimlanesDiagram-VR7AAH4N-DmOSJwaH.js +8 -0
  130. package/studio/web/assets/{tabs-B_G5zscN.js → tabs-BrHu7gJi.js} +1 -1
  131. package/studio/web/assets/{timeline-definition-24CTP7MA-D-a9ujbo.js → timeline-definition-24CTP7MA-BJWYSXqF.js} +1 -1
  132. package/studio/web/assets/{toggleHighContrast-C0UW6rI2.js → toggleHighContrast-CGksgPgv.js} +1 -1
  133. package/studio/web/assets/{treeView-Q6P3EWNA-CrW_6JnS.js → treeView-Q6P3EWNA-qxe_v6CQ.js} +1 -1
  134. package/studio/web/assets/{treemap-WGGIJYW6-BxyYLdP_.js → treemap-WGGIJYW6-dDo97XXF.js} +1 -1
  135. package/studio/web/assets/{tsMode-DBC0zmDx.js → tsMode-D3T_5QzW.js} +1 -1
  136. package/studio/web/assets/{typescript-DApRQir3.js → typescript-BegjUvtB.js} +1 -1
  137. package/studio/web/assets/{useEntityRestURL-DB6JStU1.js → useEntityRestURL-BicFpx_N.js} +1 -1
  138. package/studio/web/assets/{useLocalStorage-Dtj1QS8_.js → useLocalStorage-BqMR3D8_.js} +1 -1
  139. package/studio/web/assets/vendor-core-c2JRRJpV.js +58 -0
  140. package/studio/web/assets/vendor-datadog-CLUcJXOo.js +6 -0
  141. package/studio/web/assets/{vendor-react-Dyj4O3HE.js → vendor-react-CJV_K1u4.js} +1 -1
  142. package/studio/web/assets/vendor-tanstack-DxzraizX.js +1 -0
  143. package/studio/web/assets/{vendor-ui-vhu-UHhF.js → vendor-ui-BUjK0h8a.js} +2 -2
  144. package/studio/web/assets/{vennDiagram-4TSXK5OY-Cy7s7Mpy.js → vennDiagram-4TSXK5OY-A3i-lCdl.js} +1 -1
  145. package/studio/web/assets/{wardley-WFR3VGLG-BeBL35g2.js → wardley-WFR3VGLG-B0ik-_6g.js} +1 -1
  146. package/studio/web/assets/{wardleyDiagram-VM6X3IG4-BylmIGSg.js → wardleyDiagram-VM6X3IG4-CjrkKWUR.js} +1 -1
  147. package/studio/web/assets/{workers-C0bFIedw.js → workers-Bmx4l_9B.js} +1 -1
  148. package/studio/web/assets/x-DIzaLEdK.js +1 -0
  149. package/studio/web/assets/{xml-HWd01lU-.js → xml-Bg40Rcra.js} +1 -1
  150. package/studio/web/assets/{xychartDiagram-S5SC5T6Z-CoKALMXr.js → xychartDiagram-S5SC5T6Z-Biok4GYV.js} +1 -1
  151. package/studio/web/assets/{yaml-CIH0Nt-h.js → yaml-DolDvfXE.js} +1 -1
  152. package/studio/web/index.html +14 -14
  153. package/studio/web/assets/Chat-JpO8EtUu.js +0 -2067
  154. package/studio/web/assets/FloatingChat-Bcj3xSZu.js +0 -23
  155. package/studio/web/assets/applications-ByqLRKyZ.js +0 -296
  156. package/studio/web/assets/architecture-7GRP2DOG-DNdx5tEU.js +0 -1
  157. package/studio/web/assets/authStore-qKmCZcaf.js +0 -3
  158. package/studio/web/assets/channel-DtCV8PTL.js +0 -1
  159. package/studio/web/assets/chunk-R7TYR2AO-Irip67yr.js +0 -10
  160. package/studio/web/assets/classDiagram-DTDB5LWJ-DbO_dCNE.js +0 -1
  161. package/studio/web/assets/classDiagram-v2-JRS7N3AN-DbO_dCNE.js +0 -1
  162. package/studio/web/assets/eventmodeling-NTZA5JFV-5jbe4A5P.js +0 -1
  163. package/studio/web/assets/flowDiagram-A5DVABFB-Dp9Ezlow.js +0 -1
  164. package/studio/web/assets/index-aSt5tY-L.js +0 -824
  165. package/studio/web/assets/index.lazy-B9jiPwT8.js +0 -2
  166. package/studio/web/assets/notifications-CUoYgU98.js +0 -1
  167. package/studio/web/assets/railroad-abnf-HS5TGJTU-Bc0Qi0WH.js +0 -1
  168. package/studio/web/assets/railroad-ebnf-LZEXJU2U-G8rVVZ2C.js +0 -1
  169. package/studio/web/assets/railroad-peg-WCYAUIDC-CrehKBhC.js +0 -1
  170. package/studio/web/assets/stateDiagram-v2-4QOOHH4V-D4tuw9Su.js +0 -1
  171. package/studio/web/assets/status-D7Xn5ePA.js +0 -61
  172. package/studio/web/assets/swimlanesDiagram-VK2B7HYN-XOhmNEvq.js +0 -8
  173. package/studio/web/assets/vendor-core-RCcadM3e.js +0 -73
  174. package/studio/web/assets/vendor-datadog-BRv-mOv1.js +0 -6
  175. package/studio/web/assets/vendor-tanstack-BiFWSB3W.js +0 -1
  176. package/studio/web/assets/x-B9o9hsep.js +0 -1
  177. /package/studio/web/assets/{sizeCapture-X5ZJPWSS-B0uUizjq.js → sizeCapture-INFHLROL-B0uUizjq.js} +0 -0
@@ -0,0 +1,2267 @@
1
+ import{r as e,t}from"./rolldown-runtime-hePW80VL.js";import{S as n,_ as r,a as i,b as a,c as o,d 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,x as b,y as x}from"./vendor-core-c2JRRJpV.js";import{i as S,t as C}from"./button-BIsUKRZq.js";import{a as ee}from"./vendor-datadog-CLUcJXOo.js";import{r as te}from"./vendor-react-CJV_K1u4.js";import{L as ne,k as re,q as ie,z as ae}from"./vendor-tanstack-DxzraizX.js";import{n as oe}from"./setSessionStorage-B0bf71m4.js";import{It as se}from"./vendor-ui-BUjK0h8a.js";import{t as w}from"./createLucideIcon-CzW9508A.js";import{n as ce,r as le,t as ue}from"./chevron-up-DtKGqDn3.js";import{c as de,f as fe,g as pe,h as me,i as he,l as ge,m as _e,p as ve,r as ye,t as be,u as xe}from"./setComponentFile-D7Fww0lC.js";import{t as Se}from"./x-DIzaLEdK.js";import{n as Ce}from"./setLocalStorage-D_kflv4U.js";import{t as we}from"./useLocalStorage-BqMR3D8_.js";import{An as Te,Dr as Ee,En as De,H as Oe,It as ke,Mr as Ae,Mt as je,Nt as Me,Or as Ne,Pt as Pe,Tr as Fe,V as Ie,Z as Le,br as Re,c as ze,cr as Be,d as Ve,f as He,fr as Ue,ft as We,hr as Ge,in as Ke,m as qe,or as Je,ur as Ye,ut as Xe,yr as Ze,yt as Qe}from"./index-ZQ1upnh7.js";import{t as $e}from"./useEntityRestURL-BicFpx_N.js";import{n as et,t as tt}from"./FloatingChat-h9BJo6YG.js";import{n as nt}from"./getAnalytics-GHK8ORfM.js";var rt=w(`between-horizontal-start`,[[`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`}]]),it=w(`book`,[[`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`}]]),at=w(`chart-area`,[[`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`}]]),ot=w(`circle-x`,[[`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`}]]),st=w(`file-pen`,[[`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`}]]),ct=w(`logs`,[[`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`}]]),lt=w(`message-square-heart`,[[`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`}]]),ut=w(`send`,[[`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`}]]),dt=w(`wrench`,[[`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`}]]);async function ft(){await S.delete(`/Chat/Messages/`)}var T=e(ee(),1),E=te();function pt({setMessages:e}){let[t,n]=(0,T.useState)(!1),r=(0,T.useCallback)(async()=>{if(!t){n(!0);try{await ft(),e([])}catch(e){console.error(`Failed to clear chat:`,e)}finally{n(!1)}}},[t,e]);return(0,E.jsxs)(`button`,{type:`button`,className:`clear-chat-button gap-1`,onClick:r,disabled:t,title:`Clear chat`,children:[t?(0,E.jsx)(Ze,{className:`animate-spin`,size:18}):(0,E.jsx)(Ye,{size:18}),`Clear`]})}async function mt(){let{data:e}=await S.get(`/Chat/Messages/`);return e}var ht=`vercel.ai.error`,gt=Symbol.for(ht),_t,vt,D=class e extends (vt=Error,_t=gt,vt){constructor({name:e,message:t,cause:n}){super(t),this[_t]=!0,this.name=e,this.cause=n}static isInstance(t){return e.hasMarker(t,ht)}static hasMarker(e,t){let n=Symbol.for(t);return typeof e==`object`&&!!e&&n in e&&typeof e[n]==`boolean`&&e[n]===!0}};function yt(e){return e==null?`unknown error`:typeof e==`string`?e:e instanceof Error?e.toString():JSON.stringify(e)}var bt=`AI_InvalidArgumentError`,xt=`vercel.ai.error.${bt}`,St=Symbol.for(xt),Ct,wt,Tt=class extends (wt=D,Ct=St,wt){constructor({message:e,cause:t,argument:n}){super({name:bt,message:e,cause:t}),this[Ct]=!0,this.argument=n}static isInstance(e){return D.hasMarker(e,xt)}},Et=`AI_JSONParseError`,Dt=`vercel.ai.error.${Et}`,Ot=Symbol.for(Dt),kt,At,jt=class extends (At=D,kt=Ot,At){constructor({text:e,cause:t}){super({name:Et,message:`JSON parsing failed: Text: ${e}.
2
+ Error message: ${yt(t)}`,cause:t}),this[kt]=!0,this.text=e}static isInstance(e){return D.hasMarker(e,Dt)}},Mt=`AI_TypeValidationError`,Nt=`vercel.ai.error.${Mt}`,Pt=Symbol.for(Nt),Ft,It,O=class e extends (It=D,Ft=Pt,It){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:Mt,message:`${r}: Value: ${JSON.stringify(e)}.
3
+ Error message: ${yt(t)}`,cause:t}),this[Ft]=!0,this.value=e,this.context=n}static isInstance(e){return D.hasMarker(e,Nt)}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})}},Lt=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}},Rt=10,zt=13,k=32;function Bt(e){}function Vt(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=Bt,onError:n=Bt,onRetry:r=Bt,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(`
4
+ `)===-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 Lt(`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(`
5
+ `,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(`
6
+ `,n);continue}let i=e.charCodeAt(n);if(Ht(e,n,i)){let i=e.charCodeAt(n+5)===k?n+6:n+5,a=e.slice(i,r);if(d===0&&e.charCodeAt(r+1)===Rt){t({id:l,event:f,data:a}),l=void 0,u=``,f=void 0,n=r+2,r=e.indexOf(`
7
+ `,n);continue}u=d===0?a:`${u}
8
+ ${a}`,d++}else Ut(e,n,i)?f=e.slice(e.charCodeAt(n+6)===k?n+7:n+6,r)||void 0:_(e,n,r);n=r+1,r=e.indexOf(`
9
+ `,n)}return e.slice(n)}for(;n<e.length;){let t=e.indexOf(`\r`,n),r=e.indexOf(`
10
+ `,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)===zt&&e.charCodeAt(n)===Rt&&n++}return e.slice(n)}function _(e,t,n){if(t===n){y();return}let r=e.charCodeAt(t);if(Ht(e,t,r)){let r=e.charCodeAt(t+5)===k?t+6:t+5,i=e.slice(r,n);u=d===0?i:`${u}
11
+ ${i}`,d++;return}if(Ut(e,t,r)){f=e.slice(e.charCodeAt(t+6)===k?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)===k?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)===k?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)===k?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}
12
+ ${t}`,d++;break;case`id`:t.includes(`\0`)||(l=t);break;case`retry`:/^\d+$/.test(t)?r(parseInt(t,10)):n(new Lt(`Invalid \`retry\` value: "${t}"`,{type:`invalid-retry`,value:t,line:i}));break;default:n(new Lt(`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 Ht(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 Ut(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 Wt=class extends TransformStream{constructor({onError:e,onRetry:t,onComment:n,maxBufferSize:r}={}){let i;super({start(a){i=Vt({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:Gt,atob:Kt}=globalThis;function A(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 qt=globalThis.fetch;Jt(qt);function Jt(e){if(typeof e!=`function`)return!1;let t=Function.prototype.toString.call(e);return t.includes(`internal/deps/undici`)||t.includes(`lazy loading of undici`)}var j=({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 Tt({argument:`separator`,message:`The separator "${r}" must not be part of the alphabet "${n}".`});return()=>`${e}${r}${i()}`},Yt=j();function Xt(e){return globalThis.Buffer?.isBuffer(e)??!1}var Zt=/"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*:/,Qt=/"(?: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 $t(e){let t=JSON.parse(e);return typeof t!=`object`||!t||Zt.test(e)===!1&&Qt.test(e)===!1?t:en(t)}function en(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 tn(e){let{stackTraceLimit:t}=Error;try{Error.stackTraceLimit=0}catch{return $t(e)}try{return $t(e)}finally{Error.stackTraceLimit=t}}function nn(e){if(e.type===`object`||Array.isArray(e.type)&&e.type.includes(`object`)){let{additionalProperties:t}=e;e.additionalProperties=t!=null&&typeof t!=`boolean`&&M(t);let{properties:n}=e;if(n!=null)for(let e of Object.keys(n))n[e]=M(n[e])}e.items!=null&&(e.items=Array.isArray(e.items)?e.items.map(M):M(e.items)),e.anyOf!=null&&(e.anyOf=e.anyOf.map(M)),e.allOf!=null&&(e.allOf=e.allOf.map(M)),e.oneOf!=null&&(e.oneOf=e.oneOf.map(M));let{definitions:t}=e;if(t!=null)for(let e of Object.keys(t))t[e]=M(t[e]);return e}function M(e){return typeof e==`boolean`?e:nn(e)}var rn=Symbol(`Let zodToJsonSchema decide on which parser to use`),an={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`},on=e=>typeof e==`string`?{...an,name:e}:{...an,...e};function N(){return{}}function sn(e,t){let n={type:`array`};return e.type?._def&&e.type?._def?.typeName!==`ZodAny`&&(n.items=L(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 cn(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 ln(){return{type:`boolean`}}function un(e,t){return L(e.type._def,t)}var dn=(e,t)=>L(e.innerType._def,t);function fn(e,t,n){let r=n??t.dateStrategy;if(Array.isArray(r))return{anyOf:r.map(n=>fn(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 pn(e)}}var pn=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 mn(e,t){return{...L(e.innerType._def,t),default:e.defaultValue()}}function hn(e,t){return t.effectStrategy===`input`?L(e.schema._def,t):N()}function gn(e){return{type:`string`,enum:Array.from(e.values)}}var _n=e=>`type`in e&&e.type===`string`?!1:`allOf`in e;function vn(e,t){let n=[L(e.left._def,{...t,currentPath:[...t.currentPath,`allOf`,`0`]}),L(e.right._def,{...t,currentPath:[...t.currentPath,`allOf`,`1`]})].filter(e=>!!e),r=[];return n.forEach(e=>{if(_n(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 yn(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 bn=void 0,P={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:()=>(bn===void 0&&(bn=RegExp(`^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`,`u`)),bn),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 xn(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`:F(n,`email`,r.message,t);break;case`format:idn-email`:F(n,`idn-email`,r.message,t);break;case`pattern:zod`:I(n,P.email,r.message,t)}break;case`url`:F(n,`uri`,r.message,t);break;case`uuid`:F(n,`uuid`,r.message,t);break;case`regex`:I(n,r.regex,r.message,t);break;case`cuid`:I(n,P.cuid,r.message,t);break;case`cuid2`:I(n,P.cuid2,r.message,t);break;case`startsWith`:I(n,RegExp(`^${Sn(r.value,t)}`),r.message,t);break;case`endsWith`:I(n,RegExp(`${Sn(r.value,t)}$`),r.message,t);break;case`datetime`:F(n,`date-time`,r.message,t);break;case`date`:F(n,`date`,r.message,t);break;case`time`:F(n,`time`,r.message,t);break;case`duration`:F(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`:I(n,RegExp(Sn(r.value,t)),r.message,t);break;case`ip`:r.version!==`v6`&&F(n,`ipv4`,r.message,t),r.version!==`v4`&&F(n,`ipv6`,r.message,t);break;case`base64url`:I(n,P.base64url,r.message,t);break;case`jwt`:I(n,P.jwt,r.message,t);break;case`cidr`:r.version!==`v6`&&I(n,P.ipv4Cidr,r.message,t),r.version!==`v4`&&I(n,P.ipv6Cidr,r.message,t);break;case`emoji`:I(n,P.emoji(),r.message,t);break;case`ulid`:I(n,P.ulid,r.message,t);break;case`base64`:switch(t.base64Strategy){case`format:binary`:F(n,`binary`,r.message,t);break;case`contentEncoding:base64`:n.contentEncoding=`base64`;break;case`pattern:zod`:I(n,P.base64,r.message,t)}break;case`nanoid`:I(n,P.nanoid,r.message,t)}return n}function Sn(e,t){return t.patternStrategy===`escape`?wn(e):e}var Cn=new Set(`ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789`);function wn(e){let t=``;for(let n=0;n<e.length;n++)Cn.has(e[n])||(t+=`\\`),t+=e[n];return t}function F(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 I(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:Tn(t,r),...n&&r.errorMessages&&{errorMessage:{pattern:n}}})):e.pattern=Tn(t,r)}function Tn(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
13
+ ]))`;continue}if(r[e]===`$`){i+=`($|(?=[\r
14
+ ]))`;continue}}if(n.s&&r[e]===`.`){i+=o?`${r[e]}\r
15
+ `:`[${r[e]}\r
16
+ ]`;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 En(e,t){let n={type:`object`,additionalProperties:L(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}=xn(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}=un(e.keyType._def,t);return{...n,propertyNames:i}}return n}function Dn(e,t){return t.mapStrategy===`record`?En(e,t):{type:`array`,maxItems:125,items:{type:`array`,items:[L(e.keyType._def,{...t,currentPath:[...t.currentPath,`items`,`items`,`0`]})||N(),L(e.valueType._def,{...t,currentPath:[...t.currentPath,`items`,`items`,`1`]})||N()],minItems:2,maxItems:2}}}function On(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 kn(){return{not:N()}}function An(){return{type:`null`}}var jn={ZodString:`string`,ZodNumber:`number`,ZodBigInt:`integer`,ZodBoolean:`boolean`,ZodNull:`null`};function Mn(e,t){let n=e.options instanceof Map?Array.from(e.options.values()):e.options;if(n.every(e=>e._def.typeName in jn&&(!e._def.checks||!e._def.checks.length))){let e=n.reduce((e,t)=>{let n=jn[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 Nn(e,t)}var Nn=(e,t)=>{let n=(e.options instanceof Map?Array.from(e.options.values()):e.options).map((e,n)=>L(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 Pn(e,t){if([`ZodString`,`ZodNumber`,`ZodBigInt`,`ZodBoolean`,`ZodNull`].includes(e.innerType._def.typeName)&&(!e.innerType._def.checks||!e.innerType._def.checks.length))return{type:[jn[e.innerType._def.typeName],`null`]};let n=L(e.innerType._def,{...t,currentPath:[...t.currentPath,`anyOf`,`0`]});return n&&{anyOf:[n,{type:`null`}]}}function Fn(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 In(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=Rn(a),s=L(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=Ln(e,t);return a!==void 0&&(n.additionalProperties=a),n}function Ln(e,t){if(e.catchall._def.typeName!==`ZodNever`)return L(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 Rn(e){try{return e.isOptional()}catch{return!0}}var zn=(e,t)=>{if(t.currentPath.toString()===t.propertyPath?.toString())return L(e.innerType._def,t);let n=L(e.innerType._def,{...t,currentPath:[...t.currentPath,`anyOf`,`1`]});return n?{anyOf:[{not:N()},n]}:N()},Bn=(e,t)=>{if(t.pipeStrategy===`input`)return L(e.in._def,t);if(t.pipeStrategy===`output`)return L(e.out._def,t);let n=L(e.in._def,{...t,currentPath:[...t.currentPath,`allOf`,`0`]});return{allOf:[n,L(e.out._def,{...t,currentPath:[...t.currentPath,`allOf`,n?`1`:`0`]})].filter(e=>e!==void 0)}};function Vn(e,t){return L(e.type._def,t)}function Hn(e,t){let n={type:`array`,uniqueItems:!0,items:L(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 Un(e,t){return e.rest?{type:`array`,minItems:e.items.length,items:e.items.map((e,n)=>L(e._def,{...t,currentPath:[...t.currentPath,`items`,`${n}`]})).reduce((e,t)=>t===void 0?e:[...e,t],[]),additionalItems:L(e.rest._def,{...t,currentPath:[...t.currentPath,`additionalItems`]})}:{type:`array`,minItems:e.items.length,maxItems:e.items.length,items:e.items.map((e,n)=>L(e._def,{...t,currentPath:[...t.currentPath,`items`,`${n}`]})).reduce((e,t)=>t===void 0?e:[...e,t],[])}}function Wn(){return{not:N()}}function Gn(){return N()}var Kn=(e,t)=>L(e.innerType._def,t),qn=(e,t,n)=>{switch(t){case`ZodString`:return xn(e,n);case`ZodNumber`:return Fn(e);case`ZodObject`:return In(e,n);case`ZodBigInt`:return cn(e);case`ZodBoolean`:return ln();case`ZodDate`:return fn(e,n);case`ZodUndefined`:return Wn();case`ZodNull`:return An();case`ZodArray`:return sn(e,n);case`ZodUnion`:case`ZodDiscriminatedUnion`:return Mn(e,n);case`ZodIntersection`:return vn(e,n);case`ZodTuple`:return Un(e,n);case`ZodRecord`:return En(e,n);case`ZodLiteral`:return yn(e);case`ZodEnum`:return gn(e);case`ZodNativeEnum`:return On(e);case`ZodNullable`:return Pn(e,n);case`ZodOptional`:return zn(e,n);case`ZodMap`:return Dn(e,n);case`ZodSet`:return Hn(e,n);case`ZodLazy`:return()=>e.getter()._def;case`ZodPromise`:return Vn(e,n);case`ZodNaN`:case`ZodNever`:return kn();case`ZodEffects`:return hn(e,n);case`ZodAny`:return N();case`ZodUnknown`:return Gn();case`ZodDefault`:return mn(e,n);case`ZodBranded`:return un(e,n);case`ZodReadonly`:return Kn(e,n);case`ZodCatch`:return dn(e,n);case`ZodPipeline`:return Bn(e,n);case`ZodFunction`:case`ZodVoid`:case`ZodSymbol`:return;default:return(e=>void 0)(t)}},Jn=(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 L(e,t,n=!1){let r=t.seen.get(e);if(t.override){let i=t.override?.call(t,e,t,r,n);if(i!==rn)return i}if(r&&!n){let e=Yn(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=qn(e,e.typeName,t),o=typeof a==`function`?L(a(),t):a;if(o&&Xn(e,t,o),t.postProcess){let n=t.postProcess(o,e,t);return i.jsonSchema=o,n}return i.jsonSchema=o,o}var Yn=(e,t)=>{switch(t.$refStrategy){case`root`:return{$ref:e.path.join(`/`)};case`relative`:return{$ref:Jn(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`),N()):t.$refStrategy===`seen`?N():void 0}},Xn=(e,t,n)=>(e.description&&(n.description=e.description),n),Zn=e=>{let t=on(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}]))}},Qn=(e,t)=>{let n=Zn(t),r=typeof t==`object`&&t.definitions?Object.entries(t.definitions).reduce((e,[t,r])=>({...e,[t]:L(r._def,{...n,currentPath:[...n.basePath,n.definitionPath,t]},!0)??N()}),{}):void 0,i=typeof t==`string`?t:t?.nameStrategy===`title`?void 0:t?.name,a=L(e._def,i===void 0?n:{...n,currentPath:[...n.basePath,n.definitionPath,i]},!1)??N(),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},$n=Symbol.for(`vercel.ai.schema`);function er(e){let t;return()=>(t??=e(),t)}function tr(e,{validate:t}={}){return{[$n]:!0,_type:void 0,get jsonSchema(){return typeof e==`function`&&(e=e()),e},validate:t}}function nr(e){return typeof e==`object`&&!!e&&$n in e&&e[$n]===!0&&`jsonSchema`in e&&`validate`in e}function rr(e){return e==null?tr({type:`object`,properties:{},additionalProperties:!1}):nr(e)?e:`~standard`in e?e[`~standard`].vendor===`zod`?lr(e):ir(e):e()}function ir(e){return tr(()=>{if(!ar(e))throw Error(`Standard schema vendor '${e[`~standard`].vendor}' does not support JSON Schema conversion.`);return nn(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 O({value:t,cause:n.issues})}}})}function ar(e){return e[`~standard`].jsonSchema!=null}function or(e,t){let n=t?.useReferences??!1;return tr(()=>Qn(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 sr(e,t){let r=t?.useReferences??!1;return tr(()=>nn(n(e,{target:`draft-7`,io:`input`,reused:r?`ref`:`inline`})),{validate:async t=>{let n=await b(e,t);return n.success?{success:!0,value:n.data}:{success:!1,error:n.error}}})}function cr(e){return`_zod`in e}function lr(e,t){return cr(e)?sr(e,t):or(e,t)}async function ur({value:e,schema:t,context:n}){let r=await dr({value:e,schema:t,context:n});if(!r.success)throw O.wrap({value:e,cause:r.error,context:n});return r.value}async function dr({value:e,schema:t,context:n}){let r=rr(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:O.wrap({value:e,cause:t.error,context:n}),rawValue:e}}catch(t){return{success:!1,error:O.wrap({value:e,cause:t,context:n}),rawValue:e}}}async function R({text:e,schema:t}){try{let n=tn(e);return t==null?{success:!0,value:n,rawValue:n}:await dr({value:n,schema:t})}catch(t){return{success:!1,error:jt.isInstance(t)?t:new jt({text:e,cause:t}),rawValue:void 0}}}function fr({stream:e,schema:t}){return e.pipeThrough(new TextDecoderStream).pipeThrough(new Wt).pipeThrough(new TransformStream({async transform({data:e},n){e!==`[DONE]`&&n.enqueue(await R({text:e,schema:t}))}}))}async function z(e){return typeof e==`function`&&(e=e()),Promise.resolve(e)}new TextDecoder;var pr=Object.defineProperty,mr=(e,t)=>{for(var n in t)pr(e,n,{get:t[n],enumerable:!0})},hr=`AI_InvalidArgumentError`,gr=`vercel.ai.error.${hr}`,_r=Symbol.for(gr),vr,yr,br=class extends (yr=D,vr=_r,yr){constructor({parameter:e,value:t,message:n}){super({name:hr,message:`Invalid argument for parameter ${e}: ${n}`}),this[vr]=!0,this.parameter=e,this.value=t}static isInstance(e){return D.hasMarker(e,gr)}},xr=`AI_NoObjectGeneratedError`,Sr=`vercel.ai.error.${xr}`,Cr=Symbol.for(Sr),wr,Tr,B=class extends (Tr=D,wr=Cr,Tr){constructor({message:e=`No object generated.`,cause:t,text:n,response:r,usage:i,finishReason:a}){super({name:xr,message:e,cause:t}),this[wr]=!0,this.text=n,this.response=r,this.usage=i,this.finishReason=a}static isInstance(e){return D.hasMarker(e,Sr)}},Er=`AI_UIMessageStreamError`,Dr=`vercel.ai.error.${Er}`,Or=Symbol.for(Dr),kr,Ar,V=class extends (Ar=D,kr=Or,Ar){constructor({chunkType:e,chunkId:t,message:n}){super({name:Er,message:n}),this[kr]=!0,this.chunkType=e,this.chunkId=t}static isInstance(e){return D.hasMarker(e,Dr)}};function jr(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?jr(a,i):i}return n}var H={array:m,boolean:_,custom:o,discriminatedUnion:d,enum:v,instanceof:p,lazy:s,literal:c,looseObject:h,never:f,null:g,number:u,object:l,record:r,string:y,union:x,unknown:a},U=H.lazy(()=>H.union([H.null(),H.string(),H.number(),H.boolean(),H.record(H.string(),U.optional()),H.array(U)])),W=H.record(H.string(),H.record(H.string(),U.optional())),Mr=H.union([H.string(),H.instanceof(Uint8Array),H.instanceof(ArrayBuffer),H.custom(Xt,{message:`Must be a Buffer`})]),Nr=H.record(H.string(),H.string()),Pr=H.object({type:H.literal(`text`),text:H.string(),providerOptions:W.optional()}),Fr=H.object({type:H.literal(`image`),image:H.union([Mr,H.instanceof(URL),Nr]),mediaType:H.string().optional(),providerOptions:W.optional()}),Ir=H.discriminatedUnion(`type`,[H.object({type:H.literal(`data`),data:Mr}),H.object({type:H.literal(`url`),url:H.instanceof(URL)}),H.object({type:H.literal(`reference`),reference:Nr}),H.object({type:H.literal(`text`),text:H.string()})]),Lr=H.discriminatedUnion(`type`,[H.object({type:H.literal(`data`),data:Mr}),H.object({type:H.literal(`url`),url:H.instanceof(URL)})]),Rr=H.object({type:H.literal(`file`),data:H.union([Ir,Mr,H.instanceof(URL),Nr]),filename:H.string().optional(),mediaType:H.string(),providerOptions:W.optional()}),zr=H.object({type:H.literal(`reasoning`),text:H.string(),providerOptions:W.optional()}),Br=H.object({type:H.literal(`custom`),kind:H.string().transform(e=>e),providerOptions:W.optional()}),Vr=H.object({type:H.literal(`reasoning-file`),data:H.union([Lr,Mr,H.instanceof(URL)]),mediaType:H.string(),providerOptions:W.optional()}),Hr=H.object({type:H.literal(`tool-call`),toolCallId:H.string(),toolName:H.string(),input:H.unknown(),providerOptions:W.optional(),providerExecuted:H.boolean().optional()}),Ur=H.discriminatedUnion(`type`,[H.object({type:H.literal(`text`),value:H.string(),providerOptions:W.optional()}),H.object({type:H.literal(`json`),value:U,providerOptions:W.optional()}),H.object({type:H.literal(`execution-denied`),reason:H.string().optional(),providerOptions:W.optional()}),H.object({type:H.literal(`error-text`),value:H.string(),providerOptions:W.optional()}),H.object({type:H.literal(`error-json`),value:U,providerOptions:W.optional()}),H.object({type:H.literal(`content`),value:H.array(H.union([H.object({type:H.literal(`text`),text:H.string(),providerOptions:W.optional()}),H.object({type:H.literal(`file`),data:Ir,mediaType:H.string(),filename:H.string().optional(),providerOptions:W.optional()}),H.object({type:H.literal(`file-data`),data:H.string(),mediaType:H.string(),filename:H.string().optional(),providerOptions:W.optional()}),H.object({type:H.literal(`file-url`),url:H.string(),mediaType:H.string().optional(),providerOptions:W.optional()}),H.object({type:H.literal(`file-id`),fileId:H.union([H.string(),H.record(H.string(),H.string())]),providerOptions:W.optional()}),H.object({type:H.literal(`file-reference`),providerReference:H.record(H.string(),H.string()),providerOptions:W.optional()}),H.object({type:H.literal(`image-data`),data:H.string(),mediaType:H.string(),providerOptions:W.optional()}),H.object({type:H.literal(`image-url`),url:H.string(),providerOptions:W.optional()}),H.object({type:H.literal(`image-file-id`),fileId:H.union([H.string(),H.record(H.string(),H.string())]),providerOptions:W.optional()}),H.object({type:H.literal(`image-file-reference`),providerReference:H.record(H.string(),H.string()),providerOptions:W.optional()}),H.object({type:H.literal(`custom`),providerOptions:W.optional()})]))})]),Wr=H.object({type:H.literal(`tool-result`),toolCallId:H.string(),toolName:H.string(),output:Ur,providerOptions:W.optional()}),Gr=H.object({type:H.literal(`tool-approval-request`),approvalId:H.string(),toolCallId:H.string(),reason:H.string().optional()}),Kr=H.object({type:H.literal(`tool-approval-response`),approvalId:H.string(),approved:H.boolean(),reason:H.string().optional()}),qr=H.object({role:H.literal(`system`),content:H.string(),providerOptions:W.optional()}),Jr=H.object({role:H.literal(`user`),content:H.union([H.string(),H.array(H.union([Pr,Fr,Rr]))]),providerOptions:W.optional()}),Yr=H.object({role:H.literal(`assistant`),content:H.union([H.string(),H.array(H.union([Pr,Br,Rr,zr,Vr,Hr,Wr,Gr]))]),providerOptions:W.optional()}),Xr=H.object({role:H.literal(`tool`),content:H.array(H.union([Wr,Kr])),providerOptions:W.optional()});H.union([qr,Jr,Yr,Xr]),mr({},{array:()=>ti,choice:()=>ii,json:()=>ai,object:()=>ei,text:()=>$r});function Zr(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 Qr(e){if(e===void 0)return{value:void 0,state:`undefined-input`};let t=await R({text:e});return t.success?{value:t.value,state:`successful-parse`}:(t=await R({text:Zr(e)}),t.success?{value:t.value,state:`repaired-parse`}:{value:void 0,state:`failed-parse`})}var $r=()=>({name:`text`,responseFormat:Promise.resolve({type:`text`}),async parseCompleteOutput({text:e}){return e},async parsePartialOutput({text:e}){return{partial:e}},createElementStreamTransform(){}}),ei=({schema:e,name:t,description:n})=>{let r=rr(e);return{name:`object`,responseFormat:z(r.jsonSchema).then(e=>({type:`json`,schema:e,...t!=null&&{name:t},...n!=null&&{description:n}})),async parseCompleteOutput({text:e},t){let n=await R({text:e});if(!n.success)throw new B({message:`No object generated: could not parse the response.`,cause:n.error,text:e,response:t.response,usage:t.usage,finishReason:t.finishReason});let i=await dr({value:n.value,schema:r});if(!i.success)throw new B({message:`No object generated: response did not match schema.`,cause:i.error,text:e,response:t.response,usage:t.usage,finishReason:t.finishReason});return i.value},async parsePartialOutput({text:e}){let t=await Qr(e);switch(t.state){case`failed-parse`:case`undefined-input`:return;case`repaired-parse`:case`successful-parse`:return{partial:t.value}}},createElementStreamTransform(){}}},ti=({element:e,minItems:t,maxItems:n,name:r,description:i})=>{if(ni({name:`minItems`,value:t}),ni({name:`maxItems`,value:n}),t!=null&&n!=null&&t>n)throw new br({parameter:`minItems`,value:t,message:`minItems must be less than or equal to maxItems`});let a=rr(e);return{name:`array`,responseFormat:z(a.jsonSchema).then(e=>{let{$schema:a,definitions:o,$defs:s,...c}=e;return{type:`json`,schema:{$schema:`http://json-schema.org/draft-07/schema#`,...o!=null&&{definitions:o},...s!=null&&{$defs:s},type:`object`,properties:{elements:{type:`array`,items:c,...t!=null&&{minItems:t},...n!=null&&{maxItems:n}}},required:[`elements`],additionalProperties:!1},...r!=null&&{name:r},...i!=null&&{description:i}}}),async parseCompleteOutput({text:e},r){let i=await R({text:e});if(!i.success)throw new B({message:`No object generated: could not parse the response.`,cause:i.error,text:e,response:r.response,usage:r.usage,finishReason:r.finishReason});let o=i.value;if(typeof o!=`object`||!o||!(`elements`in o)||!Array.isArray(o.elements))throw new B({message:`No object generated: response did not match schema.`,cause:new O({value:o,cause:`response must be an object with an elements array`}),text:e,response:r.response,usage:r.usage,finishReason:r.finishReason});let s=ri({value:o.elements,minItems:t,maxItems:n});if(s!=null)throw new B({message:`No object generated: response did not match schema.`,cause:s,text:e,response:r.response,usage:r.usage,finishReason:r.finishReason});let c=[];for(let t of o.elements){let n=await dr({value:t,schema:a});if(!n.success)throw new B({message:`No object generated: response did not match schema.`,cause:n.error,text:e,response:r.response,usage:r.usage,finishReason:r.finishReason});c.push(n.value)}return c},async parsePartialOutput({text:e}){let t=await Qr(e);switch(t.state){case`failed-parse`:case`undefined-input`:return;case`repaired-parse`:case`successful-parse`:{let e=t.value;if(typeof e!=`object`||!e||!(`elements`in e)||!Array.isArray(e.elements))return;let n=t.state===`repaired-parse`&&e.elements.length>0?e.elements.slice(0,-1):e.elements,r=[];for(let e of n){let t=await dr({value:e,schema:a});t.success&&r.push(t.value)}return{partial:r}}}},createElementStreamTransform(){let e=0;return new TransformStream({transform({partialOutput:t},r){if(t!=null)for(;e<t.length;e++){if(n!=null&&e>=n){r.error(ri({value:t,maxItems:n}));return}r.enqueue(t[e])}}})}}};function ni({name:e,value:t}){if(t!=null){if(!Number.isInteger(t))throw new br({parameter:e,value:t,message:`${e} must be an integer`});if(t<0)throw new br({parameter:e,value:t,message:`${e} must be greater than or equal to 0`})}}function ri({value:e,minItems:t,maxItems:n}){if(t!=null&&e.length<t)return new O({value:e,cause:`elements array must contain at least ${t} items`});if(n!=null&&e.length>n)return new O({value:e,cause:`elements array must contain at most ${n} items`})}var ii=({options:e,name:t,description:n})=>({name:`choice`,responseFormat:Promise.resolve({type:`json`,schema:{$schema:`http://json-schema.org/draft-07/schema#`,type:`object`,properties:{result:{type:`string`,enum:e}},required:[`result`],additionalProperties:!1},...t!=null&&{name:t},...n!=null&&{description:n}}),async parseCompleteOutput({text:t},n){let r=await R({text:t});if(!r.success)throw new B({message:`No object generated: could not parse the response.`,cause:r.error,text:t,response:n.response,usage:n.usage,finishReason:n.finishReason});let i=r.value;if(typeof i!=`object`||!i||!(`result`in i)||typeof i.result!=`string`||!e.includes(i.result))throw new B({message:`No object generated: response did not match schema.`,cause:new O({value:i,cause:`response must be an object that contains a choice value.`}),text:t,response:n.response,usage:n.usage,finishReason:n.finishReason});return i.result},async parsePartialOutput({text:t}){let n=await Qr(t);switch(n.state){case`failed-parse`:case`undefined-input`:return;case`repaired-parse`:case`successful-parse`:{let t=n.value;if(typeof t!=`object`||!t||!(`result`in t)||typeof t.result!=`string`)return;let r=e.filter(e=>e.startsWith(t.result));return n.state===`successful-parse`?r.includes(t.result)?{partial:t.result}:void 0:r.length===1?{partial:r[0]}:void 0}}},createElementStreamTransform(){}}),ai=({name:e,description:t}={})=>({name:`json`,responseFormat:Promise.resolve({type:`json`,...e!=null&&{name:e},...t!=null&&{description:t}}),async parseCompleteOutput({text:e},t){let n=await R({text:e});if(!n.success)throw new B({message:`No object generated: could not parse the response.`,cause:n.error,text:e,response:t.response,usage:t.usage,finishReason:t.finishReason});return n.value},async parsePartialOutput({text:e}){let t=await Qr(e);switch(t.state){case`failed-parse`:case`undefined-input`:return;case`repaired-parse`:case`successful-parse`:return t.value===void 0?void 0:{partial:t.value}}},createElementStreamTransform(){}});new TextEncoder,new TextEncoder,j({prefix:`aitxt`,size:24}),j({prefix:`call`,size:24}),TransformStream;var G=H.record(H.string(),U.optional()),oi=er(()=>lr(H.union([H.looseObject({type:H.literal(`text-start`),id:H.string(),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`text-delta`),id:H.string(),delta:H.string(),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`text-end`),id:H.string(),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`error`),errorText:H.string()}),H.looseObject({type:H.literal(`tool-input-start`),toolCallId:H.string(),toolName:H.string(),providerExecuted:H.boolean().optional(),providerMetadata:W.optional(),toolMetadata:G.optional(),dynamic:H.boolean().optional(),title:H.string().optional()}),H.looseObject({type:H.literal(`tool-input-delta`),toolCallId:H.string(),inputTextDelta:H.string()}),H.looseObject({type:H.literal(`tool-input-available`),toolCallId:H.string(),toolName:H.string(),input:H.unknown(),providerExecuted:H.boolean().optional(),providerMetadata:W.optional(),toolMetadata:G.optional(),dynamic:H.boolean().optional(),title:H.string().optional()}),H.looseObject({type:H.literal(`tool-input-error`),toolCallId:H.string(),toolName:H.string(),input:H.unknown(),providerExecuted:H.boolean().optional(),providerMetadata:W.optional(),toolMetadata:G.optional(),dynamic:H.boolean().optional(),errorText:H.string(),title:H.string().optional()}),H.looseObject({type:H.literal(`tool-approval-request`),approvalId:H.string(),toolCallId:H.string(),approvalDescriptor:H.unknown().optional(),reason:H.string().optional(),isAutomatic:H.boolean().optional(),signature:H.string().optional()}),H.looseObject({type:H.literal(`tool-approval-response`),approvalId:H.string(),approved:H.boolean(),reason:H.string().optional(),providerExecuted:H.boolean().optional(),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`tool-output-available`),toolCallId:H.string(),output:H.unknown(),providerExecuted:H.boolean().optional(),providerMetadata:W.optional(),toolMetadata:G.optional(),dynamic:H.boolean().optional(),preliminary:H.boolean().optional()}),H.looseObject({type:H.literal(`tool-output-error`),toolCallId:H.string(),errorText:H.string(),providerExecuted:H.boolean().optional(),providerMetadata:W.optional(),toolMetadata:G.optional(),dynamic:H.boolean().optional()}),H.looseObject({type:H.literal(`tool-output-denied`),toolCallId:H.string()}),H.looseObject({type:H.literal(`reasoning-start`),id:H.string(),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`reasoning-delta`),id:H.string(),delta:H.string(),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`reasoning-end`),id:H.string(),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`custom`),kind:H.string().transform(e=>e),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`source-url`),sourceId:H.string(),url:H.string(),title:H.string().optional(),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`source-document`),sourceId:H.string(),mediaType:H.string(),title:H.string(),filename:H.string().optional(),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`file`),url:H.string(),mediaType:H.string(),providerMetadata:W.optional()}),H.looseObject({type:H.literal(`reasoning-file`),url:H.string(),mediaType:H.string(),providerMetadata:W.optional()}),H.looseObject({type:H.custom(e=>typeof e==`string`&&e.startsWith(`data-`),{message:`Type must start with "data-"`}),id:H.string().optional(),data:H.unknown(),transient:H.boolean().optional()}),H.looseObject({type:H.literal(`start-step`)}),H.looseObject({type:H.literal(`finish-step`)}),H.looseObject({type:H.literal(`reset-step`)}),H.looseObject({type:H.literal(`start`),messageId:H.string().optional(),messageMetadata:H.unknown().optional()}),H.looseObject({type:H.literal(`finish`),finishReason:H.enum([`stop`,`length`,`content-filter`,`tool-calls`,`error`,`other`]).optional(),messageMetadata:H.unknown().optional()}),H.looseObject({type:H.literal(`abort`),reason:H.string().optional()}),H.looseObject({type:H.literal(`message-metadata`),messageMetadata:H.unknown()})])));function si(e){return e.type.startsWith(`data-`)}function K(){return Object.create(null)}function ci(e){return e.type===`text`}function li(e){return e.type.startsWith(`tool-`)}function ui(e){return e.type===`dynamic-tool`}function q(e){return li(e)||ui(e)}function di(e){return e.type.split(`-`).slice(1).join(`-`)}function fi(e){return ui(e)?e.toolName:di(e)}function pi({lastMessage:e,messageId:t}){return{message:e?.role===`assistant`?e:{id:t,metadata:void 0,role:`assistant`,parts:[]},activeTextParts:K(),activeReasoningParts:K(),partialToolCalls:K()}}function mi({stream:e,messageMetadataSchema:t,dataPartSchemas:n,runUpdateMessageJob:r,onError:i,onToolCall:a,onData:o}){return e.pipeThrough(new TransformStream({async transform(e,s){await r(async({state:r,write:c})=>{function l(){let e=r.message.parts,t=e.length-1;for(;t>=0&&e[t].type!==`step-start`;)t--;return e.slice(t+1)}function u(){return l().filter(q)}function d(e){let t=u().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(q(i)&&i.toolCallId===e){t=i;break}}}if(t==null)throw new V({chunkType:`tool-invocation`,chunkId:e,message:`No tool invocation found for tool call ID "${e}".`});return t}function f(e){let t=r.message.parts.filter(q).find(t=>t.approval?.id===e);if(t==null)throw new V({chunkType:`tool-approval-response`,chunkId:e,message:`No tool invocation found for approval ID "${e}".`});return t}function p(e,t){let n=t??l().find(t=>li(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 m(e,t){let n=t??l().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.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 h(e){if(e!=null){let n=r.message.metadata==null?e:jr(r.message.metadata,e);t!=null&&await ur({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),c();break}case`text-delta`:{let t=r.activeTextParts[e.id];if(t==null)throw new V({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,c();break}case`text-end`:{let t=r.activeTextParts[e.id];if(t==null)throw new V({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],c();break}case`custom`:{let t={type:`custom`,kind:e.kind,providerMetadata:e.providerMetadata};r.message.parts.push(t),c();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),c();break}case`reasoning-delta`:{let t=r.activeReasoningParts[e.id];if(t==null)throw new V({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,c();break}case`reasoning-end`:{let t=r.activeReasoningParts[e.id];if(t==null)throw new V({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],c();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}}),c();break;case`source-url`:r.message.parts.push({type:`source-url`,sourceId:e.sourceId,url:e.url,title:e.title,providerMetadata:e.providerMetadata}),c();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}),c();break;case`tool-input-start`:{let t=l().filter(li);r.partialToolCalls[e.toolCallId]={text:``,toolName:e.toolName,index:t.length,dynamic:e.dynamic,title:e.title,toolMetadata:e.toolMetadata},e.dynamic?m({toolCallId:e.toolCallId,toolName:e.toolName,state:`input-streaming`,input:void 0,providerExecuted:e.providerExecuted,title:e.title,toolMetadata:e.toolMetadata,providerMetadata:e.providerMetadata}):p({toolCallId:e.toolCallId,toolName:e.toolName,state:`input-streaming`,input:void 0,providerExecuted:e.providerExecuted,title:e.title,toolMetadata:e.toolMetadata,providerMetadata:e.providerMetadata}),c();break}case`tool-input-delta`:{let t=r.partialToolCalls[e.toolCallId];if(t==null)throw new V({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 Qr(t.text);t.dynamic?m({toolCallId:e.toolCallId,toolName:t.toolName,state:`input-streaming`,input:n,title:t.title,toolMetadata:t.toolMetadata}):p({toolCallId:e.toolCallId,toolName:t.toolName,state:`input-streaming`,input:n,title:t.title,toolMetadata:t.toolMetadata}),c();break}case`tool-input-available`:e.dynamic?m({toolCallId:e.toolCallId,toolName:e.toolName,state:`input-available`,input:e.input,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,title:e.title,toolMetadata:e.toolMetadata}):p({toolCallId:e.toolCallId,toolName:e.toolName,state:`input-available`,input:e.input,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,title:e.title,toolMetadata:e.toolMetadata}),c(),a&&!e.providerExecuted&&await a({toolCall:e});break;case`tool-input-error`:{let t=l().filter(q).find(t=>t.toolCallId===e.toolCallId);(t==null?e.dynamic:t.type===`dynamic-tool`)?m({toolCallId:e.toolCallId,toolName:e.toolName,state:`output-error`,input:e.input,errorText:e.errorText,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,toolMetadata:e.toolMetadata}):p({toolCallId:e.toolCallId,toolName:e.toolName,state:`output-error`,input:void 0,rawInput:e.input,errorText:e.errorText,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,toolMetadata:e.toolMetadata}),c();break}case`tool-approval-request`:{let t=d(e.toolCallId);t.state=`approval-requested`,t.approval={id:e.approvalId,...e.approvalDescriptor==null?{}:{descriptor:e.approvalDescriptor},...e.reason==null?{}:{requestReason:e.reason},...e.isAutomatic===!0?{isAutomatic:!0}:{},...e.signature==null?{}:{signature:e.signature}},c();break}case`tool-approval-response`:{let t=f(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),c();break}case`tool-output-denied`:{let t=d(e.toolCallId);t.state=`output-denied`,c();break}case`tool-output-available`:{let t=d(e.toolCallId);t.type===`dynamic-tool`?m({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:t.toolMetadata},t):p({toolCallId:e.toolCallId,toolName:di(t),state:`output-available`,input:t.input,output:e.output,providerExecuted:e.providerExecuted,preliminary:e.preliminary,providerMetadata:e.providerMetadata,title:t.title,toolMetadata:t.toolMetadata},t),c();break}case`tool-output-error`:{let t=d(e.toolCallId);t.type===`dynamic-tool`?m({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:t.toolMetadata},t):p({toolCallId:e.toolCallId,toolName:di(t),state:`output-error`,input:t.input,rawInput:t.rawInput,errorText:e.errorText,providerExecuted:e.providerExecuted,providerMetadata:e.providerMetadata,title:t.title,toolMetadata:t.toolMetadata},t),c();break}case`start-step`:r.message.parts.push({type:`step-start`});break;case`finish-step`:break;case`reset-step`:{let e=l();r.activeTextParts=K(),r.activeReasoningParts=K(),r.partialToolCalls=K(),e.length>0&&(r.message.parts.splice(r.message.parts.length-e.length,e.length),c());break}case`start`:e.messageId!=null&&(r.message.id=e.messageId),await h(e.messageMetadata),(e.messageId!=null||e.messageMetadata!=null)&&c({updateStatus:!1});break;case`finish`:e.finishReason!=null&&(r.finishReason=e.finishReason),await h(e.messageMetadata),e.messageMetadata!=null&&c();break;case`message-metadata`:await h(e.messageMetadata),e.messageMetadata!=null&&c();break;case`error`:i?.(Error(e.errorText));break;default:if(si(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 ur({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),c()}}s.enqueue(e)})}}))}async function hi({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()}}j({prefix:`aitxt`,size:24}),j({prefix:`call`,size:24}),j({prefix:`aitxt`,size:24}),j({prefix:`call`,size:24}),H.record(H.string(),U.optional()),H.record(H.string(),H.string()),j({prefix:`call`,size:24}),j({prefix:`call`,size:24}),new TextEncoder,j({prefix:`aiobj`,size:24});var gi=class{constructor(){this.queue=[],this.isProcessing=!1}async processQueue(){if(!this.isProcessing){for(this.isProcessing=!0;this.queue.length>0;)await this.queue[0](),this.queue.shift();this.isProcessing=!1}}async run(e){return new Promise((t,n)=>{this.queue.push(async()=>{try{await e(),t()}catch(e){n(e)}}),this.processQueue()})}};j({prefix:`aiobj`,size:24}),j({prefix:`call`,size:24});async function _i(e){if(e==null)return[];if(!globalThis.FileList||!(e instanceof globalThis.FileList))throw Error(`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)})}}))}var vi=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 z(this.body),r=await z(this.headers),i=await z(this.credentials),a={...A(r),...A(t.headers)},o=await this.prepareSendMessagesRequest?.call(this,{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:A(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 Error(await d.text()||`Failed to fetch the chat response.`);if(!d.body)throw Error(`The response body is empty.`);return this.processResponseStream(d.body)}async reconnectToStream(e){let t=await z(this.body),n=await z(this.headers),r=await z(this.credentials),i={...A(n),...A(e.headers)},a=await this.prepareReconnectToStreamRequest?.call(this,{api:this.api,id:e.chatId,body:{...t,...e.body},headers:i,credentials:r,requestMetadata:e.metadata}),o=a?.api??`${this.api}/${e.chatId}/stream`,s=a?.headers===void 0?i:A(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 Error(await l.text()||`Failed to fetch the chat response.`);if(!l.body)throw Error(`The response body is empty.`);return this.processResponseStream(l.body)}},yi=class extends vi{constructor(e={}){super(e)}processResponseStream(e){return fr({stream:e,schema:oi}).pipeThrough(new TransformStream({async transform(e,t){if(!e.success)throw e.error;t.enqueue(e.value)}}))}},bi=class{constructor({generateId:e=Yt,id:t=e(),transport:n=new yi,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.jobExecutor=new gi,this.sendMessage=async(e,t)=>{if(e==null){await this.makeRequest({trigger:`submit-message`,messageId:this.lastMessage?.id,...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 _i(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 Error(`message with id ${e.messageId} not found`);if(this.state.messages[t].role!==`user`)throw Error(`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 Error(`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=i[i.length-1],o=r=>q(r)&&r.state===`approval-requested`&&r.approval.id===e?{...r,state:`approval-responded`,approval:{...r.approval,id:e,approved:t,reason:n}}:r;this.state.replaceMessage(i.length-1,{...a,parts:a.parts.map(o)}),this.activeResponse&&(this.activeResponse.state.message.parts=this.activeResponse.state.message.parts.map(o)),this.status!==`streaming`&&this.status!==`submitted`&&this.sendAutomaticallyWhen&&this.shouldSendAutomatically().then(e=>{e&&this.makeRequest({trigger:`submit-message`,messageId:this.lastMessage?.id,...r})})}),this.addToolOutput=async({state:e=`output-available`,toolCallId:t,output:n,errorText:r,options:i})=>this.jobExecutor.run(async()=>{let a=this.state.messages,o=a[a.length-1],s=i=>q(i)&&i.toolCallId===t?{...i,state:e,output:n,errorText:r}:i;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&&this.shouldSendAutomatically().then(e=>{e&&this.makeRequest({trigger:`submit-message`,messageId:this.lastMessage?.id,...i})})}),this.addToolResult=this.addToolOutput,this.stop=async()=>{var e,t;for(let e of this.pendingMessagePreparations)e.abort();(e=this.activeResumeRequest)==null||e.abortController.abort(),(t=this.activeResponse)==null||t.abortController.abort()},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}){this.status!==e&&(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 makeRequest({trigger:e,metadata:t,headers:n,body:r,messageId:i}){var a,o;let s=new AbortController,c=e===`resume-stream`?{abortController:s}:void 0;c&&((a=this.activeResumeRequest)==null||a.abortController.abort(),this.activeResumeRequest=c);let l=()=>c==null||this.activeResumeRequest===c,u=()=>{this.activeResumeRequest===c&&(this.activeResumeRequest=void 0)},d;if(e===`resume-stream`)try{let e=await this.transport.reconnectToStream({chatId:this.id,abortSignal:s.signal,metadata:t,headers:n,body:r});if(s.signal.aborted||!l()){await e?.cancel().catch(()=>{}),l()&&this.setStatus({status:`ready`}),u();return}if(e==null){this.setStatus({status:`ready`}),u();return}d=e}catch(e){if(s.signal.aborted||e.name===`AbortError`){l()&&this.setStatus({status:`ready`}),u();return}if(!l())return;this.onError&&e instanceof Error&&this.onError(e),this.setStatus({status:`error`,error:e}),u();return}this.setStatus({status:`submitted`,error:void 0});let f=this.lastMessage,p=!1,m=!1,h=!1,g;try{let a={state:pi({lastMessage:e===`resume-stream`||e===`regenerate-message`?void 0:this.state.snapshot(f),messageId:this.generateId()}),abortController:s};g=a,a.abortController.signal.addEventListener(`abort`,()=>{p=!0}),this.activeResponse=a;let o;if(o=e===`resume-stream`?d:await this.transport.sendMessages({chatId:this.id,messages:this.state.messages,abortSignal:a.abortController.signal,metadata:t,headers:n,body:r,trigger:e,messageId:i}),await hi({stream:mi({stream:o,onToolCall:this.onToolCall,onData:this.onData,messageMetadataSchema:this.messageMetadataSchema,dataPartSchemas:this.dataPartSchemas,runUpdateMessageJob:e=>this.jobExecutor.run(()=>a.abortController.signal.aborted?Promise.resolve():e({state:a.state,write:({updateStatus:e=!0}={})=>{a.abortController.signal.aborted||(e&&this.setStatus({status:`streaming`}),a.state.message.id===this.lastMessage?.id?this.state.replaceMessage(this.state.messages.length-1,a.state.message):this.state.pushMessage(a.state.message))}})),onError:e=>{throw e}}),abortSignal:a.abortController.signal,onError:e=>{throw e}}),p)return l()&&this.setStatus({status:`ready`}),null;l()&&this.setStatus({status:`ready`})}catch(e){if(p||e.name===`AbortError`)return p=!0,l()&&this.setStatus({status:`ready`}),null;if(!l())return null;h=!0,e instanceof TypeError&&(e.message.toLowerCase().includes(`fetch`)||e.message.toLowerCase().includes(`network`))&&(m=!0),this.onError&&e instanceof Error&&this.onError(e),this.setStatus({status:`error`,error:e})}finally{try{g&&((o=this.onFinish)==null||o.call(this,{message:g.state.message,messages:this.state.messages,isAbort:p,isDisconnect:m,isError:h,finishReason:g.state.finishReason}))}finally{this.activeResponse===g&&(this.activeResponse=void 0),u()}}!h&&await this.shouldSendAutomatically()&&await this.makeRequest({trigger:`submit-message`,messageId:this.lastMessage?.id,metadata:t,headers:n,body:r})}};function xi({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).filter(q).filter(e=>!e.providerExecuted);return r.length>0&&r.every(e=>e.state===`output-available`||e.state===`output-error`)}function Si(e){return new yi({api:_e(`/`,`Chat/Messages/`),credentials:`include`,body:{orgId:e}})}var Ci=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),wi=(e,t,n)=>{if(!t.has(e))throw TypeError(`Cannot `+n)},J=(e,t,n)=>(wi(e,t,`read from private field`),n?n.call(e):t.get(e)),Y=(e,t,n)=>{if(t.has(e))throw TypeError(`Cannot add the same private member more than once`);t instanceof WeakSet?t.add(e):t.set(e,n)},X=(e,t,n,r)=>(wi(e,t,`write to private field`),r?r.call(e,n):t.set(e,n),n);function Ti(e,t){return t==null?e:(0,Ci.default)(e,t)}function Ei(e){return Array.isArray(e)?[...e]:typeof e==`object`&&e&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null)?{...e}:e}var Z,Di,Oi,ki,Ai,ji,Q,Mi,Ni,Pi=class{constructor(e=[]){Y(this,Z,void 0),Y(this,Di,`ready`),Y(this,Oi,void 0),Y(this,ki,new Set),Y(this,Ai,new Set),Y(this,ji,new Set),this.pushMessage=e=>{X(this,Z,J(this,Z).concat(e)),J(this,Q).call(this)},this.popMessage=()=>{X(this,Z,J(this,Z).slice(0,-1)),J(this,Q).call(this)},this.replaceMessage=(e,t)=>{X(this,Z,[...J(this,Z).slice(0,e),this.snapshot(t),...J(this,Z).slice(e+1)]),J(this,Q).call(this)},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=Ei(t.metadata)),n},this[`~registerMessagesCallback`]=(e,t)=>{let n=t?Ti(e,t):e;return J(this,ki).add(n),()=>{J(this,ki).delete(n)}},this[`~registerStatusCallback`]=e=>(J(this,Ai).add(e),()=>{J(this,Ai).delete(e)}),this[`~registerErrorCallback`]=e=>(J(this,ji).add(e),()=>{J(this,ji).delete(e)}),Y(this,Q,()=>{J(this,ki).forEach(e=>e())}),Y(this,Mi,()=>{J(this,Ai).forEach(e=>e())}),Y(this,Ni,()=>{J(this,ji).forEach(e=>e())}),X(this,Z,e)}get status(){return J(this,Di)}set status(e){X(this,Di,e),J(this,Mi).call(this)}get error(){return J(this,Oi)}set error(e){X(this,Oi,e),J(this,Ni).call(this)}get messages(){return J(this,Z)}set messages(e){X(this,Z,[...e]),J(this,Q).call(this)}};Z=new WeakMap,Di=new WeakMap,Oi=new WeakMap,ki=new WeakMap,Ai=new WeakMap,ji=new WeakMap,Q=new WeakMap,Mi=new WeakMap,Ni=new WeakMap;var $,Fi=class extends bi{constructor({messages:e,...t}){let n=new Pi(e);super({...t,state:n}),Y(this,$,void 0),this[`~registerMessagesCallback`]=(e,t)=>J(this,$)[`~registerMessagesCallback`](e,t),this[`~registerStatusCallback`]=e=>J(this,$)[`~registerStatusCallback`](e),this[`~registerErrorCallback`]=e=>J(this,$)[`~registerErrorCallback`](e),X(this,$,n)}};$=new WeakMap;function Ii({throttle:e,experimental_throttle:t,resume:n=!1,...r}={}){let i=e??t,a=(0,T.useRef)({});`chat`in r||(a.current={onToolCall:r.onToolCall,onData:r.onData,onFinish:r.onFinish,onError:r.onError,sendAutomaticallyWhen:r.sendAutomaticallyWhen,transport:r.transport});let o,s=()=>a.current.transport??(o??=new yi),c={...r,transport:{sendMessages:e=>s().sendMessages(e),reconnectToStream:e=>s().reconnectToStream(e)},onToolCall:e=>{var t;return(t=a.current).onToolCall?.call(t,e)},onData:e=>{var t;return(t=a.current).onData?.call(t,e)},onFinish:e=>{var t;return(t=a.current).onFinish?.call(t,e)},onError:e=>{var t;return(t=a.current).onError?.call(t,e)},sendAutomaticallyWhen:e=>{var t;return(t=a.current).sendAutomaticallyWhen?.call(t,e)??!1}},l=(0,T.useRef)(`chat`in r?r.chat:new Fi(c));(`chat`in r&&r.chat!==l.current||`id`in r&&r.id!=null&&l.current.id!==r.id)&&(l.current=`chat`in r?r.chat:new Fi(c));let u=l.current,d=(0,T.useRef)({chat:u,messages:u.messages});d.current.chat!==u&&(d.current={chat:u,messages:u.messages});let f=(0,T.useCallback)(e=>{let t=!0,n=u[`~registerMessagesCallback`](()=>{!t||d.current.chat!==u||(d.current={chat:u,messages:u.messages},e())},i);return d.current={chat:u,messages:u.messages},()=>{t=!1,n()}},[u,i]),p=(0,T.useCallback)(()=>d.current.messages,[]),m=(0,T.useSyncExternalStore)(f,p,p),h=(0,T.useCallback)(e=>u[`~registerStatusCallback`](()=>{d.current.chat===u&&((u.status===`ready`||u.status===`error`)&&(d.current={chat:u,messages:u.messages}),e())}),[u]),g=(0,T.useCallback)(()=>u.status,[u]),_=(0,T.useSyncExternalStore)(h,g,g),v=(0,T.useSyncExternalStore)(l.current[`~registerErrorCallback`],()=>l.current.error,()=>l.current.error),y=(0,T.useCallback)(e=>{typeof e==`function`&&(e=e(l.current.messages)),l.current.messages=e},[l]);return(0,T.useEffect)(()=>{n&&l.current.resumeStream()},[n,l]),{id:l.current.id,messages:m,setMessages:y,sendMessage:l.current.sendMessage,regenerate:l.current.regenerate,clearError:l.current.clearError,stop:l.current.stop,error:v,resumeStream:l.current.resumeStream,status:_,addToolResult:l.current.addToolOutput,addToolOutput:l.current.addToolOutput,addToolApprovalResponse:l.current.addToolApprovalResponse}}function Li({input:e,setInput:t,onSubmit:n,disabled:r,autoFocus:i}){let a=(0,T.useRef)(null);return(0,T.useEffect)(()=>{i&&!r&&a.current?.focus()},[i,r]),(0,E.jsxs)(`form`,{onSubmit:n,className:`input-area`,children:[(0,E.jsx)(`input`,{ref:a,value:e,onChange:e=>t(e.target.value),placeholder:`Type a message...`,disabled:r}),(0,E.jsx)(`button`,{type:`submit`,disabled:r||!e.trim(),children:(0,E.jsx)(ut,{size:18})})]})}function Ri(){return(0,E.jsxs)(`div`,{className:`loading-state`,children:[(0,E.jsx)(Ze,{className:`animate-spin`,size:24}),(0,E.jsx)(`p`,{children:`Loading conversation...`})]})}var zi={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:l({feedbackSummary:y().describe(`A brief summary of the feedback, used as the discussion title.`),feedbackDetails:y().describe(`Detailed feedback from the user or agent observation.`),recap:y().describe(`A sanitized recap of what the agent and user did together and if it was successful. No sensitive information.`)})};async function Bi({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 Vi={...zi,icon:lt,execute:Bi},Hi={name:`createApp`,description:`Create a new Harper app with the specified name and template type.`,inputSchema:l({name:y().trim(),type:v(ve)})};async function Ui({input:{name:e,type:t},instanceClientParams:n,baseURL:r}){let i=Ke(e),a=ge(fe,`id`,t);if(!a)return{success:!1,message:`Error: Invalid template type, ${t}, please choose from: ${fe.map(e=>e.id).join(`, `)}`};let o=se.loading(`Creating from template...`,{description:`This may take a bit.`,duration:3e5});try{await xe({...n,project:i,template:a.npm||a.githubUrl})}catch(e){return{success:!1,message:`Error: ${e}`}}ke(`ReloadApplicationRootEntries`,!0),se.loading(`Created successfully!`,{description:`${i} created! Restarting the HTTP service...`,id:o,duration:3e5});try{await Qe({...n,operation:`restart_service`,replicated:n.entityType===`cluster`})}catch(e){return{success:!1,message:`Error: ${e}`}}return se.success(`Created successfully!`,{description:`${i} created!`,id:o,duration:5e3}),{success:!0,message:`App "${i}" created successfully.`,webURL:r}}var Wi={...Hi,icon:Re,execute:Ui,requiresApproval:!0},Gi={name:`deleteTableRecords`,description:`Deletes records in a particular table in a particular database on the server by their primary keys.`,inputSchema:l({database:y().trim(),table:y().trim(),primaryKeys:m(y())})};async function Ki({input:{database:e,table:t,primaryKeys:n},instanceClientParams:r,params:i}){try{let a=await He({...r,databaseName:e,tableName:t,hashValues:n}),{databaseName:o,tableName:s}=i;return await Je.invalidateQueries({queryKey:[r.entityId,o,s]}),{success:!0,data:a}}catch(e){return{success:!1,message:`Error: ${e}`}}}var qi={...Gi,icon:rt,execute:Ki,requiresApproval:!0},Ji={name:`dropComponentFile`,description:`Drops a component file by its full path (which was returned by getComponents)`,inputSchema:l({path:y().trim()})};async function Yi({input:{path:e},instanceClientParams:t}){try{let n=e.split(`/`),r=n.shift(),i=n.join(`/`);return{success:!0,data:await de({...t,file:i,project:r})}}catch(e){return{success:!1,message:`Error: ${e}`}}}var Xi={...Ji,icon:Ye,execute:Yi,requiresApproval:!0},Zi={name:`getAnalytics`,description:`Retrieves analytics metrics for the server, such as CPU usage or database operations (reads, writes, messages).`,inputSchema:l({metricName:y().describe(`The name of the metric to retrieve.`),startTime:u().describe(`The start of the time range for the metric, in milliseconds since the Unix epoch.`),endTime:u().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 nt({metric:n,startTime:r,endTime:i,instanceParams:t})}}catch(e){return{success:!1,message:`Error retrieving analytics: ${e}`}}}var $i={...Zi,icon:at,execute:Qi},ea={name:`getComponentFile`,description:`Returns the contents of a component file by its full path (which was returned by getComponents)`,inputSchema:l({path:y().trim()})};async function ta({input:{path:e},instanceClientParams:t}){try{let n=e.split(`/`),r=n.shift(),i=n.join(`/`);return{success:!0,data:await qe({...t,file:i,project:r})}}catch(e){return{success:!1,message:`Error: ${e}`}}}var na={...ea,icon:pe,execute:ta},ra={name:`getComponents`,description:`Retrieves a tree of all the component (files and folders) names and sizes stored on the server.`,inputSchema:l({})};async function ia({instanceClientParams:e}){try{let t=await We(e),{rootEntries:n}=ye(t.entries);return{success:!0,items:he(n).items}}catch(e){return{success:!1,message:`Error: ${e}`}}}var aa={...ra,icon:me,execute:ia},oa={name:`getDescribeAll`,description:`Retrieves a map of the names of databases and their tables stored on the server.`,inputSchema:l({})};async function sa({instanceClientParams:e}){try{return{success:!0,map:await Xe(e)}}catch(e){return{success:!1,message:`Error: ${e}`}}}var ca={...oa,icon:Ne,execute:sa},la={name:`getDescribeTable`,description:`Returns the schema metadata describing a particular table in a particular database on the server.`,inputSchema:l({database:y().trim(),table:y().trim()})};async function ua({input:{database:e,table:t},instanceClientParams:n}){try{return{success:!0,data:await Ie({...n,databaseName:e,tableName:t})}}catch(e){return{success:!1,message:`Error: ${e}`}}}var da={...la,icon:Ue,execute:ua},fa={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:l({})};async function pa({instanceClientParams:e,params:t}){let n=De();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(`/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=oe(`FileFocused/${e.entityId}`,void 0),i=oe(`FolderOpened/${e.entityId}`,[]),a=oe(`FileSelected/${e.entityId}`,[]);return r?{success:!0,description:`editing application files`,data:{openedPath:r,expandedItems:i,selectedItems:a}}:{success:!0,description:n}}var ma={...fa,icon:Be,execute:pa},ha={name:`insertTableRecords`,description:`Inserts one or more records into a particular table in a particular database on the server.`,inputSchema:l({database:y().trim(),table:y().trim(),records:m(i())})};async function ga({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 Je.invalidateQueries({queryKey:[r.entityId,o,s]}),{success:!0,data:a}}catch(e){return{success:!1,message:`Error: ${e}`}}}var _a={...ha,icon:rt,execute:ga,requiresApproval:!0},va={name:`listAnalyticsMetrics`,description:`Lists the available analytics metric names available for calling getAnalytics upon.`,inputSchema:l({metricTypes:v([`builtin`,`custom`]),customWindowMS:u().optional().describe(`Default to one week time window for finding custom metrics.`)})};async function ya({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 ba({input:e,instanceClientParams:t}){try{let{metricTypes:n,customWindowMS:r}=e;return{success:!0,metricNames:await ya({metricTypes:n,customWindowMS:r,instanceParams:t})}}catch(e){return{success:!1,message:`Error retrieving analytics: ${e}`}}}var xa={...va,icon:at,execute:ba},Sa=[`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`],Ca={"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 schema itself.\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 ID.\n - `POST /{TableName}/`: Creates a new record.\n - `PUT /{TableName}/{id}`: Updates an existing record.\n - `PATCH /{TableName}/{id}`: Performs a partial update on a record.\n - `DELETE /{TableName}/`: Deletes all records or filtered records.\n - `DELETE /{TableName}/{id}`: Deletes a single record by its ID.\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":`---
17
+ name: automatic-apis
18
+ description: How to use Harper's automatically generated REST and WebSocket APIs.
19
+ metadata:
20
+ mode: generate
21
+ sources:
22
+ - reference/v5/rest/overview.md#How the REST Interface Works
23
+ - reference/v5/rest/overview.md#Configuration
24
+ - reference/v5/rest/overview.md#Tables and Their Automatic Endpoints
25
+ - reference/v5/rest/overview.md#URL Structure
26
+ - reference/v5/rest/overview.md#HTTP Methods
27
+ - reference/v5/rest/overview.md#Content Types
28
+ - reference/v5/rest/overview.md#OpenAPI
29
+ - reference/v5/rest/websockets.md
30
+ sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
31
+ inputHash: a9356e92dd3dc106
32
+ ---
33
+
34
+ # Automatic APIs
35
+
36
+ Instructions for the agent to follow when using Harper's automatically generated REST and WebSocket APIs for exported tables and resources.
37
+
38
+ ## When to Use
39
+
40
+ Apply this rule when enabling HTTP REST endpoints or WebSocket subscriptions for Harper tables without writing custom handler code. Use it whenever a schema type needs to be served over HTTP, when configuring real-time subscriptions, or when setting up conditional caching behavior for REST responses.
41
+
42
+ ## How It Works
43
+
44
+ 1. **Enable REST in \`config.yaml\`**: Add \`rest: true\` to the application configuration file. This registers REST endpoints and, by default, WebSocket subscriptions for all exported resources.
45
+
46
+ \`\`\`yaml
47
+ rest: true
48
+ \`\`\`
49
+
50
+ To configure options explicitly:
51
+
52
+ \`\`\`yaml
53
+ rest:
54
+ lastModified: true # enables Last-Modified response header support
55
+ webSocket: false # disables automatic WebSocket support (enabled by default)
56
+ \`\`\`
57
+
58
+ 2. **Export the table in the schema**: Add \`@export\` to the type definition. Without \`@export\`, Harper registers no REST route and callers receive \`404\`. Without \`rest: true\`, even an exported table does not respond to HTTP requests. Both are required.
59
+
60
+ \`\`\`graphql
61
+ type Product @table @export {
62
+ id: Long @primaryKey
63
+ name: String
64
+ price: Float
65
+ }
66
+ \`\`\`
67
+
68
+ Reference the schema file in \`config.yaml\`:
69
+
70
+ \`\`\`yaml
71
+ graphqlSchema:
72
+ files: schema.graphql
73
+ rest: true
74
+ \`\`\`
75
+
76
+ 3. **Use the automatically registered endpoints**: Harper serves the following endpoints on the application HTTP server port (default \`9926\`) with no route definitions or handler code required.
77
+
78
+ | Endpoint | Description |
79
+ | ---------------------------- | --------------------------------------------------------------------------- |
80
+ | \`GET /Product\` | Returns resource description (table name, database, attributes) |
81
+ | \`GET /Product/\` | Returns the record collection; append query parameters to filter |
82
+ | \`GET /Product/{id}\` | Returns a single record by primary key; \`404\` if not found |
83
+ | \`GET /Product/{id}.property\` | Returns a single declared property of one record |
84
+ | \`POST /Product/\` | Creates a record; responds \`201\`; primary key returned in \`Location\` header |
85
+ | \`PUT /Product/{id}\` | Creates or replaces the record at \`{id}\` (upsert) |
86
+ | \`PATCH /Product/{id}\` | Merges body into existing record (shallow, top-level only) |
87
+ | \`DELETE /Product/{id}\` | Deletes the record at \`{id}\` |
88
+ | \`DELETE /Product/?query\` | Deletes every record matching the query |
89
+
90
+ 4. **Handle \`POST\` primary key and \`Location\`**: On a successful \`POST\`, the new record's primary key is returned in the \`Location\` response header — the value the body supplied if it carried the primary-key property, otherwise a Harper-assigned key. The header carries the bare key value, not a URL.
91
+
92
+ 5. **Understand \`PUT\` write behavior**: \`PUT\` replaces the stored record exactly. Three exceptions always apply: a \`@createdTime\` attribute keeps the original value, an \`@updatedTime\` attribute is re-stamped with the time of the write, and the primary key is forced to match the \`{id}\` in the URL.
93
+
94
+ 6. **Use conditional requests for caching**: GET responses include an \`ETag\` header encoding the record's version/last-modification time. Send \`If-None-Match\` on subsequent requests with the cached \`ETag\` value. If the record has not changed, Harper returns \`304 Not Modified\` with no body.
95
+
96
+ 7. **Select content type with \`Accept\`**: Use the \`Accept\` header to request a specific response format. The suffixes \`.json\`, \`.cbor\`, \`.msgpack\`, and \`.csv\` are reserved as content-type selectors on property paths and take precedence over property names. See [querying-rest-apis.md](querying-rest-apis.md) for query syntax details.
97
+
98
+ 8. **Connect via WebSocket**: WebSocket support is enabled automatically when \`rest\` is enabled. Connecting to a resource URL subscribes to changes for that resource. See [real-time-apps.md](real-time-apps.md) for real-time patterns.
99
+
100
+ \`\`\`javascript
101
+ let ws = new WebSocket('wss://server/my-resource/341');
102
+ ws.onmessage = (event) => {
103
+ let data = JSON.parse(event.data);
104
+ };
105
+ \`\`\`
106
+
107
+ 9. **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.
108
+
109
+ \`\`\`javascript
110
+ export class Echo extends Resource {
111
+ async *connect(incomingMessages) {
112
+ for await (let message of incomingMessages) {
113
+ yield message; // echo each message back
114
+ }
115
+ }
116
+ }
117
+ \`\`\`
118
+
119
+ ## Examples
120
+
121
+ ### Full schema and config setup
122
+
123
+ \`\`\`graphql
124
+ # schema.graphql
125
+ type Product @table @export {
126
+ id: Long @primaryKey
127
+ name: String
128
+ price: Float
129
+ }
130
+ \`\`\`
131
+
132
+ \`\`\`yaml
133
+ # config.yaml
134
+ graphqlSchema:
135
+ files: schema.graphql
136
+ rest: true
137
+ \`\`\`
138
+
139
+ ### Conditional GET with ETag caching
140
+
141
+ \`\`\`
142
+ GET /Product/123
143
+ # Response includes:
144
+ # ETag: "abc123"
145
+
146
+ GET /Product/123
147
+ If-None-Match: "abc123"
148
+ # Response: 304 Not Modified (no body transferred)
149
+ \`\`\`
150
+
151
+ ### POST and read the Location header
152
+
153
+ \`\`\`
154
+ POST /Product/
155
+ Content-Type: application/json
156
+
157
+ { "name": "Widget", "price": 9.99 }
158
+
159
+ # Response:
160
+ # 201 Created
161
+ # Location: 7f3a9c
162
+ \`\`\`
163
+
164
+ ### PATCH (shallow merge only)
165
+
166
+ \`\`\`
167
+ PATCH /Product/123
168
+ Content-Type: application/json
169
+
170
+ { "price": 12.99 }
171
+ \`\`\`
172
+
173
+ Only \`price\` is updated; other top-level properties are preserved. Nested objects in the body replace the stored sub-object wholesale — deep merge does not occur.
174
+
175
+ ### Request MessagePack response
176
+
177
+ \`\`\`
178
+ GET /Product/123
179
+ Accept: application/msgpack
180
+ \`\`\`
181
+
182
+ Alternatively, use the \`.msgpack\` suffix on the URL path where supported.
183
+
184
+ ### WebSocket with custom outgoing messages
185
+
186
+ \`\`\`javascript
187
+ export class Example extends Resource {
188
+ connect(incomingMessages) {
189
+ let outgoingMessages = super.connect();
190
+
191
+ let timer = setInterval(() => {
192
+ outgoingMessages.send({ greeting: 'hi again!' });
193
+ }, 1000);
194
+
195
+ incomingMessages.on('data', (message) => {
196
+ outgoingMessages.send(message);
197
+ });
198
+
199
+ outgoingMessages.on('close', () => {
200
+ clearInterval(timer);
201
+ });
202
+
203
+ return outgoingMessages;
204
+ }
205
+ }
206
+ \`\`\`
207
+
208
+ ### Disable WebSocket while keeping REST
209
+
210
+ \`\`\`yaml
211
+ rest:
212
+ webSocket: false
213
+ \`\`\`
214
+
215
+ ## Notes
216
+
217
+ - The trailing slash is significant: \`/Product\` addresses the resource itself; \`/Product/\` addresses its record collection. \`POST /Product\` (no trailing slash) returns \`404\`.
218
+ - \`HEAD\` is served as \`GET\` with the body omitted. \`QUERY\` is accepted on the collection path and reads its search from the request body.
219
+ - A \`POST\` to an existing primary key fails with \`409\` — it does not overwrite.
220
+ - A component directory with **no configuration file** gets REST enabled by Harper's built-in default. As soon as a \`config.yaml\` exists it is used verbatim — add \`rest: true\` explicitly or REST is off.
221
+ - Do not apply \`@export\` to a schema type and also export a same-named JavaScript subclass of that table — this produces conflicting endpoints.
222
+ - Server-Sent Events subscriptions are served on the same paths, negotiated via \`Accept: text/event-stream\`. They are not affected by the \`webSocket\` option.
223
+ - Every non-hidden exported resource is included in the generated OpenAPI document at \`GET /openapi\`. Mark a type \`@hidden\` or set \`static hidden = true\` on a programmatic Resource to omit it.
224
+ - MQTT over WebSockets requires the sub-protocol header \`Sec-WebSocket-Protocol: mqtt\`.
225
+ `,caching:`---
226
+ name: caching
227
+ description: How to implement integrated data caching in Harper from external sources.
228
+ metadata:
229
+ mode: generate
230
+ sources:
231
+ - learn/developers/caching-with-harper.md
232
+ sourceCommit: 4fe4c9c95e0974eaa77032f6f10e36fbd8ec64ac
233
+ inputHash: 60ad55fa37b5eec5
234
+ ---
235
+
236
+ # Caching External Data Sources in Harper
237
+
238
+ Instructions for the agent to implement integrated data caching from external sources using Harper's cache table directives and \`sourcedFrom\` API.
239
+
240
+ ## When to Use
241
+
242
+ 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.
243
+
244
+ ## How It Works
245
+
246
+ 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.
247
+
248
+ \`\`\`graphql
249
+ type JokeCache @table(expiration: 60) @export {
250
+ id: ID @primaryKey
251
+ setup: String
252
+ punchline: String
253
+ }
254
+ \`\`\`
255
+
256
+ 2. **Implement an upstream source object**: In \`resources.js\`, create an object with a \`get(id)\` method that fetches data from the external API.
257
+
258
+ \`\`\`javascript
259
+ const jokeAPI = {
260
+ async get(id) {
261
+ const response = await fetch(\`https://official-joke-api.appspot.com/jokes/\${id}\`);
262
+ return response.json();
263
+ },
264
+ };
265
+ \`\`\`
266
+
267
+ 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.
268
+
269
+ \`\`\`javascript
270
+ tables.JokeCache.sourcedFrom(jokeAPI);
271
+ \`\`\`
272
+
273
+ Harper's request flow after \`sourcedFrom\` is registered:
274
+ - Request arrives for \`/JokeCache/1\`.
275
+ - Harper checks if the record exists and is not stale.
276
+ - If fresh, Harper returns it immediately.
277
+ - If missing or stale, Harper calls \`jokeAPI.get()\`, stores the result in \`JokeCache\`, and returns it.
278
+ - Multiple simultaneous requests for the same missing or stale record wait on a single upstream call — Harper prevents cache stampedes automatically.
279
+
280
+ 4. **Configure plugins in \`config.yaml\`**: Enable \`graphqlSchema\`, \`rest\`, and \`jsResource\`.
281
+
282
+ \`\`\`yaml
283
+ graphqlSchema:
284
+ files: 'schema.graphql'
285
+ rest: true
286
+ jsResource:
287
+ files: 'resources.js'
288
+ \`\`\`
289
+
290
+ 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.
291
+
292
+ \`\`\`javascript
293
+ export class JokeCache extends tables.JokeCache {
294
+ static async post(target, data) {
295
+ const body = await data;
296
+ if (body?.action === 'invalidate') {
297
+ this.invalidate(target);
298
+ return { status: 200, data: { message: 'invalidated' } };
299
+ }
300
+ }
301
+ }
302
+ \`\`\`
303
+
304
+ Update the schema to remove \`@export\`:
305
+
306
+ \`\`\`graphql
307
+ type JokeCache @table(expiration: 60) {
308
+ id: ID @primaryKey
309
+ setup: String
310
+ punchline: String
311
+ }
312
+ \`\`\`
313
+
314
+ ## Examples
315
+
316
+ **Complete \`resources.js\`**:
317
+
318
+ \`\`\`javascript
319
+ // resources.js
320
+
321
+ const jokeAPI = {
322
+ async get(id) {
323
+ const response = await fetch(\`https://official-joke-api.appspot.com/jokes/\${id}\`);
324
+ return response.json();
325
+ },
326
+ };
327
+
328
+ tables.JokeCache.sourcedFrom(jokeAPI);
329
+
330
+ export class JokeCache extends tables.JokeCache {
331
+ static async post(target, data) {
332
+ const body = await data;
333
+ if (body?.action === 'invalidate') {
334
+ this.invalidate(target);
335
+ return { status: 200, data: { message: 'invalidated' } };
336
+ }
337
+ }
338
+ }
339
+ \`\`\`
340
+
341
+ **Complete \`schema.graphql\`**:
342
+
343
+ \`\`\`graphql
344
+ type JokeCache @table(expiration: 60) {
345
+ id: ID @primaryKey
346
+ setup: String
347
+ punchline: String
348
+ }
349
+ \`\`\`
350
+
351
+ **Fetch a cached record**:
352
+
353
+ \`\`\`javascript
354
+ const response = await fetch('http://localhost:9926/JokeCache/1');
355
+ console.log(response.status); // 200
356
+ const etag = response.headers.get('etag'); // e.g. "abCDefGHij"
357
+ const joke = await response.json();
358
+ \`\`\`
359
+
360
+ **Use ETag for conditional requests** (returns \`304 Not Modified\` if unchanged):
361
+
362
+ \`\`\`javascript
363
+ const second = await fetch('http://localhost:9926/JokeCache/1', {
364
+ headers: { 'If-None-Match': etag },
365
+ });
366
+ console.log(second.status); // 304
367
+ \`\`\`
368
+
369
+ **Bypass the cache with \`Cache-Control: no-cache\`**:
370
+
371
+ \`\`\`javascript
372
+ const response = await fetch('http://localhost:9926/JokeCache/1', {
373
+ headers: { 'Cache-Control': 'no-cache' },
374
+ });
375
+ \`\`\`
376
+
377
+ **Trigger invalidation via POST**:
378
+
379
+ \`\`\`javascript
380
+ await fetch('http://localhost:9926/JokeCache/1', {
381
+ method: 'POST',
382
+ headers: { 'Content-Type': 'application/json' },
383
+ body: JSON.stringify({ action: 'invalidate' }),
384
+ });
385
+ \`\`\`
386
+
387
+ ## Notes
388
+
389
+ - \`expiration\` is measured in seconds. Harper also supports separate \`eviction\` and \`scanInterval\` arguments on \`@table\` for fine-grained control over physical record removal.
390
+ - 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.
391
+ - 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.
392
+ - For defining custom upstream source behavior beyond a simple \`get\`, see [custom-resources.md](custom-resources.md).
393
+ - For details on how \`@table\` and \`@export\` expose REST endpoints automatically, see [automatic-apis.md](automatic-apis.md).
394
+ `,"checking-authentication":`---
395
+ name: checking-authentication
396
+ description: How to handle user authentication and sessions in Harper Resources.
397
+ metadata:
398
+ mode: generate
399
+ sources:
400
+ - >-
401
+ reference/v5/resources/resource-api.md#\`getCurrentUser(): User |
402
+ undefined\`
403
+ - reference/v5/resources/resource-api.md#Session and Login from a Resource
404
+ - reference/v5/security/jwt-authentication.md#Create Authentication Tokens
405
+ - reference/v5/security/jwt-authentication.md#Using the Operation Token
406
+ - reference/v5/security/jwt-authentication.md#Refreshing the Operation Token
407
+ - reference/v5/security/jwt-authentication.md#Scoped Tokens (Inline Role)
408
+ - >-
409
+ reference/v5/security/jwt-authentication.md#Issuing Tokens from a Custom
410
+ Resource
411
+ - reference/v5/security/jwt-authentication.md#Token Expiry Configuration
412
+ - reference/v5/security/jwt-authentication.md#When to Use JWT Auth
413
+ - reference/v5/security/jwt-authentication.md#Security Notes
414
+ sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
415
+ inputHash: 084363f039abfe73
416
+ ---
417
+
418
+ # Checking Authentication
419
+
420
+ Instructions for the agent to handle user authentication, sessions, and JWT token issuance in Harper Resources.
421
+
422
+ ## When to Use
423
+
424
+ Apply this rule when implementing login/logout flows, protecting Resource endpoints by checking the current user, issuing or refreshing JWT tokens, or configuring token expiry in Harper. Use it whenever a custom Resource needs to authenticate callers or mint credentials for downstream consumers. See [custom-resources.md](custom-resources.md) for the broader Resource authoring context.
425
+
426
+ ## How It Works
427
+
428
+ 1. **Check the current authenticated user**: Call \`getCurrentUser()\` inside any Resource method. It returns the user object (with \`username\`, \`role\`, and \`role.permission\`) or \`undefined\` if unauthenticated. Guard endpoints by returning a 401 when no user is present.
429
+
430
+ \`\`\`javascript
431
+ async get(target) {
432
+ const user = this.getCurrentUser();
433
+ if (!user) return new Response(null, { status: 401 });
434
+ return { username: user.username, role: user.role };
435
+ }
436
+ \`\`\`
437
+
438
+ 2. **Enable sessions before using login/logout**: Set \`authentication.enableSessions: true\` in \`harperdb-config.yaml\`. Without this, \`context.login\` and \`context.session\` are unavailable.
439
+
440
+ 3. **Implement login via \`getContext()\`**: Call \`this.getContext()\` to obtain the request context, then call \`context.login(username, password)\` to verify credentials and establish a session cookie.
441
+
442
+ \`\`\`javascript
443
+ export class SignIn extends Resource {
444
+ async post(_target, data) {
445
+ const context = this.getContext();
446
+ try {
447
+ await context.login(data.username, data.password);
448
+ } catch {
449
+ return new Response('Invalid credentials', { status: 403 });
450
+ }
451
+ return new Response('Logged in', { status: 200 });
452
+ }
453
+ }
454
+ \`\`\`
455
+
456
+ 4. **Implement logout**: Delete the session via \`context.session.delete(context.session.id)\`.
457
+
458
+ \`\`\`javascript
459
+ export class SignOut extends Resource {
460
+ async post() {
461
+ const context = this.getContext();
462
+ if (!context.session) return new Response(null, { status: 401 });
463
+ await context.session.delete(context.session.id);
464
+ return new Response('Logged out', { status: 200 });
465
+ }
466
+ }
467
+ \`\`\`
468
+
469
+ Cookie-based sessions are intended for browser clients. For non-browser clients, use JWT issuance (steps below).
470
+
471
+ 5. **Create authentication tokens**: Call \`create_authentication_tokens\` with credentials. No \`Authorization\` header is required for this operation.
472
+
473
+ \`\`\`json
474
+ {
475
+ "operation": "create_authentication_tokens",
476
+ "username": "username",
477
+ "password": "password"
478
+ }
479
+ \`\`\`
480
+
481
+ Response:
482
+
483
+ \`\`\`json
484
+ {
485
+ "operation_token": "<jwt-operation-token>",
486
+ "refresh_token": "<jwt-refresh-token>"
487
+ }
488
+ \`\`\`
489
+
490
+ 6. **Use the operation token**: Pass it as a \`Bearer\` token in the \`Authorization\` header on subsequent requests.
491
+
492
+ \`\`\`bash
493
+ curl --location --request POST 'http://localhost:9925' \\
494
+ --header 'Content-Type: application/json' \\
495
+ --header 'Authorization: Bearer <operation_token>' \\
496
+ --data-raw '{
497
+ "operation": "search_by_hash",
498
+ "schema": "dev",
499
+ "table": "dog",
500
+ "hash_values": [1],
501
+ "get_attributes": ["*"]
502
+ }'
503
+ \`\`\`
504
+
505
+ 7. **Refresh an expired operation token**: When the \`operation_token\` expires, use \`refresh_operation_token\` and pass the \`refresh_token\` as \`Bearer <refresh_token>\`.
506
+
507
+ \`\`\`bash
508
+ curl --location --request POST 'http://localhost:9925' \\
509
+ --header 'Content-Type: application/json' \\
510
+ --header 'Authorization: Bearer <refresh_token>' \\
511
+ --data-raw '{
512
+ "operation": "refresh_operation_token"
513
+ }'
514
+ \`\`\`
515
+
516
+ Response:
517
+
518
+ \`\`\`json
519
+ {
520
+ "operation_token": "<new-jwt-operation-token>"
521
+ }
522
+ \`\`\`
523
+
524
+ When both tokens have expired, call \`create_authentication_tokens\` again with username and password.
525
+
526
+ 8. **Mint scoped tokens for limited access**: A super user can embed an inline role in \`create_authentication_tokens\` using the same \`permission\` structure as \`add_role\`. Include \`expires_in\` to control lifetime. Do not include a \`password\` field. The \`username\` is attribution only and must not match an existing user.
527
+
528
+ \`\`\`json
529
+ {
530
+ "operation": "create_authentication_tokens",
531
+ "username": "reporting-service",
532
+ "role": {
533
+ "permission": {
534
+ "operations": ["read_only"],
535
+ "dev": {
536
+ "tables": {
537
+ "dog": {
538
+ "read": true,
539
+ "insert": false,
540
+ "update": false,
541
+ "delete": false,
542
+ "attribute_permissions": []
543
+ }
544
+ }
545
+ }
546
+ }
547
+ },
548
+ "expires_in": "7d"
549
+ }
550
+ \`\`\`
551
+
552
+ Key constraints for scoped tokens:
553
+ - No refresh token is issued; no user record is created.
554
+ - Scoped tokens cannot be revoked before expiry — choose short \`expires_in\` values.
555
+ - \`super_user\` and \`cluster_user\` are always forced to \`false\` in the embedded role.
556
+ - In mixed-version clusters, only nodes with scoped-token support accept these tokens; older nodes return 401.
557
+
558
+ 9. **Issue tokens from a custom Resource using \`server.operation\`**: Import \`server\` from \`harper\` and call \`server.operation()\` to mint tokens programmatically. Pass \`true\` as the **third argument** to run the operation as the current authenticated user; omit it when supplying credentials directly.
559
+
560
+ \`\`\`javascript
561
+ import { Resource, server } from 'harper';
562
+
563
+ export class IssueTokens extends Resource {
564
+ static async get(_target, context) {
565
+ const { operation_token, refresh_token } = await server.operation(
566
+ { operation: 'create_authentication_tokens' },
567
+ context,
568
+ true,
569
+ );
570
+ return { operation_token, refresh_token };
571
+ }
572
+
573
+ static async post(_target, data) {
574
+ const { username, password } = await data;
575
+ if (!username || !password) {
576
+ return new Response('username and password required', { status: 400 });
577
+ }
578
+ const { operation_token, refresh_token } = await server.operation({
579
+ operation: 'create_authentication_tokens',
580
+ username,
581
+ password,
582
+ });
583
+ return { operation_token, refresh_token };
584
+ }
585
+ }
586
+
587
+ export class RefreshJWT extends Resource {
588
+ static async post(_target, data) {
589
+ const { refresh_token } = await data;
590
+ if (!refresh_token) {
591
+ return new Response('refresh_token required', { status: 400 });
592
+ }
593
+ const { operation_token } = await server.operation({
594
+ operation: 'refresh_operation_token',
595
+ refresh_token,
596
+ });
597
+ return { operation_token };
598
+ }
599
+ }
600
+ \`\`\`
601
+
602
+ 10. **Configure token expiry**: Set timeouts in \`harper-config.yaml\` under the \`authentication\` section. Values follow the \`jsonwebtoken\` duration string format (e.g., \`1d\`, \`12h\`, \`60m\`).
603
+
604
+ \`\`\`yaml
605
+ authentication:
606
+ operationTokenTimeout: 1d # Default: 1 day
607
+ refreshTokenTimeout: 30d # Default: 30 days
608
+ \`\`\`
609
+
610
+ ## Examples
611
+
612
+ ### Full JWT flow via cURL
613
+
614
+ \`\`\`bash
615
+ # Step 1: Create tokens
616
+ curl --location --request POST 'http://localhost:9925' \\
617
+ --header 'Content-Type: application/json' \\
618
+ --data-raw '{
619
+ "operation": "create_authentication_tokens",
620
+ "username": "username",
621
+ "password": "password"
622
+ }'
623
+
624
+ # Step 2: Use operation token
625
+ curl --location --request POST 'http://localhost:9925' \\
626
+ --header 'Content-Type: application/json' \\
627
+ --header 'Authorization: Bearer <operation_token>' \\
628
+ --data-raw '{
629
+ "operation": "search_by_hash",
630
+ "schema": "dev",
631
+ "table": "dog",
632
+ "hash_values": [1],
633
+ "get_attributes": ["*"]
634
+ }'
635
+
636
+ # Step 3: Refresh when operation token expires
637
+ curl --location --request POST 'http://localhost:9925' \\
638
+ --header 'Content-Type: application/json' \\
639
+ --header 'Authorization: Bearer <refresh_token>' \\
640
+ --data-raw '{
641
+ "operation": "refresh_operation_token"
642
+ }'
643
+ \`\`\`
644
+
645
+ ### Session-based login/logout Resource
646
+
647
+ \`\`\`javascript
648
+ export class SignIn extends Resource {
649
+ async post(_target, data) {
650
+ const context = this.getContext();
651
+ try {
652
+ await context.login(data.username, data.password);
653
+ } catch {
654
+ return new Response('Invalid credentials', { status: 403 });
655
+ }
656
+ return new Response('Logged in', { status: 200 });
657
+ }
658
+ }
659
+
660
+ export class SignOut extends Resource {
661
+ async post() {
662
+ const context = this.getContext();
663
+ if (!context.session) return new Response(null, { status: 401 });
664
+ await context.session.delete(context.session.id);
665
+ return new Response('Logged out', { status: 200 });
666
+ }
667
+ }
668
+ \`\`\`
669
+
670
+ ## Notes
671
+
672
+ - JWT authentication is **preferred over Basic Auth** when you want to avoid sending credentials on every request, when the client can store tokens, or when making multiple sequential requests. For simple or server-to-server scenarios, use Basic Authentication.
673
+ - Always use **HTTPS** in production to protect tokens in transit.
674
+ - Treat tokens like passwords. If a token is compromised, it remains valid until expiry — use shorter \`operationTokenTimeout\` values in high-security environments.
675
+ - \`enableSessions\` must be \`true\` in config before \`context.login\` or \`context.session\` will work.
676
+ - The \`third argument\` (\`true\`) to \`server.operation\` controls whether the operation runs as the current authenticated user. Omit it when the operation body supplies its own credentials.
677
+ - Scoped tokens have a 12KB limit when encoded into an \`Authorization\` header.
678
+ `,"creating-a-fabric-account-and-cluster":`---
679
+ name: creating-a-fabric-account-and-cluster
680
+ description: How to create a Harper Fabric account, organization, and cluster.
681
+ metadata:
682
+ mode: synthesized
683
+ ---
684
+
685
+ # Creating a Harper Fabric Account and Cluster
686
+
687
+ Follow these steps to set up your Harper Fabric environment for deployment.
688
+
689
+ ## How It Works
690
+
691
+ 1. **Sign Up/In**: Go to [https://fabric.harper.fast/](https://fabric.harper.fast/) and sign up or sign in.
692
+ 2. **Create an Organization**: Create an organization (org) to manage your projects.
693
+ 3. **Create a Cluster**: Create a new cluster. This can be on the free tier, no credit card required.
694
+ 4. **Set Credentials**: During setup, set the cluster username and password to finish configuring it.
695
+ 5. **Get Application URL**: Navigate to the **Config** tab and copy the **Application URL**.
696
+ 6. **Configure Environment**: Update your \`.env\` file or GitHub Actions secrets with cluster-specific credentials.
697
+ 7. **Next Steps**: See the [deploying-to-harper-fabric](deploying-to-harper-fabric.md) rule for detailed instructions on deploying your application successfully.
698
+
699
+ ## Examples
700
+
701
+ ### Environment Configuration
702
+
703
+ \`\`\`bash
704
+ CLI_TARGET_USERNAME='YOUR_CLUSTER_USERNAME'
705
+ CLI_TARGET_PASSWORD='YOUR_CLUSTER_PASSWORD'
706
+ CLI_TARGET='YOUR_CLUSTER_URL'
707
+ \`\`\`
708
+ `,"creating-harper-apps":`---
709
+ name: creating-harper-apps
710
+ description: How to initialize a new Harper application using the CLI.
711
+ metadata:
712
+ mode: synthesized
713
+ ---
714
+
715
+ # Creating Harper Applications
716
+
717
+ The fastest way to start a new Harper project is using the \`create-harper\` CLI tool. This command
718
+ initializes a project with a standard folder structure, essential configuration files, and basic
719
+ schema definitions.
720
+
721
+ ## When to Use
722
+
723
+ Use this command when starting a new Harper application or adding a new Harper microservice to an
724
+ existing architecture.
725
+
726
+ ## Commands
727
+
728
+ Initialize a project using your preferred package manager:
729
+
730
+ ### NPM
731
+
732
+ \`\`\`bash
733
+ npm create harper@latest
734
+ \`\`\`
735
+
736
+ ### PNPM
737
+
738
+ \`\`\`bash
739
+ pnpm create harper@latest
740
+ \`\`\`
741
+
742
+ ### Bun
743
+
744
+ \`\`\`bash
745
+ bun create harper@latest
746
+ \`\`\`
747
+
748
+ ## Options
749
+
750
+ You can specify the project name and template directly:
751
+
752
+ \`\`\`bash
753
+ npm create harper@latest my-app --template default
754
+ \`\`\`
755
+
756
+ ## Next Steps
757
+
758
+ 1. **Configure Environment**: Set up your \`.env\` file with local or cloud credentials.
759
+ 2. **Define Schema**: Modify \`schema.graphql\` to fit your application's data model.
760
+ 3. **Start Development**: Run \`npm run dev\` to start the local Harper instance.
761
+ 4. **Deploy**: Use \`npm run deploy\` to push your application to Harper Fabric.
762
+ `,"custom-resources":`---
763
+ name: custom-resources
764
+ description: How to define custom REST endpoints with JavaScript or TypeScript in Harper.
765
+ metadata:
766
+ mode: generate
767
+ sources:
768
+ - reference/v5/resources/overview.md#Custom External Data Source
769
+ - reference/v5/resources/overview.md#Exporting Resources as Endpoints
770
+ - reference/v5/components/javascript-environment.md#Module Loading
771
+ sourceCommit: f37a8c4021e20d5c74c1d339a6b6c8c196b5603e
772
+ inputHash: df69870433c0b3e5
773
+ ---
774
+
775
+ # Custom Resources
776
+
777
+ Instructions for the agent to follow when defining custom REST endpoints with JavaScript or TypeScript in Harper.
778
+
779
+ ## When to Use
780
+
781
+ 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 schema, or when a specific URL shape is required.
782
+
783
+ ## How It Works
784
+
785
+ 1. **Import \`Resource\` from \`harper\`**: Always import from the \`harper\` package rather than relying on globals.
786
+
787
+ \`\`\`javascript
788
+ import { tables, Resource } from 'harper';
789
+ \`\`\`
790
+
791
+ 2. **Define a class that \`extends Resource\`**: Implement HTTP methods as \`static\` methods. Each method receives a \`target\` object.
792
+
793
+ \`\`\`javascript
794
+ export class CustomEndpoint extends Resource {
795
+ static get(target) {
796
+ return {
797
+ data: doSomething(),
798
+ };
799
+ }
800
+ }
801
+ \`\`\`
802
+
803
+ 3. **Use \`async\` static methods for external calls**: Await fetch or other async operations inside \`static\` handlers.
804
+
805
+ \`\`\`javascript
806
+ export class MyExternalData extends Resource {
807
+ static async get(target) {
808
+ const response = await fetch(\`https://api.example.com/\${target.id}\`);
809
+ return response.json();
810
+ }
811
+
812
+ static async put(target, data) {
813
+ return fetch(\`https://api.example.com/\${target.id}\`, {
814
+ method: 'PUT',
815
+ body: JSON.stringify(await data),
816
+ });
817
+ }
818
+ }
819
+ \`\`\`
820
+
821
+ 4. **Export the class to create an endpoint**: The export form controls the resulting URL. Choose the form that matches the URL shape you need.
822
+
823
+ | Export form | URL | Notes |
824
+ | ------------------------------------------- | --------------- | --------------------------------------------------------------- |
825
+ | \`export class Foo extends Resource {}\` | \`/Foo/\` | Class name becomes the path segment. Case-sensitive. |
826
+ | \`export const Bar = { Foo };\` | \`/Bar/Foo/\` | Nest under an object to add a path prefix. |
827
+ | \`export const bar = { 'foo-baz': Foo };\` | \`/bar/foo-baz/\` | Use object keys for lowercase, hyphens, or non-identifier URLs. |
828
+ | \`export { Foo as '/widget/:id' }\` | \`/widget/:id\` | Rename the export to set the path directly. |
829
+ | \`static path = '/widget/:id'\` (class field) | \`/widget/:id\` | Declare path on the class; overrides the export name. |
830
+ | \`server.resources.set('my-path', Foo);\` | \`/my-path/\` | Programmatic registration for dynamic paths. |
831
+
832
+ URL path matching is case-sensitive — \`/Foo/\` and \`/foo/\` are different endpoints.
833
+
834
+ 5. **Declare path parameters with \`static path\`**: Use \`:name\` for a single segment and \`*name\` as a catch-all. Matched values are bound onto \`target.<name>\`.
835
+
836
+ \`\`\`javascript
837
+ export class Widget extends Resource {
838
+ static path = '/widget/:id/action/:action';
839
+ static get(target) {
840
+ return { id: target.id, action: target.action };
841
+ }
842
+ }
843
+ \`\`\`
844
+
845
+ A \`static path\` takes precedence over the export name. A leading \`/\` makes the path root-relative (top-level). A leading \`./\` or bare name resolves relative to the component directory.
846
+
847
+ 6. **Register programmatically when the path is dynamic**: Use \`server.resources.set(\` when the path cannot be known at export time.
848
+
849
+ \`\`\`javascript
850
+ server.resources.set('my-path', Foo);
851
+ \`\`\`
852
+
853
+ 7. **Optionally source a table from a custom resource**: Use the resource as a caching layer for a local table.
854
+ \`\`\`javascript
855
+ tables.MyCache.sourcedFrom(MyExternalData);
856
+ \`\`\`
857
+
858
+ ## Examples
859
+
860
+ ### External API wrapper with GET and PUT
861
+
862
+ \`\`\`javascript
863
+ import { tables, Resource } from 'harper';
864
+
865
+ export class MyExternalData extends Resource {
866
+ static async get(target) {
867
+ const response = await fetch(\`https://api.example.com/\${target.id}\`);
868
+ return response.json();
869
+ }
870
+
871
+ static async put(target, data) {
872
+ return fetch(\`https://api.example.com/\${target.id}\`, {
873
+ method: 'PUT',
874
+ body: JSON.stringify(await data),
875
+ });
876
+ }
877
+ }
878
+
879
+ // Use as a cache source for a local table
880
+ tables.MyCache.sourcedFrom(MyExternalData);
881
+ \`\`\`
882
+
883
+ ### Path parameters with \`static path\`
884
+
885
+ \`\`\`javascript
886
+ import { Resource } from 'harper';
887
+
888
+ export class Widget extends Resource {
889
+ // GET /widget/10/action/jump -> target.id === '10', target.action === 'jump'
890
+ static path = '/widget/:id/action/:action';
891
+ static get(target) {
892
+ return { id: target.id, action: target.action };
893
+ }
894
+ }
895
+
896
+ export class Files extends Resource {
897
+ // GET /files/a/b/c.txt -> target.rest === 'a/b/c.txt'
898
+ static path = '/files/*rest';
899
+ static get(target) {
900
+ return { path: target.rest };
901
+ }
902
+ }
903
+ \`\`\`
904
+
905
+ ### Programmatic registration
906
+
907
+ \`\`\`javascript
908
+ import { Resource } from 'harper';
909
+
910
+ export class Foo extends Resource {
911
+ static get(target) {
912
+ return { data: doSomething() };
913
+ }
914
+ }
915
+
916
+ server.resources.set('my-path', Foo);
917
+ \`\`\`
918
+
919
+ ## Notes
920
+
921
+ - A bare \`*\` wildcard (no name) binds under \`target.wildcard\`. A wildcard must be the final segment of the path.
922
+ - Resolution order: exact/static paths always win over parameterized ones. Among parameterized routes, more specific paths win — a literal segment beats \`:param\`, which beats \`*\`, compared left to right.
923
+ - Parameterized routes appear in the generated OpenAPI document as templated paths (e.g. \`/widget/{id}/action/{action}\`) and in MCP \`resources/templates/list\` as \`{param}\` URI templates.
924
+ - If a resource \`extends\` an existing table, avoid conflicting exports between the schema and the JavaScript implementation.
925
+ - Link the \`harper\` package in your component directory to ensure correct typings: \`npm link harper\`. All installed components have \`harper\` automatically linked.
926
+ - Harper runs as a single process — \`tables\`, \`databases\`, and other APIs are the same live, process-wide objects regardless of which component accesses them.
927
+ `,"defining-relationships":`---
928
+ name: defining-relationships
929
+ description: How to define and use relationships between tables in Harper using GraphQL.
930
+ metadata:
931
+ mode: generate
932
+ sources:
933
+ - reference/v5/database/schema.md#Relationships
934
+ - reference/v5/rest/querying.md#Relationships and Joins
935
+ sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
936
+ inputHash: fd399fd81a88f13e
937
+ ---
938
+
939
+ # Defining Relationships Between Tables in Harper
940
+
941
+ Instructions for the agent to follow when defining and querying relationships between tables in Harper using the \`@relationship\` directive.
942
+
943
+ ## When to Use
944
+
945
+ 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.
946
+
947
+ ## How It Works
948
+
949
+ 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.
950
+
951
+ \`\`\`graphql
952
+ type RealityShow @table @export {
953
+ id: Long @primaryKey
954
+ networkId: Long @indexed
955
+ network: Network @relationship(from: networkId) # many-to-one
956
+ title: String @indexed
957
+ }
958
+
959
+ type Network @table @export {
960
+ id: Long @primaryKey
961
+ name: String @indexed
962
+ }
963
+ \`\`\`
964
+
965
+ If the foreign key attribute is an array, the relationship becomes many-to-many:
966
+
967
+ \`\`\`graphql
968
+ type RealityShow @table @export {
969
+ id: Long @primaryKey
970
+ networkIds: [Long] @indexed
971
+ networks: [Network] @relationship(from: networkIds)
972
+ }
973
+ \`\`\`
974
+
975
+ 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.
976
+
977
+ \`\`\`graphql
978
+ type Network @table @export {
979
+ id: Long @primaryKey
980
+ name: String @indexed
981
+ shows: [RealityShow] @relationship(to: networkId) # one-to-many
982
+ }
983
+ \`\`\`
984
+
985
+ 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.
986
+
987
+ \`\`\`graphql
988
+ type OrderItem @table @export {
989
+ id: Long @primaryKey
990
+ orderId: Long @indexed
991
+ productSku: Long @indexed
992
+ products: [Product] @relationship(from: productSku, to: sku)
993
+ }
994
+
995
+ type Product @table @export {
996
+ id: Long @primaryKey
997
+ sku: Long @indexed
998
+ name: String
999
+ }
1000
+ \`\`\`
1001
+
1002
+ 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.
1003
+
1004
+ \`\`\`
1005
+ GET /Product/?brand.name=Microsoft
1006
+ GET /Brand/?products.name=Keyboard
1007
+ \`\`\`
1008
+
1009
+ 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 \`{}\`.
1010
+
1011
+ \`\`\`
1012
+ GET /Product/?brand.name=Microsoft&select(name,brand)
1013
+ GET /Product/?brand.name=Microsoft&select(name,brand{name})
1014
+ GET /Product/?name=Keyboard&select(name,brand{name,id})
1015
+ \`\`\`
1016
+
1017
+ 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.
1018
+
1019
+ 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.
1020
+
1021
+ \`\`\`graphql
1022
+ type Product @table @export {
1023
+ id: Long @primaryKey
1024
+ name: String
1025
+ resellerIds: [Long] @indexed
1026
+ resellers: [Reseller] @relationship(from: "resellerIds")
1027
+ }
1028
+ \`\`\`
1029
+
1030
+ 7. **Define self-referential relationships** for parent-child hierarchies by pointing \`@relationship\` back at the same table type.
1031
+
1032
+ ## Examples
1033
+
1034
+ **Full schema with bidirectional relationships:**
1035
+
1036
+ \`\`\`graphql
1037
+ type Product @table @export {
1038
+ id: Long @primaryKey
1039
+ name: String
1040
+ brandId: Long @indexed
1041
+ brand: Brand @relationship(from: "brandId")
1042
+ }
1043
+
1044
+ type Brand @table @export {
1045
+ id: Long @primaryKey
1046
+ name: String
1047
+ products: [Product] @relationship(to: "brandId")
1048
+ }
1049
+ \`\`\`
1050
+
1051
+ **Querying with joins and nested select:**
1052
+
1053
+ \`\`\`
1054
+ GET /Product/?brand.name=Microsoft&select(name,brand{name,id})
1055
+ GET /Brand/?products.name=Keyboard
1056
+ \`\`\`
1057
+
1058
+ **Many-to-many query with nested select:**
1059
+
1060
+ \`\`\`
1061
+ GET /Product/?resellers.name=Cool Shop&select(id,name,resellers{name,id})
1062
+ \`\`\`
1063
+
1064
+ ## Notes
1065
+
1066
+ - Every attribute named in \`from\` or \`to\` must exist on the respective table and be annotated with \`@indexed\` to support join queries.
1067
+ - The \`to\`-only and \`from\`+\`to\` forms both require the result field type to be an array (e.g., \`[RealityShow]\`).
1068
+ - The \`from\`-only form on a non-array attribute produces a many-to-one relationship; on an array attribute it produces many-to-many.
1069
+ - Self-referential relationships are supported for hierarchical data within a single table.
1070
+ `,"delegating-to-the-built-in-agent":`---
1071
+ name: delegating-to-the-built-in-agent
1072
+ description: How to delegate tasks to Harper's built-in agent via the CLI and the agent operations API.
1073
+ metadata:
1074
+ mode: synthesized
1075
+ ---
1076
+
1077
+ # Delegating to the Built-in Agent
1078
+
1079
+ Harper 5.2+ ships with a **built-in agent** that runs _inside_ the server, on the main thread
1080
+ adjacent to the operations API. Because it runs in-process, it can do things a remote client
1081
+ cannot: call the operations API as RBAC-filtered tools, read and write component files under the
1082
+ instance's components root, attach the V8 inspector to worker threads to debug and profile them,
1083
+ schedule follow-up work, and consult the Harper best-practices skill. You send it a natural-language
1084
+ task; it runs a tool-using loop under a super_user identity and reports back.
1085
+
1086
+ ## When to Use
1087
+
1088
+ Delegate to the built-in agent when the work is best done **on the instance itself** rather than
1089
+ from your local client:
1090
+
1091
+ - Operating on a deployed instance in place — inspect the schema, build or adjust a component,
1092
+ restart, run an operation.
1093
+ - Debugging or profiling a running instance — attach to a worker thread, capture a CPU profile,
1094
+ set a logpoint.
1095
+ - Handing off a larger, multi-step task to an agent that already has the instance's tools,
1096
+ filesystem, and credentials in context.
1097
+
1098
+ Do the work in your own client instead when it's purely local (editing source before deploy) or
1099
+ when you don't want a server-side agent making changes.
1100
+
1101
+ **Prerequisites:** the target instance must have the agent enabled (an \`agent:\` config block with
1102
+ \`enabled: true\`) and a configured generative model backend. All agent operations require
1103
+ **super_user**.
1104
+
1105
+ ## How It Works
1106
+
1107
+ The lifecycle assumes you have already deployed to and authenticated with the target instance (see
1108
+ [deploying-to-harper-fabric.md](deploying-to-harper-fabric.md) — \`harper login\` stores a token so
1109
+ you don't repeat credentials). Delegation reuses that same target and credentials.
1110
+
1111
+ There are two equivalent ways to drive the agent.
1112
+
1113
+ ### Option A — the \`harper agent\` CLI (simplest)
1114
+
1115
+ A thin client over the agent operations API that reuses your stored \`harper login\` credentials, so
1116
+ no connector setup is needed:
1117
+
1118
+ \`\`\`bash
1119
+ # One-shot: send a task, print the reply, exit
1120
+ harper agent "Describe the schema, then add a price index to the Product table."
1121
+
1122
+ # Interactive session (REPL)
1123
+ harper agent
1124
+
1125
+ # Against a specific remote instead of the logged-in default
1126
+ harper agent --target <Application URL> "List the databases and tables."
1127
+ \`\`\`
1128
+
1129
+ The CLI polls the run to completion and renders the transcript (tool calls, results, and the
1130
+ agent's reply). When a run needs approval for a destructive action, it prompts you inline.
1131
+
1132
+ ### Option B — the agent operations API (programmatic)
1133
+
1134
+ Call the operations API directly (HTTP POST to the ops endpoint, super_user auth). This is the path
1135
+ to use from scripts and services.
1136
+
1137
+ 1. **Start a task** with \`agent_prompt\`. Returns a \`session_id\` and a \`status\`.
1138
+
1139
+ \`\`\`bash
1140
+ curl -s -u <user>:<pass> <ops-endpoint> \\
1141
+ -H 'Content-Type: application/json' \\
1142
+ -d '{"operation":"agent_prompt","message":"Build a Customer table (id, email, name) exported over REST."}'
1143
+ \`\`\`
1144
+
1145
+ 2. **Poll for progress** with \`get_agent_session\`, passing the \`session_id\`. The returned session
1146
+ carries the \`status\`, the \`messages\` transcript, and any \`pendingApprovals\`.
1147
+
1148
+ \`\`\`bash
1149
+ curl -s -u <user>:<pass> <ops-endpoint> \\
1150
+ -H 'Content-Type: application/json' \\
1151
+ -d '{"operation":"get_agent_session","session_id":"<id>"}'
1152
+ \`\`\`
1153
+
1154
+ Poll until \`status\` leaves \`running\` — terminal states are \`completed\`, \`aborted\`, and \`error\`;
1155
+ \`awaiting_approval\` means it is paused for an approval decision (see step 3).
1156
+
1157
+ 3. **Approve or deny a paused action.** When the agent enabled configuration has \`autoApprove:false\`,
1158
+ a destructive tool call pauses the run with a \`pendingApprovals[]\` entry. Resolve it with
1159
+ \`approve_agent_action\`, then poll again — approval executes the saved call, denial hands the
1160
+ rejection back to the agent so it can adjust.
1161
+
1162
+ \`\`\`bash
1163
+ curl -s -u <user>:<pass> <ops-endpoint> \\
1164
+ -H 'Content-Type: application/json' \\
1165
+ -d '{"operation":"approve_agent_action","session_id":"<id>","approval_id":"<approval-id>","approved":true}'
1166
+ \`\`\`
1167
+
1168
+ 4. **Continue the conversation** by passing the same \`session_id\` back into \`agent_prompt\` with a
1169
+ new \`message\`. Omit \`session_id\` to start a fresh session.
1170
+
1171
+ Supporting operations: \`list_agent_sessions\` (recent sessions), \`cancel_agent_run\` (terminate a
1172
+ running or paused session), and \`set_agent_config\` (adjust \`autoApprove\`, \`allowDestructive\`,
1173
+ \`model\`, and related settings on a running instance).
1174
+
1175
+ ## Examples
1176
+
1177
+ **Delegate a build to a deployed Fabric instance and wait for the result:**
1178
+
1179
+ \`\`\`bash
1180
+ harper login <Application URL>
1181
+ harper agent --target <Application URL> \\
1182
+ "Create a Product table (id, name, price) exported over REST, then confirm the endpoint responds."
1183
+ \`\`\`
1184
+
1185
+ **Programmatic start-and-poll loop:**
1186
+
1187
+ \`\`\`bash
1188
+ SID=$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \\
1189
+ -d '{"operation":"agent_prompt","message":"Add a vector index to the Document.embedding field."}' \\
1190
+ | jq -r .session_id)
1191
+
1192
+ while [ "$(curl -s -u <user>:<pass> <ops-endpoint> -H 'Content-Type: application/json' \\
1193
+ -d "{\\"operation\\":\\"get_agent_session\\",\\"session_id\\":\\"$SID\\"}" | jq -r .status)" = "running" ]; do
1194
+ sleep 3
1195
+ done
1196
+ \`\`\`
1197
+
1198
+ ## Notes
1199
+
1200
+ - **All agent operations require super_user.** Authenticate with \`harper login\`, which stores a
1201
+ short-lived JWT (operation token) plus a refresh token rather than your password — prefer that
1202
+ over passing credentials inline, and never embed a raw password in scripts or client config.
1203
+ - **Approvals are your safety gate.** With \`autoApprove:false\`, the agent pauses before destructive
1204
+ tools (writing files, deploying, restarting) so an operator decides. Set \`autoApprove:true\` only
1205
+ when you want unattended runs.
1206
+ - **Sessions are single-active.** A session that is \`running\` or \`awaiting_approval\` rejects a new
1207
+ \`agent_prompt\`; resolve the approval or cancel the run first.
1208
+ - **MCP alternative.** For MCP-native clients, an instance with MCP enabled exposes the agent as
1209
+ curated MCP tools (\`agent_prompt\`, \`get_agent_session\`, \`list_agent_sessions\`) at the ops API's
1210
+ \`/mcp\` endpoint — the same delegation loop over the MCP transport instead of raw operations.
1211
+ `,"deploying-to-harper-fabric":`---
1212
+ name: deploying-to-harper-fabric
1213
+ description: How to deploy a Harper application to the Harper Fabric cloud.
1214
+ metadata:
1215
+ mode: generate
1216
+ sources:
1217
+ - reference/v5/components/applications.md#Remote Management
1218
+ - >-
1219
+ fabric/cluster-creation-management.md#Connecting the Harper CLI to a
1220
+ Cluster
1221
+ sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
1222
+ inputHash: e5586ad2bcc7c12a
1223
+ ---
1224
+
1225
+ # Deploying to Harper Fabric
1226
+
1227
+ Instructions for the agent to follow when deploying a Harper application to a remote Harper Fabric cloud cluster.
1228
+
1229
+ ## When to Use
1230
+
1231
+ Apply this rule when deploying a Harper application to a remote Harper Fabric cluster or any remote Harper instance. This includes first-time deploys, redeployments, rollbacks, CI/CD pipeline deploys, and provisioning credentials for private repositories. See [creating-a-fabric-account-and-cluster.md](creating-a-fabric-account-and-cluster.md) for setting up the cluster before deploying.
1232
+
1233
+ ## How It Works
1234
+
1235
+ 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\`.
1236
+
1237
+ \`\`\`bash
1238
+ harper login <Application URL>
1239
+ # Provide cluster username and password when prompted
1240
+ \`\`\`
1241
+
1242
+ 2. **Deploy the application**: After login, run \`harper deploy\` without repeating credentials. Set \`restart=true\` and \`replicated=true\` for a production deploy.
1243
+
1244
+ \`\`\`bash
1245
+ harper deploy \\
1246
+ project=<name> \\
1247
+ package=<package> \\
1248
+ target=<remote> \\
1249
+ restart=true \\
1250
+ replicated=true
1251
+ \`\`\`
1252
+
1253
+ 3. **Use environment variables for CI/CD**: Instead of \`harper login\`, export credentials as environment variables before calling \`harper deploy\`.
1254
+
1255
+ \`\`\`bash
1256
+ export HARPER_CLI_USERNAME=<username>
1257
+ export HARPER_CLI_PASSWORD=<password>
1258
+ harper deploy \\
1259
+ project=<name> \\
1260
+ package=<package> \\
1261
+ target=<remote> \\
1262
+ restart=true \\
1263
+ replicated=true
1264
+ \`\`\`
1265
+
1266
+ 4. **Choose a package source**: The \`package\` field accepts any valid npm dependency value. Select the form that matches your source:
1267
+
1268
+ | Source | \`package\` value |
1269
+ | ----------------------- | ---------------------------------------------------- |
1270
+ | Current local directory | Omit \`package\` |
1271
+ | npm package | \`"@harperdb/status-check"\` |
1272
+ | GitHub (public) | \`"HarperFast/status-check"\` or full URL |
1273
+ | Private repo (SSH) | \`"git+ssh://git@github.com:HarperDB/secret-app.git"\` |
1274
+ | Tarball | \`"https://example.com/application.tar.gz"\` |
1275
+
1276
+ For git tags, use the \`semver\` directive:
1277
+
1278
+ \`\`\`
1279
+ HarperFast/application-template#semver:v1.0.0
1280
+ \`\`\`
1281
+
1282
+ 5. **Deploy by reference for reproducible deploys**: Pass \`by_ref=true\` to send a pinned git SHA instead of uploading a snapshot. The cluster fetches and builds from that exact commit.
1283
+
1284
+ \`\`\`bash
1285
+ harper deploy by_ref=true restart=true replicated=true
1286
+ \`\`\`
1287
+
1288
+ Use \`ref\` to target a specific commit, tag, or branch (resolved to a full SHA before sending):
1289
+
1290
+ \`\`\`bash
1291
+ # Deploy a specific tag
1292
+ harper deploy ref=v1.2.0 restart=true replicated=true
1293
+
1294
+ # Roll back by deploying an older commit
1295
+ harper deploy ref=9f8c2a1 restart=true replicated=true
1296
+ \`\`\`
1297
+
1298
+ **Key constraints for \`ref\` values:**
1299
+ - Must name something a clone can fetch: \`refs/heads/*\` and \`refs/tags/*\`, or a bare branch or tag name.
1300
+ - Anything else (e.g., \`refs/pull/123/head\`) is rejected up front.
1301
+ - If a ref can't be resolved, the deploy stops — run \`git fetch\` and retry, or pass a full commit SHA.
1302
+ - Commit and push before deploying: the cluster clones from the remote and only sees pushed commits.
1303
+
1304
+ 6. **Deploy private repositories by reference**: Pass \`credential=true\` alongside \`by_ref=true\`. The CLI attaches a credentials reference; the cluster resolves the secret in memory at clone time — no token travels in the operation body or lands on disk.
1305
+
1306
+ \`\`\`bash
1307
+ harper deploy by_ref=true credential=true restart=true replicated=true
1308
+ \`\`\`
1309
+
1310
+ 7. **Provision a deploy credential for private sources**: Run \`harper deploy setup=true\` once per component and source. This is interactive and requires **super_user** — run it with an administrative credential, not the CI identity.
1311
+
1312
+ \`\`\`bash
1313
+ harper deploy setup=true
1314
+ \`\`\`
1315
+
1316
+ This command:
1317
+ 1. Fetches the cluster's public key with \`get_secrets_public_key\`.
1318
+ 2. Encrypts the token locally into an \`enc:v1:\` envelope.
1319
+ 3. Stores only the ciphertext with \`set_secret\`, in the component-scoped tier.
1320
+ 4. Grants the component permission to resolve it with \`grant_secret\`.
1321
+ 5. Prints the \`credentials\` reference for the deploy to use.
1322
+
1323
+ Use a **fine-grained** personal access token (PAT) scoped to **Contents: Read-only** on the specific repository. Avoid session tokens from \`gh\` CLI — they typically carry \`repo\`, \`read:org\`, \`gist\`, and \`workflow\` scopes across your whole account.
1324
+
1325
+ ## Examples
1326
+
1327
+ **Standard deploy after login:**
1328
+
1329
+ \`\`\`bash
1330
+ harper login https://my-cluster.harperdbcloud.com
1331
+ harper deploy \\
1332
+ project=my-app \\
1333
+ package="HarperFast/my-app" \\
1334
+ target=https://my-cluster.harperdbcloud.com \\
1335
+ restart=true \\
1336
+ replicated=true
1337
+ \`\`\`
1338
+
1339
+ **CI/CD deploy using environment variables:**
1340
+
1341
+ \`\`\`bash
1342
+ export HARPER_CLI_USERNAME=admin
1343
+ export HARPER_CLI_PASSWORD=secret
1344
+ harper deploy \\
1345
+ project=my-app \\
1346
+ package="HarperFast/my-app" \\
1347
+ target=https://my-cluster.harperdbcloud.com \\
1348
+ restart=true \\
1349
+ replicated=true
1350
+ \`\`\`
1351
+
1352
+ **Deploy by reference in GitHub Actions (pull request):**
1353
+
1354
+ \`\`\`bash
1355
+ harper deploy ref=\${{ github.event.pull_request.head.sha }} restart=true replicated=true
1356
+ \`\`\`
1357
+
1358
+ **Deploy a private repo by reference with a provisioned credential:**
1359
+
1360
+ \`\`\`bash
1361
+ # Provision once (run as super_user)
1362
+ harper deploy setup=true
1363
+
1364
+ # Deploy subsequently
1365
+ harper deploy by_ref=true credential=true restart=true replicated=true
1366
+ \`\`\`
1367
+
1368
+ ## Notes
1369
+
1370
+ - \`auth_username\` and \`auth_password\` can be passed directly as deploy parameters for one-off commands, but this is not recommended for production. Dedicated authentication parameters take precedence over environment variables and saved login tokens.
1371
+ - The \`enc:v1:\` envelope means the plaintext token never leaves your machine — only ciphertext is stored and replicated.
1372
+ - Deploy credentials are stored scoped to the component, never in the global \`processEnv\` tier. If a global secret exists at the derived name, it is converted to the component-scoped tier automatically.
1373
+ - Because stored credentials are durable, later deploys and rollbacks reuse them without re-entering anything.
1374
+ - The unpushed-commit check is skipped under GitHub Actions; the dirty-tree warning still applies.
1375
+ - Deploying by reference means the cluster installs and builds from source. If your application requires a build step that cannot run on the node, deploy the built output as a payload deploy instead.
1376
+ - For SSH-based private repos, use the \`add_ssh_key\` operation to register keys before deploying.
1377
+ `,"extending-tables":`---
1378
+ name: extending-tables
1379
+ description: How to add custom logic to automatically generated table resources in Harper.
1380
+ metadata:
1381
+ mode: generate
1382
+ sources:
1383
+ - reference/v5/resources/overview.md#Extending a Table
1384
+ - reference/v5/resources/resource-api.md#Throwing Errors
1385
+ sourceCommit: ce0ab713d918d789bc1c9f22e461e963ccc1dff1
1386
+ inputHash: 19738fbc732e0a1a
1387
+ ---
1388
+
1389
+ # Extending Tables
1390
+
1391
+ Instructions for the agent to follow when adding custom logic to automatically generated table resources in Harper.
1392
+
1393
+ ## When to Use
1394
+
1395
+ Apply this rule when you need to add computed properties, intercept writes, enforce validation, or otherwise customize the behavior of a Harper table resource beyond what the default generated endpoints provide. Use it any time a \`@table\` type needs server-side logic attached to its REST handlers.
1396
+
1397
+ ## How It Works
1398
+
1399
+ 1. **Define the schema without \`@export\`**: Declare the table type in \`schema.graphql\` and omit the \`@export\` directive. Leaving \`@export\` on the schema while also exporting a subclass with the same name produces conflicting endpoints. Let the JavaScript class own the URL instead.
1400
+
1401
+ \`\`\`graphql
1402
+ # Omit the \`@export\` directive
1403
+ type MyTable @table {
1404
+ id: Long @primaryKey
1405
+ # ...
1406
+ }
1407
+ \`\`\`
1408
+
1409
+ 2. **Extend the generated table class**: In \`resources.js\`, extend from the \`tables.<TypeName>\` global. The class name you export becomes the URL path. The exported class extends tables.
1410
+
1411
+ \`\`\`javascript
1412
+ export class MyTable extends tables.MyTable {
1413
+ static async get(target) {
1414
+ const record = await super.get(target);
1415
+ return { ...record, computedField: 'value' };
1416
+ }
1417
+
1418
+ static async post(target, data) {
1419
+ this.create({ ...(await data), status: 'pending' });
1420
+ }
1421
+ }
1422
+ \`\`\`
1423
+
1424
+ 3. **Call \`super\` to preserve default behavior**: When delegating to \`super\`, match the argument form to the operation:
1425
+ - Reads/deletes: \`super.get(target)\` / \`super.delete(target)\`
1426
+ - Collection create: \`super.post(target, record)\` — target carries no id
1427
+ - Updates: \`super.put(target, data)\` / \`super.patch(target, data)\`
1428
+
1429
+ Omit the \`super\` call only if you intend to replace the default behavior entirely.
1430
+
1431
+ 4. **Set \`statusCode\` on thrown errors to control HTTP responses**: Uncaught errors are caught by the protocol handler and produce error responses for REST. Use \`.statusCode\` — a plain \`.status\` property is ignored.
1432
+
1433
+ \`\`\`javascript
1434
+ const error = new Error('Name is required');
1435
+ error.statusCode = 400; // use statusCode, NOT status
1436
+ throw error;
1437
+ \`\`\`
1438
+
1439
+ 5. **Configure Harper to load both files**: Ensure your configuration references the schema and resource files.
1440
+
1441
+ \`\`\`yaml
1442
+ rest: true
1443
+ graphqlSchema:
1444
+ files: schema.graphql
1445
+ jsResource:
1446
+ files: resources.js
1447
+ \`\`\`
1448
+
1449
+ ## Examples
1450
+
1451
+ Full end-to-end example — schema, resource class, and error handling:
1452
+
1453
+ \`\`\`graphql
1454
+ # schema.graphql — omit @export so the JS class owns the endpoint
1455
+ type MyTable @table {
1456
+ id: Long @primaryKey
1457
+ }
1458
+ \`\`\`
1459
+
1460
+ \`\`\`javascript
1461
+ // resources.js
1462
+ export class MyTable extends tables.MyTable {
1463
+ static async get(target) {
1464
+ // get the record from the database
1465
+ const record = await super.get(target);
1466
+ // add a computed property before returning
1467
+ return { ...record, computedField: 'value' };
1468
+ }
1469
+
1470
+ static async post(target, data) {
1471
+ // custom action on POST
1472
+ this.create({ ...(await data), status: 'pending' });
1473
+ }
1474
+ }
1475
+ \`\`\`
1476
+
1477
+ Throwing a controlled HTTP error:
1478
+
1479
+ \`\`\`javascript
1480
+ if (!authorized) {
1481
+ const error = new Error('Forbidden');
1482
+ error.statusCode = 403;
1483
+ throw error;
1484
+ }
1485
+ \`\`\`
1486
+
1487
+ ## Notes
1488
+
1489
+ - Always omit \`@export\` from the schema type when a JavaScript subclass is exporting the same name. The two registrations conflict.
1490
+ - \`super\` must be called with the correct arguments for each operation type — mismatched arguments will not behave as expected.
1491
+ - \`statusCode\` is the only recognized property for controlling HTTP status on thrown errors; \`.status\` is ignored.
1492
+ `,"handling-binary-data":`---
1493
+ name: handling-binary-data
1494
+ description: How to store and serve binary data like images or audio in Harper.
1495
+ metadata:
1496
+ mode: generate
1497
+ sources:
1498
+ - reference/v5/database/api.md#Accepting Binary in JSON Requests
1499
+ - reference/v5/database/api.md#Serving Binary from a Resource
1500
+ - reference/v5/rest/content-types.md#Storing Arbitrary Content Types
1501
+ sourceCommit: ce0ab713d918d789bc1c9f22e461e963ccc1dff1
1502
+ inputHash: fa06480e6fae7614
1503
+ ---
1504
+
1505
+ # Handling Binary Data
1506
+
1507
+ Instructions for the agent to follow when storing and serving binary data (images, audio, arbitrary content types) in Harper.
1508
+
1509
+ ## When to Use
1510
+
1511
+ 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 REST clients send \`base64\`-encoded data inside JSON, when raw binary is uploaded via \`PUT\`/\`POST\`, or when a resource must stream binary back to the client with the correct \`Content-Type\`.
1512
+
1513
+ ## How It Works
1514
+
1515
+ 1. **Accept base64-encoded binary from JSON clients**: Decode the incoming \`base64\` string with \`Buffer.from\` and wrap it using \`createBlob\`, recording the MIME type. Override \`post\` in your resource class:
1516
+
1517
+ \`\`\`typescript
1518
+ import { type RequestTargetOrId, tables, createBlob } from 'harper';
1519
+
1520
+ export class Photo extends tables.Photo {
1521
+ static async post(target: RequestTargetOrId, record: any) {
1522
+ if (record.data) {
1523
+ record.data = createBlob(Buffer.from(record.data, record.encoding || 'base64'), {
1524
+ type: record.contentType || 'application/octet-stream',
1525
+ });
1526
+ }
1527
+ return super.post(target, record);
1528
+ }
1529
+ }
1530
+ \`\`\`
1531
+
1532
+ 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:
1533
+
1534
+ \`\`\`typescript
1535
+ export class Photo extends tables.Photo {
1536
+ static async get(target: RequestTargetOrId) {
1537
+ const record = await super.get(target);
1538
+ if (record?.data) {
1539
+ return {
1540
+ status: 200,
1541
+ headers: { 'Content-Type': record.data.type || 'application/octet-stream' },
1542
+ body: record.data,
1543
+ };
1544
+ }
1545
+ return record;
1546
+ }
1547
+ }
1548
+ \`\`\`
1549
+
1550
+ 3. **Upload raw binary with a non-standard content type**: Make a \`PUT\` or \`POST\` with any non-standard \`Content-Type\` header. Harper automatically stores the body as a record with \`contentType\` and \`data\` properties:
1551
+
1552
+ \`\`\`http
1553
+ PUT /my-resource/33
1554
+ Content-Type: text/calendar
1555
+
1556
+ BEGIN:VCALENDAR
1557
+ VERSION:2.0
1558
+ ...
1559
+ \`\`\`
1560
+
1561
+ Harper stores this as:
1562
+
1563
+ \`\`\`json
1564
+ { "contentType": "text/calendar", "data": "BEGIN:VCALENDAR\\nVERSION:2.0\\n..." }
1565
+ \`\`\`
1566
+
1567
+ Retrieving that record returns the response with the stored \`Content-Type\` and body. If the content type is not from the \`text\` family, the data is treated as binary (a Node.js \`Buffer\`).
1568
+
1569
+ 4. **Upload binary to a specific property**: Use \`application/octet-stream\` (or any image/binary MIME type) and target a sub-path to store binary directly on a property:
1570
+
1571
+ \`\`\`http
1572
+ PUT /my-resource/33/image
1573
+ Content-Type: image/gif
1574
+
1575
+ ...image data...
1576
+ \`\`\`
1577
+
1578
+ ## Examples
1579
+
1580
+ **End-to-end: accept base64 JSON, store as blob, serve as binary**
1581
+
1582
+ \`\`\`typescript
1583
+ import { type RequestTargetOrId, tables, createBlob } from 'harper';
1584
+
1585
+ export class Photo extends tables.Photo {
1586
+ // Accept base64-encoded uploads in JSON
1587
+ static async post(target: RequestTargetOrId, record: any) {
1588
+ if (record.data) {
1589
+ record.data = createBlob(Buffer.from(record.data, record.encoding || 'base64'), {
1590
+ type: record.contentType || 'application/octet-stream',
1591
+ });
1592
+ }
1593
+ return super.post(target, record);
1594
+ }
1595
+
1596
+ // Stream the blob back with the correct Content-Type
1597
+ static async get(target: RequestTargetOrId) {
1598
+ const record = await super.get(target);
1599
+ if (record?.data) {
1600
+ return {
1601
+ status: 200,
1602
+ headers: { 'Content-Type': record.data.type || 'application/octet-stream' },
1603
+ body: record.data,
1604
+ };
1605
+ }
1606
+ return record;
1607
+ }
1608
+ }
1609
+ \`\`\`
1610
+
1611
+ ## Notes
1612
+
1613
+ - \`createBlob\` takes a \`Buffer\` as its first argument and an options object with a \`type\` property for the MIME type. See [using-blob-datatype.md](using-blob-datatype.md) for full details on the blob data type.
1614
+ - Always fall back to \`application/octet-stream\` when no MIME type is known, both when creating and when serving blobs.
1615
+ - When Harper retrieves a record that has both \`contentType\` and \`data\` properties, it automatically sets the response \`Content-Type\` and body — no custom \`get\` override is required for that case unless you need additional logic.
1616
+ - Non-\`text\` content types cause \`data\` to be stored and returned as a Node.js \`Buffer\`.
1617
+ `,"load-env":`---
1618
+ name: load-env
1619
+ description: >-
1620
+ How to load environment variables from .env files into a Harper application
1621
+ using the loadEnv plugin.
1622
+ metadata:
1623
+ mode: generate
1624
+ sources:
1625
+ - reference/v5/environment-variables/overview.md
1626
+ sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
1627
+ inputHash: b4db5ede6b93d426
1628
+ ---
1629
+
1630
+ # Load Environment Variables with loadEnv
1631
+
1632
+ Instructions for the agent to follow when loading environment variables from \`.env\` files into a Harper application using the \`loadEnv\` plugin.
1633
+
1634
+ ## When to Use
1635
+
1636
+ 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.
1637
+
1638
+ ## How It Works
1639
+
1640
+ 1. **Declare \`loadEnv\` in \`config.yaml\`**: Add \`loadEnv\` as the first entry in \`config.yaml\`. It is built into Harper and requires no installation.
1641
+
1642
+ \`\`\`yaml
1643
+ loadEnv:
1644
+ files: '.env'
1645
+ \`\`\`
1646
+
1647
+ 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.
1648
+
1649
+ \`\`\`yaml
1650
+ # config.yaml — loadEnv must come first
1651
+ loadEnv:
1652
+ files: '.env'
1653
+
1654
+ rest: true
1655
+
1656
+ myApp:
1657
+ files: './src/*.js'
1658
+ \`\`\`
1659
+
1660
+ 3. **Access loaded values in component code**: After \`loadEnv\` runs, all loaded values are available on \`process.env\` and shared across all components.
1661
+
1662
+ 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.
1663
+
1664
+ \`\`\`yaml
1665
+ loadEnv:
1666
+ files: '.env'
1667
+ override: true
1668
+ \`\`\`
1669
+
1670
+ 5. **Load multiple files**: Pass an array of paths or a glob pattern to \`files\`. Files are loaded in the order specified.
1671
+
1672
+ \`\`\`yaml
1673
+ loadEnv:
1674
+ files:
1675
+ - '.env'
1676
+ - '.env.local'
1677
+ \`\`\`
1678
+
1679
+ or with a glob:
1680
+
1681
+ \`\`\`yaml
1682
+ loadEnv:
1683
+ files: 'env-vars/*'
1684
+ \`\`\`
1685
+
1686
+ ### Configuration Options
1687
+
1688
+ | Option | Type | Required | Description |
1689
+ | ---------- | -------------------- | -------- | -------------------------------------------------------------------------------------- |
1690
+ | \`files\` | \`string \\| string[]\` | **Yes** | Path(s) or glob pattern(s) to the env file(s) to load. |
1691
+ | \`override\` | \`boolean\` | No | If \`true\`, loaded values override existing environment variables. Defaults to \`false\`. |
1692
+
1693
+ ## Examples
1694
+
1695
+ **Single file, default behavior:**
1696
+
1697
+ \`\`\`yaml
1698
+ # config.yaml
1699
+ loadEnv:
1700
+ files: '.env'
1701
+
1702
+ rest: true
1703
+
1704
+ myApp:
1705
+ files: './src/*.js'
1706
+ \`\`\`
1707
+
1708
+ **Multiple files with override:**
1709
+
1710
+ \`\`\`yaml
1711
+ # config.yaml
1712
+ loadEnv:
1713
+ files:
1714
+ - '.env'
1715
+ - '.env.local'
1716
+ override: true
1717
+
1718
+ rest: true
1719
+
1720
+ myApp:
1721
+ files: './src/*.js'
1722
+ \`\`\`
1723
+
1724
+ ## Notes
1725
+
1726
+ - \`loadEnv\` loads values into \`process.env\` for **application** code only — it does not configure Harper itself.
1727
+ - 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.
1728
+ - For production credentials, prefer the encrypted secrets store over a committed \`.env\` file. Secrets are also delivered to components via \`process.env\`.
1729
+ `,logging:`---
1730
+ name: logging
1731
+ description: >-
1732
+ Best practices for logging in Harper, including console capture, the granular
1733
+ logger interface, and programmatic log retrieval.
1734
+ metadata:
1735
+ mode: generate
1736
+ sources:
1737
+ - reference/v5/logging/overview.md
1738
+ - reference/v5/logging/api.md
1739
+ sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
1740
+ inputHash: 46cd384598304e3b
1741
+ ---
1742
+
1743
+ # Harper Logging
1744
+
1745
+ Instructions for the agent to follow when implementing logging in Harper applications, including direct logger usage, tagged loggers, and console capture behavior.
1746
+
1747
+ ## When to Use
1748
+
1749
+ 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.
1750
+
1751
+ ## How It Works
1752
+
1753
+ 1. **Use the \`logger\` global directly** — \`logger\` is available in all JavaScript components without any imports. Call the method matching the desired severity level:
1754
+
1755
+ \`\`\`javascript
1756
+ logger.trace('detailed trace message');
1757
+ logger.debug('debug info', { someContext: 'value' });
1758
+ logger.info('informational message');
1759
+ logger.warn('potential issue');
1760
+ logger.error('error occurred', error);
1761
+ logger.fatal('fatal error');
1762
+ logger.notify('server is ready');
1763
+ \`\`\`
1764
+
1765
+ Only entries at or above the configured \`logging.level\` (or \`logging.external.level\`) are written to \`hdb.log\`.
1766
+
1767
+ 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.
1768
+
1769
+ \`\`\`javascript
1770
+ const log = logger.withTag('my-resource');
1771
+ \`\`\`
1772
+
1773
+ Because \`TaggedLogger\` methods for disabled levels are \`null\`, always use optional chaining (\`?.\`) when calling them:
1774
+
1775
+ \`\`\`javascript
1776
+ log.debug?.('Fetching record', { id });
1777
+ log.warn?.('Record not found', { id });
1778
+ log.error?.('Failed to update record', err);
1779
+ \`\`\`
1780
+
1781
+ \`TaggedLogger\` does not have a \`withTag()\` method.
1782
+
1783
+ 3. **Understand the interface contracts** — \`MainLogger\` always has all methods defined:
1784
+
1785
+ \`\`\`typescript
1786
+ interface MainLogger {
1787
+ trace(...messages: any[]): void;
1788
+ debug(...messages: any[]): void;
1789
+ info(...messages: any[]): void;
1790
+ warn(...messages: any[]): void;
1791
+ error(...messages: any[]): void;
1792
+ fatal(...messages: any[]): void;
1793
+ notify(...messages: any[]): void;
1794
+ withTag(tag: string): TaggedLogger;
1795
+ }
1796
+ \`\`\`
1797
+
1798
+ \`TaggedLogger\` methods may be \`null\`:
1799
+
1800
+ \`\`\`typescript
1801
+ interface TaggedLogger {
1802
+ trace: ((...messages: any[]) => void) | null;
1803
+ debug: ((...messages: any[]) => void) | null;
1804
+ info: ((...messages: any[]) => void) | null;
1805
+ warn: ((...messages: any[]) => void) | null;
1806
+ error: ((...messages: any[]) => void) | null;
1807
+ fatal: ((...messages: any[]) => void) | null;
1808
+ notify: ((...messages: any[]) => void) | null;
1809
+ }
1810
+ \`\`\`
1811
+
1812
+ 4. **Know the log levels** — From least to most severe:
1813
+
1814
+ | Level | Description |
1815
+ | -------- | -------------------------------------------------------------------- |
1816
+ | \`trace\` | Highly detailed internal execution tracing. |
1817
+ | \`debug\` | Diagnostic information useful during development. |
1818
+ | \`info\` | General operational events. |
1819
+ | \`warn\` | Potential issues that don't prevent normal operation. |
1820
+ | \`error\` | Errors that affect specific operations. |
1821
+ | \`fatal\` | Critical errors causing process termination. |
1822
+ | \`notify\` | Important operational milestones. Always logged regardless of level. |
1823
+
1824
+ The default log level is \`warn\`. Setting a level includes that level and all more-severe levels.
1825
+
1826
+ 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.
1827
+
1828
+ 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\`.
1829
+
1830
+ ## Examples
1831
+
1832
+ ### Basic logging in a resource
1833
+
1834
+ \`\`\`javascript
1835
+ export class MyResource extends Resource {
1836
+ async get(id) {
1837
+ logger.debug('Fetching record', { id });
1838
+ const record = await super.get(id);
1839
+ if (!record) {
1840
+ logger.warn('Record not found', { id });
1841
+ }
1842
+ return record;
1843
+ }
1844
+
1845
+ async put(record) {
1846
+ logger.info('Updating record', { id: record.id });
1847
+ try {
1848
+ return await super.put(record);
1849
+ } catch (err) {
1850
+ logger.error('Failed to update record', err);
1851
+ throw err;
1852
+ }
1853
+ }
1854
+ }
1855
+ \`\`\`
1856
+
1857
+ ### Tagged logging with \`withTag()\`
1858
+
1859
+ \`\`\`javascript
1860
+ const log = logger.withTag('my-resource');
1861
+
1862
+ export class MyResource extends Resource {
1863
+ async get(id) {
1864
+ log.debug?.('Fetching record', { id });
1865
+ const record = await super.get(id);
1866
+ if (!record) {
1867
+ log.warn?.('Record not found', { id });
1868
+ }
1869
+ return record;
1870
+ }
1871
+
1872
+ async put(record) {
1873
+ log.info?.('Updating record', { id: record.id });
1874
+ try {
1875
+ return await super.put(record);
1876
+ } catch (err) {
1877
+ log.error?.('Failed to update record', err);
1878
+ throw err;
1879
+ }
1880
+ }
1881
+ }
1882
+ \`\`\`
1883
+
1884
+ Tagged entries appear in \`hdb.log\` with the tag in the header:
1885
+
1886
+ \`\`\`
1887
+ 2023-03-09T14:25:05.269Z [info] [my-resource]: Updating record
1888
+ \`\`\`
1889
+
1890
+ ## Notes
1891
+
1892
+ - All log output is written to \`<ROOTPATH>/log/hdb.log\`. The \`logger\` global writes to this file at the configured \`logging.external\` level.
1893
+ - Log entry format for \`logger\`: \`<timestamp> [<level>] [<thread>/<id>]: <message>\`
1894
+ - Log entry format for \`TaggedLogger\`: \`<timestamp> [<level>] [<tag>]: <message>\`
1895
+ - \`console.log\` output is only forwarded to \`hdb.log\` when \`logging.console: true\` is explicitly set; it is not forwarded by default.
1896
+ - When logging to standard streams, run Harper in the foreground (\`harper\`, not \`harper start\`).
1897
+ - \`TaggedLogger\` is bound to the configured log level at creation time — always use \`?.\` on its methods.
1898
+ `,"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 Loading\n sourceCommit: 677ad213d67822e109c83619e181ca23a59823db\n inputHash: ce2181ecade6522f\n---\n\n# Programmatic Table Requests\n\nInstructions for the agent to interact with Harper tables programmatically using the `tables` object, including querying, transactions, and module integration.\n\n## When to Use\n\nApply this rule when writing server-side Harper component code that reads from or writes to tables directly — bypassing REST endpoints — such as in request handlers, background jobs, timers, or SSR rendering. Use it whenever you need to construct queries with `conditions`, manage transactions explicitly, or perform CRDT-safe mutations.\n\n## How It Works\n\n1. **Import `tables` from `harper`**: Access all tables in the default `data` database via the `tables` object. Each table defined with `@table` in `schema.graphql` is a property.\n\n ```javascript\n import { tables } from 'harper';\n const { Product } = tables;\n // same as: databases.data.Product\n ```\n\n2. **Define your schema with `@table`**: Tables must be declared in `schema.graphql`. Use `@indexed` on attributes you intend to sort or filter efficiently.\n\n ```graphql\n type Product @table {\n id: Long @primaryKey\n name: String\n price: Float\n }\n ```\n\n3. **Use `search(` to query records**: 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` arrays to filter**: 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. **Apply `select` to shape results**: Return only the fields you need. Supports arrays, nested relationship selects, and special properties.\n\n ```javascript\n // Array of fields\n Product.search({ select: ['name', 'price'] });\n\n // Nested relationship select\n Book.get({ id: 42, select: ['id', 'title', { name: 'author', select: ['name'] }] });\n ```\n\n Special `select` values: `$id`, `$updatedtime`, `$distance`.\n\n6. **Apply `sort` with an `@indexed` attribute**: Harper uses an index to provide sort order. Sort by an `@indexed` attribute without requiring a condition, or provide at least one condition when sorting by a non-indexed attribute.\n\n ```javascript\n // Sort by primary key with an open-ended condition to avoid scan error\n Product.search({\n conditions: [{ attribute: 'id', comparator: 'greater_than', value: '' }],\n sort: { attribute: 'id' },\n });\n ```\n\n Sort object properties:\n\n | Property | Description |\n | ------------ | -------------------------------------------------------- |\n | `attribute` | Property name or array for chained relationship property |\n | `descending` | Sort descending if `true` (default: `false`) |\n | `next` | Secondary sort to resolve ties (same structure) |\n\n7. **Use `limit` and `offset` for pagination**:\n\n ```javascript\n Product.search({ conditions: [...], limit: 20, offset: 40 });\n ```\n\n8. **Use `explain` and `enforceExecutionOrder` for debugging**:\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 static async post(target, data) {\n const record = await this.update(target.id);\n record.addTo('quantity', -1); // decrement safely across nodes\n }\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 natural transaction context.\n\n ```javascript\n import { tables } from 'harper';\n const { MyTable } = tables;\n\n if (isMainThread) {\n setInterval(async () => {\n let data = await (await fetch('https://example.com/data')).json();\n transaction(async (txn) => {\n for (let item of data) {\n await MyTable.put(item, txn);\n }\n });\n }, 3600000); // every hour\n }\n ```\n\n The `txn` object members:\n\n | Member | Type | Description |\n | --------------------- | --------------- | ------------------------------------------------------ |\n | `commit()` | `() => Promise` | Commits the current transaction |\n | `abort()` | `() => void` | Aborts the transaction and resets it |\n | `resetReadSnapshot()` | `() => void` | Resets the read snapshot to the latest committed state |\n | `timestamp` | `number` | Timestamp associated with the current transaction |\n\n11. **Understand atomicity boundaries**: All tables within 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\n12. **Keep `harper` external in bundlers**: When using SSR bundlers, mark `harper` as external so it resolves to the live runtime. In `vite.config`:\n\n ```javascript\n ssr: {\n external: ['harper'];\n }\n ```\n\n## Examples\n\n### Nested conditions query\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### Full CRUD sequence\n\n```javascript\n// Create a new record (id auto-generated)\nconst created = await Product.create({ name: 'Shirt', price: 9.5 });\n\n// Modify the record\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### SSR rendering 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### Mutable update with `addTo`\n\n```javascript\nconst product = await Product.update(32);\nproduct.status = 'active';\nproduct.subtractFrom('quantity', 1);\nproduct.save();\n```\n\n## Notes\n\n- `tables` calls run in a trusted server-side context and do **not** automatically apply the target table's role permissions. Enforce authorization in your own application logic.\n- Destructive operations (`update`, `patch`, `delete`) act on live data and are not easily reversible. Always scope with specific `conditions`, validate the affected set before writing, and gate behind authorization controls.\n- Sorting by the bare `@primaryKey` alone with no conditions triggers `HdbError: <attribute> is not indexed and not combined with any other conditions`. Add an open-ended range condition or pass `allowFullScan: true` to permit an unconditional scan.\n- Selecting a relationship field without filtering on it behaves as a **LEFT JOIN**; adding a condition on a related attribute behaves as an **INNER JOIN**.\n- `transaction()` is safe to call defensively — if a transaction is already active on the context, it reuses it and executes the callback immediately.\n- Link the `harper` package for correct typings in standalone component directories: `npm link harper`.\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: 677ad213d67822e109c83619e181ca23a59823db\n inputHash: 0f8efee293628a52\n---\n\n# Querying REST APIs\n\nInstructions for the agent to filter, sort, select, and paginate Harper REST API collections using URL query parameters.\n\n## When to Use\n\nApply this rule whenever building or modifying code that queries Harper REST collection endpoints. Use it when you need to filter records by attribute values, apply comparison operators, sort or paginate results, or join across related tables. See [automatic-apis.md](automatic-apis.md) for how Harper exposes tables as REST endpoints.\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\n2. **Apply comparison operators (FIQL syntax)**: Use FIQL operators in the query string for numeric, string, and date comparisons.\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, Harper converts values automatically. Use explicit prefixes to force a 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 order of operations. Prefer square brackets when constructing 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 Construct from 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\n8. **Paginate with `limit(`**: Use `limit(end)` or `limit(start,end)` to control result count and offset.\n\n9. **Sort with `sort(`**: Use `sort(property)` or `sort(+property,-property,...)`. Prefix `+` or no prefix = ascending; `-` = descending.\n\n10. **Query across relationships**: Use dot-syntax to filter by related table attributes. Relationships must be defined in the schema using `@relationship`. Relationship attributes are not included by default — use `select()` to include them.\n\n ```\n GET /Product/?brand.name=Microsoft&select(name,brand{name})\n ```\n\n11. **Query for null values**: Use `=null` as the value to match null or non-null records.\n ```\n GET /Product/?discount=null\n ```\n\n## Examples\n\n**Filter with comparison operators and select:**\n\n```\nGET /Product/?category=software&price=gt=100&price=lt=200&select(name,price)\n```\n\n**Paginate and sort:**\n\n```\nGET /Product/?rating=gt=3&inStock=true&select(rating,name)&limit(20)\nGET /Product/?rating=gt=3&limit(10,30)\nGET /Product/?rating=gt=3&sort(+name)\nGET /Product/?sort(+rating,-price)\n```\n\n**OR logic with grouping:**\n\n```\nGET /Product/?price=lt=100|[rating=5&[tag=fast|tag=scalable|tag=efficient]&inStock=true]\n```\n\n**Relationship join with nested select:**\n\nDefine the schema:\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\nQuery with join:\n\n```\nGET /Product/?brand.name=Microsoft&select(name,brand{name,id})\nGET /Brand/?products.name=Keyboard\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**Access a specific property by record ID:**\n\n```\nGET /MyTable/123.propertyName\n```\n\n## Notes\n\n- Only indexed attributes can be used as the primary filter attribute; when combining multiple attributes, only one needs to be indexed.\n- Relationship attributes are excluded from responses by default. Always use `select(` to include them.\n- When selecting a related attribute without filtering on it, the behavior is a LEFT JOIN — the property is omitted if the foreign key is null or references a non-existent record.\n- The suffixes `.json`, `.cbor`, `.msgpack`, and `.csv` in URL paths are reserved as content-type selectors and take precedence over a property of the same name.\n- Square brackets are preferred over parentheses when building grouped queries programmatically, because `[` and `]` are safely URL-encoded by standard encoding functions while `(` is not.\n',"real-time-apps":`---
1899
+ name: real-time-apps
1900
+ description: How to build real-time features in Harper using WebSockets and Pub/Sub.
1901
+ metadata:
1902
+ mode: generate
1903
+ sources:
1904
+ - reference/v5/rest/websockets.md
1905
+ sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
1906
+ inputHash: a8afd4d3a52f77ba
1907
+ ---
1908
+
1909
+ # Real-Time Apps with WebSockets and Pub/Sub
1910
+
1911
+ Instructions for the agent to follow when building real-time features in Harper using WebSockets and Pub/Sub.
1912
+
1913
+ ## When to Use
1914
+
1915
+ 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.
1916
+
1917
+ ## How It Works
1918
+
1919
+ 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:
1920
+
1921
+ \`\`\`yaml
1922
+ rest:
1923
+ webSocket: false
1924
+ \`\`\`
1925
+
1926
+ 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.
1927
+
1928
+ \`\`\`javascript
1929
+ let ws = new WebSocket('wss://server/my-resource/341');
1930
+ ws.onmessage = (event) => {
1931
+ let data = JSON.parse(event.data);
1932
+ };
1933
+ \`\`\`
1934
+
1935
+ \`new WebSocket('wss://server/my-resource/341')\` accesses the resource defined for \`my-resource\` with record id \`341\` and subscribes to it.
1936
+
1937
+ 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.
1938
+
1939
+ 4. **Use the default \`connect()\` for event-style access**: Call \`super.connect()\` to get a streaming iterable that provides:
1940
+ - A \`send(message)\` method for pushing outgoing messages
1941
+ - A \`close\` event for cleanup on disconnect
1942
+
1943
+ 5. **Handle message ordering in distributed environments**: Harper delivers messages to local subscribers immediately without inter-node coordination delay.
1944
+
1945
+ | Message Type | Behavior |
1946
+ | -------------------------------------------------------- | ----------------------------------------------------------------------- |
1947
+ | Non-retained (no \`retain\` flag) | Every message delivered in order received; suitable for chat |
1948
+ | Retained (published with \`retain\`, or PUT/updated in DB) | Only the latest-timestamp message is kept; suitable for sensor readings |
1949
+
1950
+ 6. **Use MQTT over WebSockets** when needed by setting the sub-protocol header:
1951
+ \`\`\`
1952
+ Sec-WebSocket-Protocol: mqtt
1953
+ \`\`\`
1954
+
1955
+ ## Examples
1956
+
1957
+ **Simple echo server** — override \`connect(incomingMessages)\` to yield each incoming message back to the client:
1958
+
1959
+ \`\`\`javascript
1960
+ export class Echo extends Resource {
1961
+ async *connect(incomingMessages) {
1962
+ for await (let message of incomingMessages) {
1963
+ yield message; // echo each message back
1964
+ }
1965
+ }
1966
+ }
1967
+ \`\`\`
1968
+
1969
+ **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:
1970
+
1971
+ \`\`\`javascript
1972
+ export class Example extends Resource {
1973
+ connect(incomingMessages) {
1974
+ let outgoingMessages = super.connect();
1975
+
1976
+ let timer = setInterval(() => {
1977
+ outgoingMessages.send({ greeting: 'hi again!' });
1978
+ }, 1000);
1979
+
1980
+ incomingMessages.on('data', (message) => {
1981
+ outgoingMessages.send(message); // echo incoming messages
1982
+ });
1983
+
1984
+ outgoingMessages.on('close', () => {
1985
+ clearInterval(timer);
1986
+ });
1987
+
1988
+ return outgoingMessages;
1989
+ }
1990
+ }
1991
+ \`\`\`
1992
+
1993
+ ## Notes
1994
+
1995
+ - WebSocket connections target a resource URL path. By default, connecting to a resource subscribes to changes for that resource.
1996
+ - The \`connect(incomingMessages)\` method **must** return an async iterable or generator; returning a plain value will not work.
1997
+ - \`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.
1998
+ - For one-way real-time streaming without bidirectional communication, consider Server-Sent Events instead.
1999
+ - For full pub/sub capabilities, Harper also supports MQTT; set \`Sec-WebSocket-Protocol: mqtt\` to use MQTT over WebSockets.
2000
+ `,"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: 677ad213d67822e109c83619e181ca23a59823db\n inputHash: ecb06058191ba29f\n---\n\n# Schema Design and GraphQL Tooling\n\nInstructions for the agent to follow when designing Harper database schemas using GraphQL type definitions, core directives, and tooling configuration.\n\n## When to Use\n\nApply this rule when creating or modifying Harper schema files (`.graphql`), configuring schema loading 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. **Create a GraphQL schema file** with Harper-specific directives. Schemas ensure required tables exist on deployment, enforce types and constraints, control indexing, and define relationships.\n\n ```graphql\n type Dog @table {\n id: Long @primaryKey\n name: String\n breed: String\n age: Int\n }\n ```\n\n2. **Register the schema in `config.yaml`** using the `graphqlSchema` plugin:\n\n ```yaml\n graphqlSchema:\n files: \'schema.graphql\'\n ```\n\n Both plugins and applications can specify schemas.\n\n3. **Mark each type as a table** with `@table`. The type name becomes the table name by default.\n\n ```graphql\n type MyTable @table {\n id: Long @primaryKey\n }\n ```\n\n Key `@table` 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 for anonymous GET/HEAD 200/304 responses |\n | `randomAccessFields` | `Boolean` | `storage.randomAccessFields` | Pin this table\'s record encoding |\n\n4. **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 - **UUID string** — when type is `String` or `ID`\n - **Auto-incrementing integer** — when type is `Int`, `Long`, or `Any`\n\n Use `Long` or `Any` for auto-generated numeric keys; `Int` is 32-bit and may be insufficient for large tables.\n\n ```graphql\n type Product @table {\n id: Long @primaryKey\n name: String\n }\n ```\n\n5. **Add secondary indexes** with `@indexed` on any attribute that will be used for filtering in REST queries, SQL, or NoSQL operations. If the field value is an array, each element is individually indexed.\n\n ```graphql\n type Product @table {\n id: Long @primaryKey\n category: String @indexed\n price: Float @indexed\n }\n ```\n\n6. **Expose tables via REST and other interfaces** using `@export`. Without `@export`, the table has no REST/MQTT route (callers get 404). The optional `name` parameter sets the URL path segment.\n\n ```graphql\n type MyTable @table @export(name: "my-table") {\n id: Long @primaryKey\n }\n ```\n\n `@export` is a routing directive, not access control. The table remains accessible through the Operations API and SQL regardless. REST must also be enabled for the application (via `rest: true` in `config.yaml` or Harper\'s built-in default).\n\n7. **Apply additional type directives** as needed:\n - `@sealed` — prevents records from including properties beyond those declared in the schema.\n - `@hidden` — suppresses the type from MCP tool descriptors and the OpenAPI document. Does not restrict data access.\n\n8. **Apply field directives** for computed and lifecycle behavior:\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 - `@embed` — computes an embedding vector when the source field is written (requires `source` and `model` arguments; field type must be `[Float]`).\n - `@hidden` (field) — suppresses the field from generated specs and MCP tool schemas; does not restrict data access.\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 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 lifecycle fields and indexing:**\n\n```graphql\ntype Event @table(database: "analytics", expiration: 86400) {\n id: Long @primaryKey\n name: String @indexed\n createdAt: Long @createdTime\n updatedAt: Long @updatedTime\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**Sealed table preventing extra properties:**\n\n```graphql\ntype StrictRecord @table @sealed {\n id: Long @primaryKey\n name: String\n}\n```\n\n**`config.yaml` schema registration:**\n\n```yaml\ngraphqlSchema:\n files: \'schema.graphql\'\n```\n\n## Notes\n\n- Schemas are flexible by default — records may include additional properties beyond those declared. Use `@sealed` to prevent this.\n- Use unique `database` names in plugins or applications to avoid table naming collisions, since all tables default to the `"data"` database.\n- Replication is enabled by default. If you disable replication and re-enable it later, the table will not catch up on writes made while replication was disabled.\n- `@hidden` (type or field) is a metadata-visibility directive only. Use table-level role permissions and `attribute_permissions` whitelists to restrict actual data access.\n- `@export` absence causes 404 on REST/MQTT routes but does not protect data from the Operations API or SQL.\n- The `cacheControl` argument emits headers only on anonymous (unauthenticated) GET/HEAD 200/304 responses. Authenticated responses receive `Cache-Control: private, no-cache`.\n- `randomAccessFields` on `@table` pins the record encoding at table creation time. Editing the argument later does not repin an existing table.\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":`---
2001
+ name: typescript-type-stripping
2002
+ description: How to run TypeScript files directly in Harper without a build step.
2003
+ metadata:
2004
+ mode: generate
2005
+ sources:
2006
+ - reference/v5/components/javascript-environment.md#TypeScript Support
2007
+ sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
2008
+ inputHash: 4e6bd8b610edd595
2009
+ ---
2010
+
2011
+ # TypeScript Type Stripping in Harper
2012
+
2013
+ Instructions for the agent to run \`.ts\` files directly in Harper without a build step using Node.js's built-in type stripping.
2014
+
2015
+ ## When to Use
2016
+
2017
+ 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.
2018
+
2019
+ ## How It Works
2020
+
2021
+ 1. **Ensure Node.js version**: Require Node.js 22.6 or later. Type stripping is unavailable on earlier versions.
2022
+
2023
+ 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:
2024
+
2025
+ \`\`\`yaml
2026
+ jsResource:
2027
+ files: 'resources/*.ts'
2028
+ \`\`\`
2029
+
2030
+ 3. **Use explicit \`.ts\` extensions in local imports**: Node's loader does not resolve \`'./helper'\` to \`'./helper.ts'\`, so always include the full extension:
2031
+
2032
+ \`\`\`typescript
2033
+ import { helper } from './helper.ts';
2034
+ \`\`\`
2035
+
2036
+ 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.
2037
+
2038
+ ## Examples
2039
+
2040
+ A complete Harper resource written in TypeScript, using imports from the \`harper\` package:
2041
+
2042
+ \`\`\`typescript
2043
+ import { type RequestTargetOrId, Resource, tables } from 'harper';
2044
+
2045
+ export class MyResource extends Resource {
2046
+ async get(target?: RequestTargetOrId): Promise<{ message: string }> {
2047
+ return { message: 'Hello from TS' };
2048
+ }
2049
+ }
2050
+ \`\`\`
2051
+
2052
+ Paired \`config.yaml\` entry loading the file via \`jsResource\`:
2053
+
2054
+ \`\`\`yaml
2055
+ jsResource:
2056
+ files: 'resources/*.ts'
2057
+ \`\`\`
2058
+
2059
+ ## Notes
2060
+
2061
+ - No build step or transpiler is required — Harper runs \`.ts\` files directly.
2062
+ - Type imports (e.g., \`import { type RequestTargetOrId }\`) from the \`harper\` package work as usual.
2063
+ - Unsupported TypeScript features include: enums with runtime values, namespaces with runtime semantics, and anything requiring code transformation beyond simple type stripping.
2064
+ `,"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: f37a8c4021e20d5c74c1d339a6b6c8c196b5603e\n inputHash: 92e03eb0b830f335\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.md](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` type.\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 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**: The `Blob` type implements the Web API `Blob` interface. Use `.bytes()`, `.text()`, `.arrayBuffer()`, `.stream()`, or `.slice()` as needed.\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 precede commit**: By default, `Blob` is 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**: Interrupted streams must be handled explicitly.\n\n ```javascript\n export class MyEndpoint extends MyTable {\n static async get(target) {\n const record = super.get(target);\n let blob = record.data;\n blob.on('error', () => {\n MyTable.invalidate(target);\n });\n return { status: 200, headers: {}, body: blob };\n }\n }\n ```\n\n6. **Rely on automatic coercion where applicable**: When a field is typed as `Blob` in the schema, any string or buffer assigned via `put`, `patch`, or `publish` is automatically coerced to a `Blob` — no manual `createBlob()` call is needed in those cases.\n\n### `BlobOptions` reference\n\nPass an options object as the second argument to `createBlob()`.\n\n| Option | Type | Default | Description |\n| ------------------ | --------- | ----------- | ------------------------------------------------------------------------------------------------------------------------ |\n| `type` | `string` | `undefined` | MIME type to associate with the blob (e.g., `image/jpeg`). Readable via `blob.type` and used when serving HTTP. |\n| `size` | `number` | `undefined` | Size of the data in bytes, if known ahead of time. Otherwise inferred from a buffer or determined as a stream completes. |\n| `saveBeforeCommit` | `boolean` | `false` | Wait until the blob is fully written before the transaction commits. |\n| `compress` | `boolean` | `false` | Compress the stored data with deflate. |\n| `flush` | `boolean` | `false` | Flush the 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);\n// blob exists, but data is still streaming to storage\nawait MyTable.put({ id: 'my-record', data: blob });\n\nlet record = await MyTable.get('my-record');\n// blob data is accessible as it arrives\nlet outgoingStream = record.data.stream();\n```\n\n**Guarantee full write before commit using `saveBeforeCommit`:**\n\n```javascript\nlet blob = createBlob(stream, { saveBeforeCommit: true });\nawait MyTable.put({ id: 'my-record', data: blob });\n```\n\n## Notes\n\n- `Blob` stores data separately from the record. If you need the binary data to be a true, ACID-committed part of the record, use a `Bytes` field instead.\n- All standard Web API `Blob` methods — `.text()`, `.arrayBuffer()`, `.stream()`, `.slice()`, and `.bytes()` — are available on retrieved blob fields.\n- Without `saveBeforeCommit: true`, blobs are **not** ACID-compliant by default; a record can reference a blob before it is fully written to storage.\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: 677ad213d67822e109c83619e181ca23a59823db\n inputHash: 01c0fafb9afa68a0\n---\n\n# v5 Upgrade: Breaking Changes and Migration Guide\n\nInstructions for the agent to apply 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 to v5, when encountering runtime errors related to renamed packages, changed APIs, or security restrictions after a v5 upgrade, or when scaffolding new v5-compatible application code.\n\n## How It Works\n\n1. **Update the package import from `harperdb` to `harper`**: All application code must import from `harper`, not `harperdb`.\n\n ```javascript\n import { tables } from 'harper';\n ```\n\n2. **Enable `allowInstallScripts` if packages require install scripts**: Harper v5 uses `--ignore-scripts` by default when installing packages. If a package requires execution of install scripts (e.g., to install native binaries), set the `allowInstallScripts` option when deploying.\n\n3. **Update `Table.get` usage — return value is now a frozen record object**: `Table.get` now returns a plain record object, not a table class instance. The record is frozen; you cannot add or mutate properties directly.\n - Replace direct property mutation:\n\n ```javascript\n let record = await Table.get(id);\n record = { ...record, property: 'changed' };\n ```\n\n - Replace `wasLoadedFromSource()` with `loadedFromSource` on the `target` object:\n\n ```javascript\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 still expose `getUpdatedTime` and `getExpiresAt` methods.\n\n4. **Update transaction and context handling using `getContext`**: Harper v5 uses asynchronous context tracking. Context and the current transaction are automatically carried to all downstream calls — you no longer pass context explicitly. Import `getContext` and `transaction` from `harper`:\n\n ```javascript\n import { getContext, transaction } from 'harper';\n ```\n\n If your code previously omitted context to escape a transaction (e.g., to poll for updated data), explicitly commit the transaction and/or wrap each read in a new `transaction()` call:\n\n ```javascript\n import { setTimeout as delay } from 'node:timers/promises';\n import { getContext, transaction } from 'harper';\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\n5. **Register allowed spawn commands via `allowedSpawnCommands`**: `spawn` and `execFile` may only launch executables listed in `applications.allowedSpawnCommands` in `harperdb-config.yaml`. Only the first token of the command is matched. `exec` is not usable through the substituted module; `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. **Use `saveBeforeCommit` instead of `blob.save()`**: The `blob.save()` method has been removed. Pass the `saveBeforeCommit` flag in the options to the `Blob` constructor instead.\n\n7. **Handle `headers` on returned response objects**: If you return an object from a REST method with a `headers` property, Harper v5 will use it as the response headers.\n\n8. **Configure the VM module loader and `lockdown` in `harperdb-config.yaml`**: v5 loads application modules through Node.js's VM module API. Control all behavior under the `applications` key:\n\n ```yaml\n applications:\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 **`moduleLoader` options:**\n\n | Value | Behavior |\n | -------------------- | -------------------------------------------------------------------------------------------------------------------------------- |\n | `vm-current-context` | Default. VM loader in Harper's own context; shares intrinsics with Harper. Best compatibility. |\n | `vm` | VM loader in a separate per-application context with its own intrinsics. Stronger isolation but may cause `instanceof` failures. |\n | `native` | Standard Node.js `import()`. No VM loader; application-specific context (`logger`, `config`) unavailable. |\n | `compartment` | SES Compartment-based loading. For specialized sandboxing only. |\n\n **`lockdown` options:**\n\n | Value | Behavior |\n | ------------------- | ------------------------------------------------------- |\n | `freeze-after-load` | Default. Freezes intrinsics after all components load. |\n | `freeze` | Freezes intrinsics before loading any application code. |\n | `ses` | Full SES lockdown via the `ses` package. Strictest. |\n | `none` | No lockdown. Use as a temporary workaround only. |\n\n To disable the VM loader entirely and restore pre-v5 behavior:\n\n ```yaml\n applications:\n moduleLoader: native\n ```\n\n## Examples\n\n**Full transaction polling pattern (v5):**\n\n```javascript\nimport { setTimeout as delay } from 'node:timers/promises';\nimport { getContext, transaction } from 'harper';\n\nclass 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**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**Full `harperdb-config.yaml` `applications` 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**Restricting allowed built-in modules:**\n\n```yaml\napplications:\n allowedBuiltinModules:\n - fs\n - path\n - http\n```\n\n## Notes\n\n- Always import Harper APIs from `'harper'`, not from global variables or `'harperdb'`.\n- `getContext` is exported from `'harper'` and provides access to the current transaction without passing context explicitly.\n- Record objects returned by `Table.get` are frozen — spread into a new object before modifying.\n- `loadedFromSource` is a property on the `target` object, replacing the removed `wasLoadedFromSource()` instance method.\n- `saveBeforeCommit` replaces the removed `blob.save()` method.\n- The `headers` property on a returned REST response object is used as response headers.\n- Under `lockdown: ses`, the constrained `fetch` applies only in `vm` mode. In `vm-current-context` and `native` modes, application code uses the standard global `fetch`.\n- In production, `allowedDirectory: app` is the default; modules outside the application directory tree will throw. Set `allowedDirectory: any` only if legitimately required.\n- `dependencyLoader: native` is a narrower option than `moduleLoader: native` — it uses native loading only for npm packages while keeping the VM loader for first-party application source files.\n","vector-indexing":`---
2065
+ name: vector-indexing
2066
+ description: How to enable and query vector indexes for similarity search in Harper.
2067
+ metadata:
2068
+ mode: generate
2069
+ sources:
2070
+ - reference/v5/database/schema.md#Vector Indexing
2071
+ sourceCommit: d4cbc1a7dd400462e4a3243f944b3a75d89b29ca
2072
+ inputHash: 1dae788bc850ea90
2073
+ ---
2074
+
2075
+ # Vector Indexing
2076
+
2077
+ Instructions for the agent to enable HNSW vector indexes on table fields and query them for similarity search in Harper.
2078
+
2079
+ ## When to Use
2080
+
2081
+ 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.
2082
+
2083
+ ## How It Works
2084
+
2085
+ 1. **Declare the vector index** on a \`[Float]\` field using \`@indexed(type: "HNSW")\`:
2086
+
2087
+ \`\`\`graphql
2088
+ type Document @table {
2089
+ id: Long @primaryKey
2090
+ textEmbeddings: [Float] @indexed(type: "HNSW")
2091
+ }
2092
+ \`\`\`
2093
+
2094
+ 2. **Query nearest neighbors** using \`Document.search()\` with the \`sort\` parameter. Set \`attribute\` to the indexed field and \`target\` to the query vector:
2095
+
2096
+ \`\`\`javascript
2097
+ let results = Document.search({
2098
+ sort: { attribute: 'textEmbeddings', target: searchVector },
2099
+ limit: 5,
2100
+ });
2101
+ \`\`\`
2102
+
2103
+ 3. **Combine with filter conditions** to narrow results before or during graph traversal. Selective conditions are automatically diverted to an exact-scan strategy:
2104
+
2105
+ \`\`\`javascript
2106
+ let results = Document.search({
2107
+ conditions: [{ attribute: 'price', comparator: 'lt', value: 50 }],
2108
+ sort: { attribute: 'textEmbeddings', target: searchVector },
2109
+ limit: 5,
2110
+ });
2111
+ \`\`\`
2112
+
2113
+ 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:
2114
+
2115
+ \`\`\`javascript
2116
+ let results = Document.search(
2117
+ {
2118
+ sort: { attribute: 'textEmbeddings', target: searchVector },
2119
+ vectorFilter: (record) =>
2120
+ record.tenantId === context.user.tenantId && record.status === 'published',
2121
+ limit: 10,
2122
+ },
2123
+ context,
2124
+ );
2125
+ \`\`\`
2126
+
2127
+ 5. **Filter by distance threshold** using \`target\` directly on a condition alongside \`comparator\` and \`value\`. This returns matches within the threshold without using \`sort\`:
2128
+
2129
+ \`\`\`javascript
2130
+ let results = Document.search({
2131
+ conditions: {
2132
+ attribute: 'textEmbeddings',
2133
+ comparator: 'lt',
2134
+ value: 0.1,
2135
+ target: searchVector,
2136
+ },
2137
+ });
2138
+ \`\`\`
2139
+
2140
+ 6. **Include computed distance in results** by adding \`$distance\` to \`select\`. Works with both \`sort\`-based and threshold queries:
2141
+
2142
+ \`\`\`javascript
2143
+ let results = Document.search({
2144
+ select: ['name', '$distance'],
2145
+ sort: { attribute: 'textEmbeddings', target: searchVector },
2146
+ limit: 5,
2147
+ });
2148
+ \`\`\`
2149
+
2150
+ 7. **Tune per-query search options** on the \`sort\` descriptor using \`distance\` and \`ef\`:
2151
+
2152
+ \`\`\`javascript
2153
+ let results = Document.search({
2154
+ sort: { attribute: 'textEmbeddings', target: searchVector, distance: 'dotProduct', ef: 200 },
2155
+ limit: 5,
2156
+ });
2157
+ \`\`\`
2158
+
2159
+ 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\`):
2160
+
2161
+ \`\`\`javascript
2162
+ let results = Document.search(
2163
+ {
2164
+ sort: { attribute: 'textEmbeddings', target: searchVector, ef: 200, filterExpansion: 40 },
2165
+ vectorFilter: (record) => record.category === 'rare',
2166
+ limit: 10,
2167
+ },
2168
+ context,
2169
+ );
2170
+ \`\`\`
2171
+
2172
+ 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:
2173
+
2174
+ \`\`\`javascript
2175
+ function canReadReport(record, context) {
2176
+ const user = context.user;
2177
+ if (user?.role?.permission?.super_user) return true;
2178
+ return user?.username != null && record.ownerId != null && record.ownerId === user.username;
2179
+ }
2180
+
2181
+ export class Reports extends tables.Reports {
2182
+ search(target) {
2183
+ target.rowFilter = canReadReport;
2184
+ return super.search(target);
2185
+ }
2186
+ }
2187
+ \`\`\`
2188
+
2189
+ ### HNSW Index Parameters
2190
+
2191
+ Configure parameters directly on \`@indexed(type: "HNSW", ...)\`:
2192
+
2193
+ | Parameter | Default | Description |
2194
+ | ---------------------- | ----------------- | ------------------------------------------------------------------------------------------------ |
2195
+ | \`distance\` | \`"cosine"\` | Distance function: \`"cosine"\`, \`"euclidean"\`, or \`"dotProduct"\` |
2196
+ | \`efConstruction\` | \`100\` | Max nodes explored during index construction. Higher = better recall, lower = better performance |
2197
+ | \`M\` | \`16\` | Preferred connections per graph layer |
2198
+ | \`optimizeRouting\` | \`0.5\` | Heuristic aggressiveness for omitting redundant connections (0 = off, 1 = most aggressive) |
2199
+ | \`mL\` | computed from \`M\` | Normalization factor for level generation |
2200
+ | \`efConstructionSearch\` | auto-scaled | Max nodes explored during search. When unset, auto-scales with index size |
2201
+ | \`quantization\` | — | \`"int8"\` stores vectors quantized to int8 |
2202
+ | \`filterExpansion\` | \`24\` | Visit-budget multiplier for filtered search: visits at most \`ef * filterExpansion\` nodes |
2203
+
2204
+ Per-query \`sort\` descriptor options:
2205
+
2206
+ | Option | Values | Description |
2207
+ | ---------- | ----------------------------------------- | ------------------------------------------------------ |
2208
+ | \`distance\` | \`"cosine"\`, \`"euclidean"\`, \`"dotProduct"\` | Overrides the index's distance function for this query |
2209
+ | \`ef\` | integer | Overrides the search exploration budget for this query |
2210
+
2211
+ ## Examples
2212
+
2213
+ **Index with custom HNSW parameters:**
2214
+
2215
+ \`\`\`graphql
2216
+ type Document @table {
2217
+ id: Long @primaryKey
2218
+ textEmbeddings: [Float]
2219
+ @indexed(type: "HNSW", distance: "euclidean", optimizeRouting: 0, efConstructionSearch: 100)
2220
+ }
2221
+ \`\`\`
2222
+
2223
+ **Index with int8 quantization:**
2224
+
2225
+ \`\`\`graphql
2226
+ type Document @table {
2227
+ id: Long @primaryKey
2228
+ textEmbeddings: [Float] @indexed(type: "HNSW", quantization: "int8")
2229
+ }
2230
+ \`\`\`
2231
+
2232
+ **Nearest-neighbor search with distance included:**
2233
+
2234
+ \`\`\`javascript
2235
+ let results = Document.search({
2236
+ select: ['name', '$distance'],
2237
+ sort: { attribute: 'textEmbeddings', target: searchVector },
2238
+ limit: 5,
2239
+ });
2240
+ \`\`\`
2241
+
2242
+ **Filtered traversal with tuned budget:**
2243
+
2244
+ \`\`\`javascript
2245
+ let results = Document.search(
2246
+ {
2247
+ sort: { attribute: 'textEmbeddings', target: searchVector, ef: 200, filterExpansion: 40 },
2248
+ vectorFilter: (record) => record.category === 'rare',
2249
+ limit: 10,
2250
+ },
2251
+ context,
2252
+ );
2253
+ \`\`\`
2254
+
2255
+ ## Notes
2256
+
2257
+ - \`vectorFilter\` and \`rowFilter\` are available from the JavaScript API only; they cannot be set through REST or QUERY request data.
2258
+ - \`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.
2259
+ - \`rowFilter\` does not apply to a direct primary-key \`get\`.
2260
+ - Changing \`efConstructionSearch\` on an existing index does not trigger a rebuild. Structural parameters (\`distance\`, \`M\`, \`efConstruction\`, \`quantization\`) do rebuild the index when changed.
2261
+ - 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.
2262
+ - The correct parameter name is \`efConstruction\` (seeds the construction budget) and \`efConstructionSearch\` (controls search budget). The name \`efSearchConstruction\` is a previous documentation error.
2263
+ - When no \`ef\` is passed and \`efConstructionSearch\` (or \`efConstruction\`) is not explicitly set, the search budget auto-scales with index size.
2264
+ - \`cosine\` is the default distance function when \`distance\` is not specified.
2265
+ `},wa={name:`readHarperSkill`,description:`Returns documentation for a Harper skill or best practice. Skills provide guidance on developing Harper applications.`,inputSchema:l({skill:v(Sa)})};async function Ta({input:{skill:e}}){return{success:!!Ca[e],message:Ca[e]||`No skill found with the name ${e}`}}var Ea={...wa,icon:it,execute:Ta},Da={name:`readLogs`,description:`Returns the matching logs from the server.`,inputSchema:l({log_name:v([`hdb.log`,`system.log`]).default(`hdb.log`),limit:y().or(g()).optional(),level:v([`notify`,`error`,`warn`,`info`,`debug`,`trace`,`undefined`]).or(g()).optional(),from:y().or(g()).optional(),until:y().or(g()).optional()})};async function Oa({input:e,instanceClientParams:t}){try{return{success:!0,data:await ze({...t,logFilters:e,replicated:t.entityType===`cluster`})}}catch(e){return{success:!1,message:`Error: ${e}`}}}var ka={...Da,icon:ct,execute:Oa},Aa={name:`readTableRecords`,description:`Retrieves some or all table records from a database on the server.`,inputSchema:l({database:y().trim(),table:y().trim(),pageIndex:u().default(0),pageSize:u().default(10),primaryKey:y(),conditions:m(l({search_attribute:y(),search_type:v([`between`,`eq`,`equals`,`greater_than`,`greater_than_equal`,`less_than`,`less_than_equal`,`ne`,`not_equal`,`starts_with`]),search_value:i()})),sort:l({attribute:y(),descending:_()})})};async function ja({input:{database:e,table:t,conditions:n,primaryKey:r,...i},instanceClientParams:a}){try{if(!n.length){let{data:n}=await Me({...a,databaseName:e,tableName:t,onlyIfCached:!0,searchAttribute:r,...i});return{success:!0,data:n}}let{data:o}=await Oe({...a,databaseName:e,tableName:t,onlyIfCached:!0,conditions:n,...i});return{success:!0,data:o}}catch(e){return{success:!1,message:`Error: ${e}`}}}var Ma={...Aa,icon:Ge,execute:ja},Na={name:`restartHTTPService`,description:`Restarts the HTTP service on the server to allow schema and resource changes to be applied.`,inputSchema:l({})};async function Pa({instanceClientParams:e,baseURL:t}){let n=se.loading(`Restarting HTTP service...`,{description:`This may take a bit.`,duration:3e5});try{await Qe({...e,operation:`restart_service`,replicated:e.entityType===`cluster`})}catch(e){return{success:!1,message:`Error: ${e}`}}return se.success(`Done!`,{description:`HTTP Service restarted!`,id:n,duration:5e3}),{success:!0,message:`HTTP Service restarted!`,webURL:t}}var Fa={...Na,icon:Ze,execute:Pa,requiresApproval:!0},Ia={name:`setComponentFile`,description:`Returns the contents of a component file by its full path (which was returned by getComponents)`,inputSchema:l({path:y().trim(),payload:y(),encoding:v([`utf8`,`ASCII`,`binary`,`hex`,`base64`,`utf16le`,`latin1`,`ucs2`])})};async function La({input:{path:e,encoding:t,payload:n},instanceClientParams:r}){try{let i=e.split(`/`),a=i.shift(),o=i.join(`/`),s=await be({...r,file:o,project:a,payload:n,encoding:t});return await Je.invalidateQueries({queryKey:[r.entityId,`get_component_file`,a,o]}),ke(`ReloadApplicationRootEntries`,!0),{success:!0,data:s}}catch(e){return{success:!1,message:`Error: ${e}`}}}var Ra={...Ia,icon:st,execute:La,requiresApproval:!0},za={name:`updateTableRecords`,description:`Updates records in a particular table in a particular database on the server.`,inputSchema:l({database:y().trim(),table:y().trim(),records:m(i())})};async function Ba({input:{database:e,table:t,records:n},instanceClientParams:r,params:i}){try{let a=await Ve({...r,databaseName:e,tableName:t,records:n}),{databaseName:o,tableName:s}=i;return await Je.invalidateQueries({queryKey:[r.entityId,o,s]}),{success:!0,data:a}}catch(e){return{success:!1,message:`Error: ${e}`}}}var Va={readHarperSkill:Ea,createApp:Wi,readLogs:ka,getAnalytics:$i,listAnalyticsMetrics:xa,restartHTTPService:Fa,collectFeedback:Vi,getUserContext:ma,getComponentFile:na,getComponents:aa,setComponentFile:Ra,dropComponentFile:Xi,getDescribeAll:ca,getDescribeTable:da,insertTableRecords:_a,readTableRecords:Ma,updateTableRecords:{...za,icon:rt,execute:Ba,requiresApproval:!0},deleteTableRecords:qi};function Ha(e){return Va[e]}function Ua(e){return e.state===`input-available`&&!!Ha(fi(e))?.requiresApproval}function Wa(e){let t=[];for(let[n,r]of(e??[]).entries()){if(q(r)){if(Ua(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}ci(r)&&r.text.length>0&&t.push({kind:`part`,part:r,index:n})}return t}function Ga({part:e,onApprove:t,onDeny:n,onAlwaysApprove:r,isApproving:i}){let[a,o]=(0,T.useState)(!1),[s,c]=(0,T.useState)(!1),l=fi(e),u=Ha(l),d=u?.icon||Ee,f=u?.requiresApproval,p=(0,T.useMemo)(()=>!e.input||typeof e.input==`object`&&Object.keys(e.input).length===0,[e.input]),m=(0,T.useMemo)(()=>{let t=JSON.stringify(e.input,null,` `);return{json:t,lines:t?t.split(`
2266
+ `).length:0}},[e.input]),h=(0,T.useMemo)(()=>{let t=JSON.stringify(e.output,null,` `);return{json:t,lines:t?t.split(`
2267
+ `).length:0}},[e.output]);return(0,E.jsxs)(`div`,{className:`tool-invocation ${e.state}`,children:[(0,E.jsxs)(`div`,{className:`tool-info`,children:[(0,E.jsxs)(`div`,{className:`tool-name`,children:[(0,E.jsx)(d,{size:14}),(0,E.jsx)(`span`,{children:l})]}),(0,E.jsxs)(`div`,{className:`tool-status`,children:[e.state===`input-streaming`&&(0,E.jsx)(`span`,{children:`Thinking...`}),e.state===`input-available`&&(0,E.jsx)(`span`,{children:i?`Executing...`:f?`Awaiting Approval...`:`Executing...`}),e.state===`output-available`&&(e.output?.error?(0,E.jsx)(ot,{size:14,className:`text-destructive`}):(0,E.jsx)(le,{size:14}))]})]}),e.state!==`input-streaming`&&(0,E.jsxs)(`div`,{className:`tool-io`,children:[!p&&(0,E.jsxs)(`div`,{className:`tool-args`,children:[(0,E.jsxs)(`div`,{className:`flex items-center justify-between gap-2 mb-1`,children:[(0,E.jsx)(`strong`,{children:`Input:`}),m.lines>3&&(0,E.jsx)(C,{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,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(ue,{size:12}),`Hide`]}):(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(ce,{size:12}),`Show`]})})]}),(0,E.jsx)(`div`,{className:a?`whitespace-pre-wrap`:`line-clamp-3 overflow-hidden whitespace-pre-wrap`,children:m.json})]}),e.state===`input-available`&&f&&(0,E.jsxs)(`div`,{className:`flex gap-2 mt-3 pt-3 border-t`,children:[(0,E.jsxs)(C,{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,E.jsx)(Fe,{className:`mr-2 h-3 w-3 animate-spin`}):null,`Approve`]}),(0,E.jsx)(C,{type:`button`,size:`sm`,variant:`outline`,className:`h-8 text-xs approval-outline`,onClick:()=>r?.(e.toolCallId),disabled:i,children:`Always Approve`}),(0,E.jsx)(C,{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,E.jsx)(E.Fragment,{children:u?.render?u.render(e):(0,E.jsxs)(`div`,{className:`tool-result`,children:[(0,E.jsxs)(`div`,{className:`flex items-center justify-between gap-2 mb-1`,children:[(0,E.jsx)(`strong`,{children:`Result:`}),h.lines>3&&(0,E.jsx)(C,{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,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(ue,{size:12}),`Hide`]}):(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(ce,{size:12}),`Show`]})})]}),(0,E.jsx)(`div`,{className:s?`whitespace-pre-wrap`:`line-clamp-3 overflow-hidden whitespace-pre-wrap`,children:h.json})]})})]})]})}function Ka({parts:e,onApprove:t,onDeny:n,onAlwaysApprove:r,approvingToolCallIds:i}){let[a,o]=(0,T.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?fi(e[0]):void 0,u=l&&Ha(l)?.icon||dt,d=l??`${e.length} tools`;return(0,E.jsxs)(`div`,{className:`tool-group`,children:[(0,E.jsxs)(`button`,{type:`button`,className:`tool-group-summary`,"aria-expanded":a,onClick:()=>o(!a),children:[a?(0,E.jsx)(ce,{size:14}):(0,E.jsx)(Ae,{size:14}),(0,E.jsx)(u,{size:14}),(0,E.jsx)(`span`,{children:s?`Using ${d}...`:`Used ${d}`}),(0,E.jsx)(`span`,{className:`tool-group-status`,children:s?(0,E.jsx)(Fe,{size:14,className:`animate-spin`}):c?(0,E.jsx)(ot,{size:14,className:`text-destructive`}):(0,E.jsx)(le,{size:14})})]}),a&&e.map(e=>(0,E.jsx)(Ga,{part:e,onApprove:t,onDeny:n,onAlwaysApprove:r,isApproving:i?.has(e.toolCallId)},e.toolCallId))]})}function qa({message:e,onApprove:t,onDeny:n,onAlwaysApprove:r,approvingToolCallIds:i}){return e.parts?.some(e=>ci(e)&&e.text.length>0||q(e))?(0,E.jsxs)(tt.div,{initial:{opacity:0,y:10},animate:{opacity:1,y:0},className:`message-bubble ${e.role===`user`?`user`:`assistant`}`,children:[(0,E.jsx)(`div`,{className:`avatar`,children:e.role===`user`?(0,E.jsx)(Be,{size:18}):(0,E.jsx)(et,{size:18})}),(0,E.jsx)(`div`,{className:`content`,children:Wa(e.parts).map(e=>{if(e.kind===`tool-group`)return(0,E.jsx)(Ka,{parts:e.parts,onApprove:t,onDeny:n,onAlwaysApprove:r,approvingToolCallIds:i},e.parts[0].toolCallId);let{part:a,index:o}=e;return ci(a)?(0,E.jsx)(`div`,{className:`text-block`,children:a.text},o):q(a)?(0,E.jsx)(Ga,{part:a,onApprove:t,onDeny:n,onAlwaysApprove:r,isApproving:i?.has(a.toolCallId)},o):null})})]},e.id):null}function Ja(e,t){if(e!==`submitted`&&e!==`streaming`)return!1;if(t?.role!==`assistant`)return!0;let n=t.parts?.at(-1);return n?ci(n)?n.state!==`streaming`||n.text.length===0:!q(n)||n.state===`output-available`||n.state===`output-error`:!0}function Ya(){return(0,E.jsxs)(tt.div,{initial:{opacity:0,y:10},animate:{opacity:1,y:0},transition:{delay:.2},className:`message-bubble assistant`,children:[(0,E.jsx)(`div`,{className:`avatar`,children:(0,E.jsx)(et,{size:18})}),(0,E.jsxs)(`div`,{className:`content thinking-indicator`,role:`status`,"aria-label":`Harper Agent is thinking`,children:[(0,E.jsx)(`span`,{className:`thinking-dot`}),(0,E.jsx)(`span`,{className:`thinking-dot`}),(0,E.jsx)(`span`,{className:`thinking-dot`})]})]})}function Xa(e){return ne({queryKey:[`getMyUsage`,e],queryFn:async()=>{let{data:t}=await S.get(`/Chat/Usage/${e}`);return t}})}function Za(){let{organizationId:e}=re({strict:!1});return ae(Xa(e))}function Qa(){let{data:e,isLoading:t,error:n}=Za();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,E.jsxs)(`div`,{className:`usage-container`,children:[(0,E.jsxs)(`div`,{className:`usage-info`,children:[(0,E.jsx)(`span`,{children:`Monthly Org Usage`}),(0,E.jsxs)(`span`,{children:[o(r),` / `,o(i)]}),(0,E.jsxs)(`span`,{children:[Math.round(a),`%`]})]}),(0,E.jsx)(`div`,{className:`usage-bar-bg`,children:(0,E.jsx)(`div`,{className:`usage-bar-fill`,style:{width:`${a}%`}})})]})}function $a({autoFocus:e,closeChat:t}){let n=re({strict:!1}),{organizationId:r}=n,[i,a]=Pe(`ApplicationChat`,``),[o,s]=(0,T.useState)(!0),[c,l]=(0,T.useState)({}),[u,d]=(0,T.useState)(new Set),[f,p]=we(Ce.ChatAlwaysApprovedTools,[]),m=new Set(f),h=$e(),g=Te(),_=ie(),{messages:v,sendMessage:y,status:b,addToolOutput:x,setMessages:S}=Ii({transport:Si(r),generateId:j(),sendAutomaticallyWhen:xi,onFinish(){_.invalidateQueries({queryKey:[`getMyUsage`]})},async onToolCall({toolCall:e}){if(e.dynamic)return;let t=Ha(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,T.useCallback)(async e=>{let t=c[e];if(t){d(t=>{let n=new Set(t);return n.add(e),n});try{let r=Ha(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,T.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,T.useCallback)(async e=>{let t=c[e];t&&(p(e=>je([...e,t.toolName])),await C(e))},[c,p,C]);(0,T.useEffect)(()=>{(async()=>{try{let e=await mt();Array.isArray(e)&&S(e)}catch(e){console.error(`Failed to fetch initial messages:`,e)}finally{s(!1)}})()},[S]);let ne=b===`streaming`||b===`submitted`,ae=(0,T.useRef)(null);return(0,T.useEffect)(()=>{ae.current?.scrollIntoView({behavior:`smooth`})},[v]),(0,E.jsxs)(`div`,{className:`flex flex-col h-full`,children:[(0,E.jsxs)(`div`,{className:`flex items-start justify-between gap-6 px-4 py-2.5 border-b border-border bg-card`,children:[(0,E.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0 flex-1`,children:[(0,E.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,E.jsx)(et,{className:`text-primary`,size:20}),(0,E.jsx)(`span`,{className:`font-semibold text-foreground`,children:`Harper Agent`})]}),(0,E.jsx)(Qa,{})]}),(0,E.jsxs)(`div`,{className:`flex items-center gap-2 shrink-0`,children:[(0,E.jsx)(pt,{setMessages:S}),(0,E.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,E.jsx)(Se,{size:20})})]})]}),(0,E.jsx)(`div`,{className:`flex-1 overflow-hidden`,children:(0,E.jsxs)(`div`,{className:`chat-interface h-full w-full`,children:[(0,E.jsxs)(`div`,{className:`messages-area`,children:[o&&(0,E.jsx)(Ri,{}),!o&&v.length===0&&(0,E.jsxs)(`div`,{className:`empty-state`,children:[(0,E.jsx)(et,{size:48}),(0,E.jsx)(`p`,{children:`Ask me to create a Harper app!`})]}),v.map(e=>(0,E.jsx)(qa,{message:e,onApprove:C,onDeny:ee,onAlwaysApprove:te,approvingToolCallIds:u},e.id)),Ja(b,v.at(-1))&&(0,E.jsx)(Ya,{}),(0,E.jsx)(`div`,{ref:ae})]}),(0,E.jsx)(Li,{input:i,setInput:a,onSubmit:e=>{e.preventDefault(),i.trim()&&!ne&&!o&&(y({text:i}),a(``))},disabled:o,autoFocus:e})]})})]})}export{$a as Chat};