@holoscript/core 8.0.8 → 8.0.10

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 (463) hide show
  1. package/LICENSE +21 -0
  2. package/bin/holoscript.cjs +5 -1
  3. package/dist/GLTFPipeline-L76YTXKA.js +15 -0
  4. package/dist/GLTFPipeline-ZWJN7GI6.cjs +36 -0
  5. package/dist/HoloCompositionParser-K76KTEEE.js +12 -0
  6. package/dist/HoloCompositionParser-UZH6Y4HO.cjs +45 -0
  7. package/dist/HoloScriptCodeParser-2D4ERFUZ.js +6 -0
  8. package/dist/HoloScriptCodeParser-UDRLDA7I.cjs +23 -0
  9. package/dist/HoloScriptPlusParser-FTMCGWXD.js +13 -0
  10. package/dist/HoloScriptPlusParser-LQR5GIKF.cjs +30 -0
  11. package/dist/HoloScriptRuntime-UTRLJI5L.js +12 -0
  12. package/dist/HoloScriptRuntime-YBYXJHJN.cjs +21 -0
  13. package/dist/SceneIRCompiler-63KA3M2F.js +17 -0
  14. package/dist/SceneIRCompiler-KWUFHZ4O.cjs +42 -0
  15. package/dist/{USDZExporter-AUHZODOF.cjs → USDZExporter-ML4UMIGC.cjs} +3 -3
  16. package/dist/{USDZExporter-GXB4AUSG.js → USDZExporter-ZFKLQ76G.js} +3 -3
  17. package/dist/agents/index.cjs +1 -1
  18. package/dist/agents/index.js +1 -1
  19. package/dist/audit/AuditLogger.d.ts +106 -0
  20. package/dist/audit/AuditQueryBuilder.d.ts +79 -0
  21. package/dist/audit/ComplianceReporter.d.ts +90 -0
  22. package/dist/audit/index.d.ts +13 -0
  23. package/dist/{chunk-FGMDMJM7.js → chunk-2BYAUHBO.js} +3 -3
  24. package/dist/{chunk-EDPYCLRC.cjs → chunk-2E6PP5UE.cjs} +83 -59
  25. package/dist/{chunk-GASCWJK5.js → chunk-2EGGAZT3.js} +65 -287
  26. package/dist/{chunk-SIEXCILW.js → chunk-2I3XAGMF.js} +3 -3
  27. package/dist/{chunk-OS533P33.cjs → chunk-2KKOYZYI.cjs} +1074 -1655
  28. package/dist/chunk-2MQKNDNH.js +51 -0
  29. package/dist/{chunk-7DMMMBCY.cjs → chunk-2X3IPCBK.cjs} +10 -10
  30. package/dist/chunk-2ZOTBO25.cjs +202 -0
  31. package/dist/chunk-356N4NPM.cjs +152 -0
  32. package/dist/{chunk-X3GJPRVK.cjs → chunk-36NPLOI4.cjs} +92 -81
  33. package/dist/{chunk-SLU6UPJI.cjs → chunk-37HHY7RH.cjs} +5 -5
  34. package/dist/{chunk-YDPEHCCI.cjs → chunk-3CDDRUER.cjs} +3 -118
  35. package/dist/chunk-3F7ZZWIN.js +1651 -0
  36. package/dist/chunk-3ILD7TKP.cjs +187 -0
  37. package/dist/{chunk-X27QRG4C.cjs → chunk-3IUHTGDD.cjs} +153 -41
  38. package/dist/{chunk-PXNZ6TME.js → chunk-4376VHNJ.js} +60 -36
  39. package/dist/{chunk-YKGPCZEE.cjs → chunk-4FIBOJW6.cjs} +10 -10
  40. package/dist/chunk-4Q7RKQQI.cjs +14195 -0
  41. package/dist/{chunk-YCM6QQRT.cjs → chunk-566ZB4LZ.cjs} +180 -8
  42. package/dist/{chunk-2Y3V22SG.js → chunk-5CURIYGT.js} +3 -3
  43. package/dist/{chunk-QBWNJXNR.js → chunk-5FYMCVBY.js} +5 -5
  44. package/dist/{chunk-ZA34AN3R.js → chunk-5LVFTGKH.js} +841 -12
  45. package/dist/{chunk-Z6PMDS4G.js → chunk-5OUOMBKK.js} +1090 -53
  46. package/dist/chunk-63AUAIL6.js +48 -0
  47. package/dist/chunk-655FATJU.js +110 -0
  48. package/dist/{chunk-TQ377OLB.cjs → chunk-65S62L4D.cjs} +28 -22
  49. package/dist/chunk-6NTAZN5Z.cjs +502 -0
  50. package/dist/chunk-6TRX6AR2.js +9376 -0
  51. package/dist/chunk-6ZCCLKS2.cjs +51 -0
  52. package/dist/{chunk-MDR235RZ.js → chunk-734OB5SK.js} +721 -8
  53. package/dist/{chunk-Q2Q34NF3.js → chunk-77QI552M.js} +6 -6
  54. package/dist/{chunk-WKX5AMOY.js → chunk-7I74JWPW.js} +3 -3
  55. package/dist/{chunk-5FSKKMR7.cjs → chunk-7IDJDMPU.cjs} +232 -4
  56. package/dist/chunk-7VBKRGIZ.cjs +847 -0
  57. package/dist/{chunk-KCPHASCO.js → chunk-AATYYLYK.js} +5 -5
  58. package/dist/{chunk-DTGBOSEM.cjs → chunk-AWXFEAQL.cjs} +12 -12
  59. package/dist/{chunk-THCK6ECW.js → chunk-B3CWTCK2.js} +231 -3
  60. package/dist/chunk-BCP4E7MD.cjs +997 -0
  61. package/dist/chunk-BG7RPYJP.js +91 -0
  62. package/dist/{chunk-VF257YKO.js → chunk-BP4KTMLY.js} +117 -5
  63. package/dist/{chunk-YN6IETRZ.js → chunk-BQJ3YBF3.js} +6424 -987
  64. package/dist/{chunk-7X5PNIQJ.js → chunk-BXSOEQI3.js} +27 -25
  65. package/dist/{chunk-WFKVS2K4.cjs → chunk-C65C5VED.cjs} +1101 -55
  66. package/dist/{chunk-KOUAQL2B.cjs → chunk-CANAL6CT.cjs} +1569 -168
  67. package/dist/{chunk-FDSGHPAK.js → chunk-CBSDJF3B.js} +3 -3
  68. package/dist/{chunk-FEWUHQNM.cjs → chunk-CFVK26P4.cjs} +4 -4
  69. package/dist/{chunk-M7K4LE66.cjs → chunk-CI33NINL.cjs} +4 -4
  70. package/dist/{chunk-2OWDB4FB.js → chunk-CKV5NYAE.js} +6 -6
  71. package/dist/{chunk-LPS5AMQD.cjs → chunk-CM4NHXVO.cjs} +24 -24
  72. package/dist/chunk-D3HSRXYB.js +115 -0
  73. package/dist/{chunk-FNHJRI32.js → chunk-DBH3D2MU.js} +1561 -160
  74. package/dist/{chunk-GW3LKD5C.js → chunk-DJNH4HFW.js} +3 -3
  75. package/dist/{chunk-2GCNOI2U.cjs → chunk-DN6F4DHH.cjs} +9 -3
  76. package/dist/chunk-DQRJ5RJG.js +432 -0
  77. package/dist/{chunk-C7ZBSV2Q.js → chunk-DUG3OQAS.js} +106 -43
  78. package/dist/chunk-ECUYJOJ2.cjs +199 -0
  79. package/dist/chunk-EEMTCAK7.js +182 -0
  80. package/dist/{chunk-3SKLAUDD.cjs → chunk-EI3J6LTW.cjs} +359 -581
  81. package/dist/{chunk-SSSN7VAM.js → chunk-EM2OGPYE.js} +4 -4
  82. package/dist/{chunk-56VOWURB.cjs → chunk-ENZCIQZC.cjs} +522 -453
  83. package/dist/chunk-ESBXUYOC.cjs +836 -0
  84. package/dist/{chunk-L5U4W6JC.js → chunk-ESSEF6U2.js} +4 -4
  85. package/dist/chunk-ET3AN4W3.js +824 -0
  86. package/dist/chunk-EXKSFGSX.cjs +14 -14
  87. package/dist/{chunk-I76GIKN7.cjs → chunk-F3YT6GUF.cjs} +17 -17
  88. package/dist/{chunk-WRWEX5ZF.js → chunk-F75DZBBI.js} +3 -3
  89. package/dist/{chunk-4JGRWHZJ.js → chunk-FG6PYGIZ.js} +12 -6
  90. package/dist/chunk-FGGJQRLZ.js +14 -14
  91. package/dist/{chunk-SXOSDZD6.js → chunk-FWCHPCD4.js} +128 -10
  92. package/dist/{chunk-XW6IXEQH.cjs → chunk-G35CAQ7I.cjs} +5 -5
  93. package/dist/{chunk-7VMGP2FW.js → chunk-G6KACNMO.js} +132 -188
  94. package/dist/{chunk-FJPADH77.cjs → chunk-GAWJJAGT.cjs} +6 -6
  95. package/dist/chunk-GGR7QUKK.cjs +4724 -0
  96. package/dist/chunk-GIBF4BHH.js +14098 -0
  97. package/dist/{chunk-AWB7QYY6.cjs → chunk-GILYQ5VH.cjs} +6 -6
  98. package/dist/{chunk-FWVYAMNW.cjs → chunk-GKFCHGCM.cjs} +4 -4
  99. package/dist/{chunk-Y3ZFVUUU.js → chunk-GLEJ2TLS.js} +3 -3
  100. package/dist/{chunk-3GWWJT7L.js → chunk-GP3OKSKT.js} +13 -13
  101. package/dist/chunk-GTIPOEHN.js +196 -0
  102. package/dist/{chunk-LMJ5EBV7.cjs → chunk-GZPUA2TL.cjs} +16 -15
  103. package/dist/{chunk-IELX2OKI.js → chunk-H4LJ4HEA.js} +3 -115
  104. package/dist/{chunk-ACVWFGBI.cjs → chunk-HCX4SMFK.cjs} +3 -3
  105. package/dist/{chunk-HYNTS5CP.js → chunk-HHRZ6STP.js} +24 -2
  106. package/dist/chunk-HIDEIEFG.js +48 -0
  107. package/dist/{chunk-W6RW64BB.cjs → chunk-HSWBIDZM.cjs} +228 -5
  108. package/dist/{chunk-GSOUNWEL.js → chunk-HVX7QQYK.js} +1035 -1616
  109. package/dist/{chunk-TRCGG42I.cjs → chunk-IFW5IW4L.cjs} +6502 -1019
  110. package/dist/{chunk-F5LKJ6F5.cjs → chunk-IJU6AS67.cjs} +32 -28
  111. package/dist/{chunk-TVANHNJ7.js → chunk-IK6Z2HNC.js} +173 -3
  112. package/dist/{chunk-QGMUTVVH.cjs → chunk-IS7NTKY5.cjs} +20 -20
  113. package/dist/{chunk-YFVQWSNJ.js → chunk-IXWGYWH6.js} +3 -3
  114. package/dist/chunk-J2DAS2PL.js +148 -0
  115. package/dist/chunk-JBQXZIWB.js +148 -0
  116. package/dist/chunk-JBYBMBMW.js +261 -0
  117. package/dist/{chunk-LWUOUCYL.js → chunk-JEJ7TT54.js} +5 -5
  118. package/dist/{chunk-DUCB6LGL.js → chunk-JOTCTHN5.js} +211 -5
  119. package/dist/{chunk-47RUFYKR.js → chunk-K56UVEXV.js} +3 -3
  120. package/dist/{chunk-XSFNHP2E.js → chunk-KB26H447.js} +3 -3
  121. package/dist/{chunk-3NGDZZR4.js → chunk-KEYG7HGY.js} +430 -37
  122. package/dist/{chunk-DY72BMU5.js → chunk-KFVNCW4Q.js} +4 -4
  123. package/dist/chunk-KPGC7SK2.js +192 -0
  124. package/dist/chunk-KROXXBK5.cjs +53 -0
  125. package/dist/{chunk-ZPZAVHXL.cjs → chunk-LB3ZZMKT.cjs} +137 -19
  126. package/dist/chunk-LDYZWNJW.cjs +1654 -0
  127. package/dist/{chunk-UGXHYIWT.cjs → chunk-LHNHO27U.cjs} +28 -28
  128. package/dist/{chunk-ULMSSHCJ.cjs → chunk-LMB555QB.cjs} +405 -38
  129. package/dist/{chunk-CBJY23VL.js → chunk-MD4IVS42.js} +3 -3
  130. package/dist/{chunk-VMLKSV26.js → chunk-MDDWEBCX.js} +226 -3
  131. package/dist/chunk-MUWH4QK5.cjs +94 -0
  132. package/dist/{chunk-T6W3Z3SH.cjs → chunk-N2ILK7BF.cjs} +4 -4
  133. package/dist/{chunk-6EX3I5P4.cjs → chunk-N74QRJGP.cjs} +6 -6
  134. package/dist/chunk-NFFHLX37.cjs +132 -0
  135. package/dist/chunk-NJSYWX7H.js +190 -0
  136. package/dist/{chunk-SH5PPVK7.cjs → chunk-NQN5LFN4.cjs} +165 -221
  137. package/dist/chunk-NTQRFOPP.cjs +56 -0
  138. package/dist/{chunk-YFWRJW4B.js → chunk-NWMJPTVL.js} +51 -55
  139. package/dist/{chunk-63ZGNR2T.cjs → chunk-OC6GV5QH.cjs} +42 -42
  140. package/dist/{chunk-7E7GCQCK.js → chunk-OG3MBHM3.js} +9 -3
  141. package/dist/chunk-OMNHTCS4.cjs +264 -0
  142. package/dist/chunk-OQXB6NNC.cjs +117 -0
  143. package/dist/{chunk-Y57W2CT5.cjs → chunk-OV46CCA2.cjs} +51 -55
  144. package/dist/{chunk-6PRZIHGL.cjs → chunk-OV6HHKLE.cjs} +21 -266
  145. package/dist/{chunk-C37BHB7N.cjs → chunk-P26YYPOO.cjs} +5 -5
  146. package/dist/{chunk-2BZHWUSR.cjs → chunk-P7JEKJTZ.cjs} +16 -16
  147. package/dist/{chunk-NGBXXZLL.cjs → chunk-PDXX5UI4.cjs} +45 -45
  148. package/dist/{chunk-FHQFPYV4.js → chunk-PJFTTSKE.js} +8 -7
  149. package/dist/{chunk-GVFJ6T4S.cjs → chunk-PVODJEJ7.cjs} +721 -8
  150. package/dist/{chunk-ZZ7RO2UQ.js → chunk-Q265VZJ4.js} +3 -3
  151. package/dist/{chunk-E7JP3BS3.cjs → chunk-Q47JEE2C.cjs} +4 -4
  152. package/dist/chunk-QBY7LRN2.js +281 -0
  153. package/dist/{chunk-GH6C2QGW.cjs → chunk-QEDMOGF3.cjs} +857 -16
  154. package/dist/{chunk-5WUNN6ZN.cjs → chunk-QHSAVOOL.cjs} +24 -2
  155. package/dist/chunk-QJNCDVYM.cjs +137 -0
  156. package/dist/chunk-QM7FJB5W.js +4717 -0
  157. package/dist/chunk-QQFB4RT2.cjs +448 -0
  158. package/dist/chunk-QSYBHQAD.cjs +196 -0
  159. package/dist/{chunk-2YSNA2F2.cjs → chunk-QVJMDVYZ.cjs} +5 -5
  160. package/dist/chunk-RAJMAX6D.js +131 -0
  161. package/dist/{chunk-VVKVQZI6.js → chunk-RAN3ECAS.js} +3 -3
  162. package/dist/chunk-RKGUZBOC.js +79 -0
  163. package/dist/{chunk-HLFDZ35S.cjs → chunk-RRQIAJLQ.cjs} +8 -8
  164. package/dist/{chunk-PABONWKI.js → chunk-RXQNRZ5F.js} +3 -3
  165. package/dist/{chunk-7ZHYEITD.cjs → chunk-S4O7CFHQ.cjs} +10 -11
  166. package/dist/chunk-SE7ANBGN.js +128 -0
  167. package/dist/{chunk-X2AVEAZM.cjs → chunk-SJIRCTIS.cjs} +176 -2
  168. package/dist/{chunk-WXQ57LAO.js → chunk-SQXAZHDG.js} +4 -4
  169. package/dist/chunk-SR73L5ZP.cjs +84 -0
  170. package/dist/{chunk-OABEO6HH.js → chunk-SZOLTWWD.js} +2349 -214
  171. package/dist/{chunk-5JEU52DW.js → chunk-TPZ3BN2D.js} +3 -3
  172. package/dist/{chunk-QFU53KRH.js → chunk-TSTFYGGX.js} +4 -5
  173. package/dist/{chunk-EBS32AJI.cjs → chunk-TZCFKZ6Z.cjs} +5 -5
  174. package/dist/chunk-UCVZXQ63.js +724 -0
  175. package/dist/chunk-UGQ3QNV6.js +992 -0
  176. package/dist/chunk-UNMVGZVW.js +142 -0
  177. package/dist/{chunk-TEVX443G.js → chunk-UTUD6GJI.js} +178 -6
  178. package/dist/chunk-UXBYA7DV.cjs +155 -0
  179. package/dist/chunk-V6GXFSK7.cjs +443 -0
  180. package/dist/chunk-VHORVGCG.cjs +761 -0
  181. package/dist/{chunk-B4EKV6HQ.js → chunk-VKCTYLDN.js} +500 -442
  182. package/dist/{chunk-O3MGZDCT.cjs → chunk-VTEXJOSD.cjs} +63 -63
  183. package/dist/chunk-VVAAZITA.cjs +287 -0
  184. package/dist/{chunk-V7AKE5VT.cjs → chunk-W2GA3EQB.cjs} +435 -42
  185. package/dist/chunk-W4GABFCR.js +37 -0
  186. package/dist/chunk-WKEK4NCA.js +490 -0
  187. package/dist/{chunk-5A4TCHOC.cjs → chunk-WNYXT4TE.cjs} +4 -4
  188. package/dist/{chunk-S762UEV5.js → chunk-WTH23ISI.js} +6 -6
  189. package/dist/{chunk-BTKZGHAV.js → chunk-WWNCM473.js} +10 -6
  190. package/dist/{chunk-4YNYPHH3.cjs → chunk-X75H75UY.cjs} +2351 -213
  191. package/dist/{chunk-LYJJI7ZK.cjs → chunk-XCUDDFKC.cjs} +216 -9
  192. package/dist/chunk-YAO4HPJL.cjs +120 -0
  193. package/dist/chunk-YKY2JBER.cjs +9443 -0
  194. package/dist/chunk-YP5CHEYU.cjs +154 -0
  195. package/dist/{chunk-WRNEH5MO.js → chunk-YW3I2FQL.js} +18 -7
  196. package/dist/{chunk-BRNUPDHJ.js → chunk-Z557DVYN.js} +386 -19
  197. package/dist/{chunk-VSACG2AR.js → chunk-Z6LZUYZF.js} +21 -264
  198. package/dist/chunk-ZHPMP447.cjs +43 -0
  199. package/dist/{chunk-LODTHGST.cjs → chunk-ZK7KX5CD.cjs} +133 -70
  200. package/dist/{chunk-CCPSNT6B.cjs → chunk-ZRPALBQB.cjs} +5 -5
  201. package/dist/chunk-ZVFHPMC5.js +824 -0
  202. package/dist/chunk-ZVQMKUGJ.js +443 -0
  203. package/dist/cli/holoscript-runner.cjs +86 -48
  204. package/dist/cli/holoscript-runner.js +54 -16
  205. package/dist/codebase/index.cjs +1 -1
  206. package/dist/codebase/index.js +1 -1
  207. package/dist/compiler/agent-inference.cjs +11 -11
  208. package/dist/compiler/agent-inference.js +9 -6
  209. package/dist/compiler/android-xr.cjs +13 -12
  210. package/dist/compiler/android-xr.js +11 -10
  211. package/dist/compiler/android.cjs +12 -12
  212. package/dist/compiler/android.js +10 -10
  213. package/dist/compiler/business-quest.cjs +10 -10
  214. package/dist/compiler/business-quest.js +2 -2
  215. package/dist/compiler/coco.cjs +1 -1
  216. package/dist/compiler/coco.js +1 -1
  217. package/dist/compiler/context.cjs +9 -9
  218. package/dist/compiler/context.js +6 -6
  219. package/dist/compiler/daimon-seed.cjs +36 -0
  220. package/dist/compiler/daimon-seed.js +12 -0
  221. package/dist/compiler/domain-block-utils.cjs +205 -156
  222. package/dist/compiler/domain-block-utils.js +8 -7
  223. package/dist/compiler/dtdl.cjs +10 -10
  224. package/dist/compiler/dtdl.js +7 -7
  225. package/dist/compiler/gltf-pipeline.cjs +9 -9
  226. package/dist/compiler/gltf-pipeline.js +2 -2
  227. package/dist/compiler/godot.cjs +10 -9
  228. package/dist/compiler/godot.js +9 -8
  229. package/dist/compiler/incremental.cjs +11 -11
  230. package/dist/compiler/incremental.js +6 -6
  231. package/dist/compiler/index.cjs +2585 -3273
  232. package/dist/compiler/index.d.ts +149 -35
  233. package/dist/compiler/index.js +2197 -3102
  234. package/dist/compiler/ios.cjs +11 -11
  235. package/dist/compiler/ios.js +9 -9
  236. package/dist/compiler/llm-provider-capabilities.cjs +9 -9
  237. package/dist/compiler/llm-provider-capabilities.js +6 -6
  238. package/dist/compiler/nodetoy.cjs +1 -1
  239. package/dist/compiler/nodetoy.js +1 -1
  240. package/dist/compiler/openxr.cjs +11 -10
  241. package/dist/compiler/openxr.js +10 -9
  242. package/dist/compiler/remotion.cjs +1 -1
  243. package/dist/compiler/remotion.js +1 -1
  244. package/dist/compiler/reproducibility.cjs +1 -1
  245. package/dist/compiler/reproducibility.js +1 -1
  246. package/dist/compiler/sdf.cjs +11 -10
  247. package/dist/compiler/sdf.js +9 -8
  248. package/dist/compiler/semantic-scene.cjs +7 -7
  249. package/dist/compiler/semantic-scene.js +2 -2
  250. package/dist/compiler/state.cjs +9 -9
  251. package/dist/compiler/state.js +7 -7
  252. package/dist/compiler/trait-composition.cjs +11 -11
  253. package/dist/compiler/trait-composition.js +7 -7
  254. package/dist/compiler/traits/ProvenanceSemiring.d.ts +167 -0
  255. package/dist/compiler/traits/Semiring.d.ts +26 -0
  256. package/dist/compiler/unity.cjs +11 -10
  257. package/dist/compiler/unity.js +10 -9
  258. package/dist/compiler/unreal.cjs +12 -11
  259. package/dist/compiler/unreal.js +10 -9
  260. package/dist/compiler/urdf.cjs +18 -17
  261. package/dist/compiler/urdf.js +9 -8
  262. package/dist/compiler/usd-physics.cjs +11 -10
  263. package/dist/compiler/usd-physics.js +8 -7
  264. package/dist/compiler/visionos.cjs +11 -10
  265. package/dist/compiler/visionos.js +10 -9
  266. package/dist/compiler/vrchat.cjs +11 -10
  267. package/dist/compiler/vrchat.js +9 -8
  268. package/dist/compiler/wasm.cjs +14 -13
  269. package/dist/compiler/wasm.js +9 -8
  270. package/dist/compiler/webgpu.cjs +10 -9
  271. package/dist/compiler/webgpu.js +9 -8
  272. package/dist/constants.cjs +398 -0
  273. package/dist/constants.js +9 -0
  274. package/dist/coordinators/index.cjs +4 -1
  275. package/dist/coordinators/index.js +4 -1
  276. package/dist/debugger.cjs +11 -9
  277. package/dist/debugger.js +9 -7
  278. package/dist/{dist-ACXG6IH5.cjs → dist-GRMOP2UL.cjs} +341 -65
  279. package/dist/{dist-VD4L6V7Q.js → dist-ZBUKU2JG.js} +333 -63
  280. package/dist/entries/interop.cjs +23 -23
  281. package/dist/entries/interop.js +3 -3
  282. package/dist/entries/scripting.cjs +12 -11
  283. package/dist/entries/scripting.js +8 -7
  284. package/dist/evolution/index.cjs +66 -0
  285. package/dist/evolution/index.d.ts +162 -0
  286. package/dist/evolution/index.js +17 -0
  287. package/dist/hololand/index.cjs +41 -37
  288. package/dist/hololand/index.js +3 -3
  289. package/dist/index.cjs +12394 -3224
  290. package/dist/index.d.ts +260 -18
  291. package/dist/index.js +10755 -2182
  292. package/dist/keyword-registry/PluginKeywordRegistry.d.ts +138 -0
  293. package/dist/keyword-registry/index.cjs +29 -0
  294. package/dist/keyword-registry/index.d.ts +1 -0
  295. package/dist/keyword-registry/index.js +4 -0
  296. package/dist/math/tropical-spmv.cjs +11 -11
  297. package/dist/math/tropical-spmv.d.ts +1 -0
  298. package/dist/math/tropical-spmv.js +2 -2
  299. package/dist/math/tropicalSpmv.d.ts +43 -0
  300. package/dist/math/vec3.cjs +1 -1
  301. package/dist/math/vec3.js +1 -1
  302. package/dist/matter/StagedMatter.d.ts +82 -0
  303. package/dist/matter/index.cjs +17 -0
  304. package/dist/matter/index.d.ts +1 -0
  305. package/dist/matter/index.js +4 -0
  306. package/dist/{ml-dsa-Z7JDKW4L.js → ml-dsa-J6VKVT4K.js} +3 -3
  307. package/dist/{ml-dsa-FSKBTE3W.cjs → ml-dsa-MTIK4ZUQ.cjs} +23 -23
  308. package/dist/paper-0c-spike/index.cjs +1 -1
  309. package/dist/paper-0c-spike/index.js +1 -1
  310. package/dist/parameter-envelope/ParameterEnvelope.d.ts +128 -0
  311. package/dist/parameter-envelope/index.cjs +17 -0
  312. package/dist/parameter-envelope/index.d.ts +2 -0
  313. package/dist/parameter-envelope/index.js +4 -0
  314. package/dist/parser/HoloCompositionTypes.d.ts +2013 -0
  315. package/dist/parser/PipelineParser.d.ts +162 -0
  316. package/dist/parser.cjs +36 -20
  317. package/dist/parser.js +13 -9
  318. package/dist/plugin-manifest/ManifestAutoLoader.d.ts +35 -0
  319. package/dist/plugin-manifest/PluginManifest.d.ts +156 -0
  320. package/dist/plugin-manifest/index.cjs +25 -0
  321. package/dist/plugin-manifest/index.d.ts +2 -0
  322. package/dist/plugin-manifest/index.js +4 -0
  323. package/dist/plugin-solver-contract/PluginSolverContract.d.ts +288 -0
  324. package/dist/plugin-solver-contract/index.cjs +41 -0
  325. package/dist/plugin-solver-contract/index.d.ts +1 -0
  326. package/dist/plugin-solver-contract/index.js +4 -0
  327. package/dist/policy/ContentPolicyGate.d.ts +51 -0
  328. package/dist/policy/PolicyPack.d.ts +120 -0
  329. package/dist/policy/blocklist.d.ts +32 -0
  330. package/dist/policy/defaults.d.ts +44 -0
  331. package/dist/policy/index.cjs +106 -0
  332. package/dist/policy/index.d.ts +17 -0
  333. package/dist/policy/index.js +5 -0
  334. package/dist/policy/jsonLogic.d.ts +31 -0
  335. package/dist/policy/types.d.ts +174 -0
  336. package/dist/proof-composition/ProofCompositionLaw.d.ts +93 -0
  337. package/dist/proof-composition/index.cjs +25 -0
  338. package/dist/proof-composition/index.d.ts +1 -0
  339. package/dist/proof-composition/index.js +4 -0
  340. package/dist/receipts/hash-policy.d.ts +85 -0
  341. package/dist/reconstruction/index.cjs +75 -47
  342. package/dist/reconstruction/index.js +5 -5
  343. package/dist/runtime/plugin-trait-registrar.d.ts +36 -0
  344. package/dist/runtime.cjs +81 -79
  345. package/dist/runtime.js +9 -7
  346. package/dist/scene-presets-EDKYZ5GW.js +4 -0
  347. package/dist/scene-presets-NCW2CFRS.cjs +17 -0
  348. package/dist/self-improvement/index.cjs +1 -1
  349. package/dist/self-improvement/index.js +1 -1
  350. package/dist/{src-PT2FERDV.cjs → src-G3D7ZATQ.cjs} +545 -21
  351. package/dist/{src-J6CM4AUR.js → src-UXYA7HM6.js} +539 -16
  352. package/dist/storage/index.cjs +10 -10
  353. package/dist/storage/index.js +3 -3
  354. package/dist/testing.cjs +1 -1
  355. package/dist/testing.js +1 -1
  356. package/dist/tools/index.cjs +3631 -0
  357. package/dist/tools/index.js +3581 -0
  358. package/dist/traitDocs/traitDocs.cjs +9 -9
  359. package/dist/traitDocs/traitDocs.d.ts +1 -0
  360. package/dist/traitDocs/traitDocs.js +3 -3
  361. package/dist/traits/ControlLoopTrait.d.ts +154 -0
  362. package/dist/traits/KinematicChainTrait.d.ts +116 -0
  363. package/dist/traits/SensorSamplingTrait.d.ts +154 -0
  364. package/dist/traits/TraitTypes.d.ts +270 -0
  365. package/dist/traits/TransactionTrait.d.ts +121 -0
  366. package/dist/traits/botanical-lotus.cjs +54 -50
  367. package/dist/traits/botanical-lotus.js +2 -5
  368. package/dist/traits/control-loop.cjs +21 -0
  369. package/dist/traits/control-loop.d.ts +1 -0
  370. package/dist/traits/control-loop.js +4 -0
  371. package/dist/traits/engines/cloth-verlet.d.ts +65 -0
  372. package/dist/traits/engines/index.cjs +19 -19
  373. package/dist/traits/engines/index.d.ts +15 -275
  374. package/dist/traits/engines/index.js +3 -3
  375. package/dist/traits/engines/motion-data-schema.d.ts +119 -0
  376. package/dist/traits/engines/motion-matching.d.ts +102 -0
  377. package/dist/traits/engines/noise.d.ts +25 -0
  378. package/dist/traits/engines/onnx-adapter.d.ts +173 -0
  379. package/dist/traits/engines/onnx-motion-matching.d.ts +163 -0
  380. package/dist/traits/engines/pfnn-network.d.ts +92 -0
  381. package/dist/traits/engines/synthetic-walk-cycle.d.ts +38 -0
  382. package/dist/traits/engines/tensor-ops.d.ts +15 -0
  383. package/dist/traits/index.cjs +2789 -1259
  384. package/dist/traits/index.d.ts +35 -0
  385. package/dist/traits/index.js +1692 -411
  386. package/dist/traits/kinematic-chain.cjs +25 -0
  387. package/dist/traits/kinematic-chain.d.ts +1 -0
  388. package/dist/traits/kinematic-chain.js +4 -0
  389. package/dist/traits/sensor-sampling.cjs +33 -0
  390. package/dist/traits/sensor-sampling.d.ts +1 -0
  391. package/dist/traits/sensor-sampling.js +4 -0
  392. package/dist/traits/simulation-solver-factory.cjs +1 -1
  393. package/dist/traits/simulation-solver-factory.js +1 -1
  394. package/dist/traits/transaction.cjs +29 -0
  395. package/dist/traits/transaction.d.ts +1 -0
  396. package/dist/traits/transaction.js +4 -0
  397. package/dist/traits/webcam-gaze.cjs +1 -1
  398. package/dist/traits/webcam-gaze.js +1 -1
  399. package/dist/type-checker.cjs +7 -6
  400. package/dist/type-checker.js +5 -4
  401. package/dist/types/AdvancedTypeSystem.d.ts +489 -0
  402. package/dist/types/HoloScriptPlus.d.ts +810 -0
  403. package/dist/types/base.d.ts +248 -0
  404. package/dist/types.d.ts +799 -0
  405. package/dist/world/index.cjs +3 -4
  406. package/dist/world/index.js +3 -4
  407. package/dist/world-model/index.cjs +1 -1
  408. package/dist/world-model/index.js +1 -1
  409. package/dist/wot/index.cjs +1 -1
  410. package/dist/wot/index.js +1 -1
  411. package/package.json +135 -69
  412. package/dist/GLTFPipeline-3EJLL3TS.cjs +0 -38
  413. package/dist/GLTFPipeline-3OWJV2VK.js +0 -17
  414. package/dist/HoloScriptPlusParser-ADWRY5OS.cjs +0 -698
  415. package/dist/HoloScriptPlusParser-JLJDXHSC.js +0 -693
  416. package/dist/HoloScriptPlusParser-QCGFPWIL.cjs +0 -27
  417. package/dist/HoloScriptPlusParser-WENAFEFV.js +0 -10
  418. package/dist/HoloScriptRuntime-GGRVEU4T.cjs +0 -19
  419. package/dist/HoloScriptRuntime-GJK6VJPH.js +0 -10
  420. package/dist/chunk-22Q5A6RV.js +0 -979
  421. package/dist/chunk-4C57PR42.js +0 -468
  422. package/dist/chunk-4W5G5QVL.cjs +0 -847
  423. package/dist/chunk-5ET6L4NS.js +0 -487
  424. package/dist/chunk-C4GSLCHE.cjs +0 -7153
  425. package/dist/chunk-F2G6CWFT.js +0 -7089
  426. package/dist/chunk-G6PNEASF.cjs +0 -814
  427. package/dist/chunk-GTA4Q3A3.js +0 -1491
  428. package/dist/chunk-HCCQLAA4.cjs +0 -74
  429. package/dist/chunk-HFSVMKM3.cjs +0 -72
  430. package/dist/chunk-KAIVDEIG.cjs +0 -4539
  431. package/dist/chunk-KQU64CIX.cjs +0 -1508
  432. package/dist/chunk-MSYSQ2AJ.js +0 -4533
  433. package/dist/chunk-NHJLP37F.js +0 -70
  434. package/dist/chunk-NHYRRAGI.cjs +0 -490
  435. package/dist/chunk-OIQYIIO3.cjs +0 -473
  436. package/dist/chunk-OL46QLBJ.js +0 -61
  437. package/dist/chunk-SKZPC5AG.cjs +0 -984
  438. package/dist/chunk-SLHLXUMO.js +0 -445
  439. package/dist/chunk-UOIGFQXC.cjs +0 -9750
  440. package/dist/chunk-UOL6Y5TD.cjs +0 -759
  441. package/dist/chunk-UVFI7XIR.cjs +0 -452
  442. package/dist/chunk-VEPGBETF.js +0 -844
  443. package/dist/chunk-XQ5MWMP2.js +0 -756
  444. package/dist/chunk-XT6JY5W5.js +0 -9684
  445. package/dist/chunk-YRRAUB35.js +0 -811
  446. package/dist/compiler/GasOptimizationAnalyzer.d.ts +0 -73
  447. package/dist/compiler/NFTMarketplaceCompiler.d.ts +0 -23
  448. package/dist/compiler/ar.cjs +0 -23
  449. package/dist/compiler/ar.js +0 -11
  450. package/dist/compiler/babylon.cjs +0 -21
  451. package/dist/compiler/babylon.js +0 -15
  452. package/dist/compiler/multi-layer.cjs +0 -24
  453. package/dist/compiler/multi-layer.js +0 -15
  454. package/dist/compiler/playcanvas.cjs +0 -19
  455. package/dist/compiler/playcanvas.js +0 -13
  456. package/dist/compiler/r3f.cjs +0 -39
  457. package/dist/compiler/r3f.d.ts +0 -22
  458. package/dist/compiler/r3f.js +0 -17
  459. package/dist/compiler/vrr.cjs +0 -24
  460. package/dist/compiler/vrr.js +0 -12
  461. package/dist/parser/NFTMarketplaceTypes.d.ts +0 -191
  462. package/dist/playwright-3AT3AKTV.cjs +0 -98673
  463. package/dist/playwright-VZPDVUEQ.js +0 -98660
@@ -1,7 +1,34 @@
1
- import { ErrorRecovery, generateQuickFixes, enrichErrorWithSuggestions, LIFECYCLE_HOOKS, STRUCTURAL_DIRECTIVES } from './chunk-VSACG2AR.js';
2
- import { VR_TRAITS } from './chunk-GASCWJK5.js';
1
+ import { ErrorRecovery, generateQuickFixes, enrichErrorWithSuggestions } from './chunk-Z6LZUYZF.js';
2
+ import { isBrainKeyword, isCognitiveVerb, nearestCognitiveVerb } from './chunk-UNMVGZVW.js';
3
+ import { LIFECYCLE_HOOKS, STRUCTURAL_DIRECTIVES } from './chunk-JBYBMBMW.js';
4
+ import { VR_TRAITS } from './chunk-2EGGAZT3.js';
5
+ import { isLocomotionReactionTrigger } from './chunk-63AUAIL6.js';
3
6
  import * as crypto from 'crypto';
4
7
 
8
+ // src/traits/FrameDeclarationTrait.ts
9
+ var DEFAULT_FRAME = {
10
+ domain: "*",
11
+ horizon: "",
12
+ capability_tier: 2,
13
+ trust_tier: 2};
14
+ function coerceConfig(raw) {
15
+ const domain = typeof raw.domain === "string" ? raw.domain : DEFAULT_FRAME.domain;
16
+ const horizon = typeof raw.horizon === "string" ? raw.horizon : DEFAULT_FRAME.horizon;
17
+ let capability_tier = DEFAULT_FRAME.capability_tier;
18
+ const rawCT = raw.capability_tier;
19
+ if (rawCT === 0 || rawCT === 1 || rawCT === 2 || rawCT === 3) {
20
+ capability_tier = rawCT;
21
+ }
22
+ let trust_tier = DEFAULT_FRAME.trust_tier;
23
+ const rawTT = raw.trust_tier;
24
+ if (rawTT === 0 || rawTT === 1 || rawTT === 2 || rawTT === 3) {
25
+ trust_tier = rawTT;
26
+ }
27
+ const allowed_tools = Array.isArray(raw.allowed_tools) ? raw.allowed_tools.filter((t) => typeof t === "string") : [];
28
+ const denied_domains = Array.isArray(raw.denied_domains) ? raw.denied_domains.filter((d) => typeof d === "string") : [];
29
+ return { domain, horizon, capability_tier, trust_tier, allowed_tools, denied_domains };
30
+ }
31
+
5
32
  // src/parser/HybridChunker.ts
6
33
  function countTokens(text) {
7
34
  return Math.ceil(text.length / 4);
@@ -1142,13 +1169,18 @@ var Lexer = class {
1142
1169
  this.tokens.push(this.createToken("DEDENT", ""));
1143
1170
  this.pendingDedents--;
1144
1171
  }
1172
+ if (this.column === 1) {
1173
+ const beforePos = this.pos;
1174
+ const beforeTokenCount = this.tokens.length;
1175
+ const beforePendingDedents = this.pendingDedents;
1176
+ this.handleIndentation();
1177
+ if (this.pos !== beforePos || this.tokens.length !== beforeTokenCount || this.pendingDedents !== beforePendingDedents) {
1178
+ continue;
1179
+ }
1180
+ }
1145
1181
  const char = this.source[this.pos];
1146
1182
  if (char === " " || char === " ") {
1147
- if (this.column === 1) {
1148
- this.handleIndentation();
1149
- } else {
1150
- this.advance();
1151
- }
1183
+ this.advance();
1152
1184
  continue;
1153
1185
  }
1154
1186
  if (char === "/" && this.peek(1) === "/") {
@@ -1754,13 +1786,15 @@ var Lexer = class {
1754
1786
  return this.isIdentifierStart(char) || this.isDigit(char) || char === "-";
1755
1787
  }
1756
1788
  };
1757
- var HoloScriptPlusParser = class {
1789
+ var HoloScriptPlusParser = class _HoloScriptPlusParser {
1758
1790
  constructor(options = {}) {
1759
1791
  this.tokens = [];
1760
1792
  this.pos = 0;
1761
1793
  this.source = "";
1762
1794
  this.errors = [];
1763
1795
  this.warnings = [];
1796
+ this.blockConfigDirectives = /* @__PURE__ */ new WeakSet();
1797
+ this.blockConfigDirectiveLocations = /* @__PURE__ */ new WeakMap();
1764
1798
  this.imports = [];
1765
1799
  this.hasState = false;
1766
1800
  this.hasVRTraits = false;
@@ -1829,6 +1863,8 @@ var HoloScriptPlusParser = class {
1829
1863
  this.source = source;
1830
1864
  this.errors = [];
1831
1865
  this.warnings = [];
1866
+ this.blockConfigDirectives = /* @__PURE__ */ new WeakSet();
1867
+ this.blockConfigDirectiveLocations = /* @__PURE__ */ new WeakMap();
1832
1868
  this.imports = [];
1833
1869
  this.hasState = false;
1834
1870
  this.hasVRTraits = false;
@@ -1839,8 +1875,110 @@ var HoloScriptPlusParser = class {
1839
1875
  const lexer = new Lexer(source);
1840
1876
  this.tokens = lexer.tokenize();
1841
1877
  const root = this.parseDocument();
1878
+ this.desugarSafeDaemon(root);
1842
1879
  return this.buildResult(root);
1843
1880
  }
1881
+ /** Canonical safety-trait configs that `@safe_daemon` expands into. */
1882
+ static safeDaemonDefaults() {
1883
+ return {
1884
+ rate_limiter: { strategy: "token_bucket", max_tokens: 20, refill_rate: 4, window_ms: 6e4 },
1885
+ circuit_breaker: {
1886
+ failure_threshold: 5,
1887
+ window_ms: 3e5,
1888
+ reset_timeout_ms: 6e5,
1889
+ success_threshold: 2,
1890
+ failure_rate_threshold: 0,
1891
+ min_requests: 5
1892
+ },
1893
+ timeout_guard: { default_timeout_ms: 3e4, fallback_action: "abort" },
1894
+ economy: {
1895
+ initial_balance: 5,
1896
+ default_spend_limit: 1,
1897
+ spend_limit_period: 36e5,
1898
+ max_transaction_history: 200,
1899
+ escrow_enabled: false
1900
+ },
1901
+ structured_logger: {
1902
+ min_level: "info",
1903
+ max_entries: 500,
1904
+ rotation_count: 100,
1905
+ emit_events: true,
1906
+ console_output: true
1907
+ }
1908
+ };
1909
+ }
1910
+ /**
1911
+ * Expand a `@safe_daemon { ... }` config into the five safety traits it
1912
+ * stands for (@rate_limiter + @circuit_breaker + @timeout_guard + @economy +
1913
+ * @structured_logger). Supports flat convenience overrides (budget / rate /
1914
+ * timeout_ms / …) and nested per-trait overrides (economy: { ... }).
1915
+ */
1916
+ expandSafeDaemon(config) {
1917
+ const c = config && typeof config === "object" ? config : {};
1918
+ const out = _HoloScriptPlusParser.safeDaemonDefaults();
1919
+ const num = (v) => typeof v === "number";
1920
+ if (num(c.budget)) out.economy.initial_balance = c.budget;
1921
+ if (num(c.spend_limit)) out.economy.default_spend_limit = c.spend_limit;
1922
+ if (num(c.rate)) out.rate_limiter.refill_rate = c.rate;
1923
+ if (num(c.max_tokens)) out.rate_limiter.max_tokens = c.max_tokens;
1924
+ if (num(c.window_ms)) out.rate_limiter.window_ms = c.window_ms;
1925
+ if (num(c.failure_threshold)) out.circuit_breaker.failure_threshold = c.failure_threshold;
1926
+ if (num(c.reset_timeout_ms)) out.circuit_breaker.reset_timeout_ms = c.reset_timeout_ms;
1927
+ if (num(c.timeout_ms)) out.timeout_guard.default_timeout_ms = c.timeout_ms;
1928
+ if (typeof c.log_level === "string") out.structured_logger.min_level = c.log_level;
1929
+ for (const t of ["rate_limiter", "circuit_breaker", "timeout_guard", "economy", "structured_logger"]) {
1930
+ const override = c[t];
1931
+ if (override && typeof override === "object") Object.assign(out[t], override);
1932
+ }
1933
+ return Object.entries(out);
1934
+ }
1935
+ /**
1936
+ * Walk the parsed AST and expand every `@safe_daemon` into its five safety
1937
+ * traits, wherever traits are stored: a node's `traits` Map (composition /
1938
+ * world / object), a brain's `traits` Record, and any `directives` array.
1939
+ * Existing per-trait declarations are not overwritten.
1940
+ */
1941
+ desugarSafeDaemon(root) {
1942
+ const seen = /* @__PURE__ */ new Set();
1943
+ const visit = (node) => {
1944
+ if (!node || typeof node !== "object" || seen.has(node)) return;
1945
+ seen.add(node);
1946
+ const n = node;
1947
+ if (n.traits instanceof Map && n.traits.has("safe_daemon")) {
1948
+ const cfg = n.traits.get("safe_daemon");
1949
+ n.traits.delete("safe_daemon");
1950
+ for (const [name, c] of this.expandSafeDaemon(cfg)) {
1951
+ if (!n.traits.has(name)) n.traits.set(name, c);
1952
+ }
1953
+ this.hasVRTraits = true;
1954
+ }
1955
+ if (n.traits && !(n.traits instanceof Map) && typeof n.traits === "object") {
1956
+ const rec = n.traits;
1957
+ if ("safe_daemon" in rec) {
1958
+ const cfg = rec["safe_daemon"];
1959
+ delete rec["safe_daemon"];
1960
+ for (const [name, c] of this.expandSafeDaemon(cfg)) {
1961
+ if (!(name in rec)) rec[name] = c;
1962
+ }
1963
+ }
1964
+ }
1965
+ if (Array.isArray(n.directives)) {
1966
+ const dirs = n.directives;
1967
+ const idx = dirs.findIndex((d) => d && d.type === "trait" && d.name === "safe_daemon");
1968
+ if (idx >= 0) {
1969
+ const cfg = dirs[idx].config;
1970
+ const expanded = this.expandSafeDaemon(cfg).map(([name, c]) => ({ type: "trait", name, config: c }));
1971
+ const existing = new Set(dirs.map((d) => d.name));
1972
+ dirs.splice(idx, 1, ...expanded.filter((e) => !existing.has(e.name)));
1973
+ }
1974
+ }
1975
+ for (const key of ["children", "body", "states"]) {
1976
+ const child = n[key];
1977
+ if (Array.isArray(child)) child.forEach(visit);
1978
+ }
1979
+ };
1980
+ visit(root);
1981
+ }
1844
1982
  /**
1845
1983
  * Performs an incremental parse using ChunkBasedIncrementalParser with
1846
1984
  * AST-aware chunking, hash-based caching, and dependency tracking.
@@ -2014,28 +2152,48 @@ var HoloScriptPlusParser = class {
2014
2152
  }
2015
2153
  const isNodeStart = this.check("IDENTIFIER") || this.check("STATE_MACHINE") || this.check("STATE") || this.check("TRANSITION") || this.check("INITIAL");
2016
2154
  if (isNodeStart) {
2017
- const node = this.parseNode();
2018
- const existingDirectives = node.directives || [];
2019
- node.directives = [...currentDirectives, ...existingDirectives];
2020
- if (node.type === "template") {
2021
- for (const d of currentDirectives) {
2022
- if (d.type === "version") {
2023
- node.version = d.version;
2024
- } else if (d.type === "migrate") {
2025
- if (!node.migrations) node.migrations = [];
2026
- node.migrations.push({
2027
- type: "Migration",
2028
- fromVersion: d.fromVersion,
2029
- body: d.body
2030
- });
2155
+ if (this.check("IDENTIFIER") && this.current().value === "connect") {
2156
+ const connectNode = this.parseHsConnectStatement();
2157
+ topLevelNodes.push(connectNode);
2158
+ } else if (this.check("IDENTIFIER") && this.current().value === "execute") {
2159
+ const executeNode = this.parseHsExecuteStatement();
2160
+ topLevelNodes.push(executeNode);
2161
+ } else if (this.check("IDENTIFIER") && isBrainKeyword(this.current().value)) {
2162
+ const brainNode = this.parseBrainDeclaration();
2163
+ brainNode.directives = [
2164
+ ...currentDirectives,
2165
+ ...brainNode.directives || []
2166
+ ];
2167
+ topLevelNodes.push(brainNode);
2168
+ } else {
2169
+ const node = this.parseNode();
2170
+ const existingDirectives = node.directives || [];
2171
+ node.directives = [...currentDirectives, ...existingDirectives];
2172
+ if (node.type === "template") {
2173
+ for (const d of currentDirectives) {
2174
+ if (d.type === "version") {
2175
+ node.version = d.version;
2176
+ } else if (d.type === "migrate") {
2177
+ if (!node.migrations) node.migrations = [];
2178
+ node.migrations.push({
2179
+ type: "Migration",
2180
+ fromVersion: d.fromVersion,
2181
+ body: d.body
2182
+ });
2183
+ }
2031
2184
  }
2032
2185
  }
2186
+ topLevelNodes.push(node);
2033
2187
  }
2034
- topLevelNodes.push(node);
2035
2188
  } else {
2036
2189
  if (currentDirectives.length > 0) {
2037
2190
  if (this.check("EOF")) {
2038
2191
  globalDirectives.push(...currentDirectives);
2192
+ } else if (this.check("LBRACE")) {
2193
+ const block = this.parseBlockContent();
2194
+ const last = currentDirectives[currentDirectives.length - 1];
2195
+ last.config = { ...last.config || {}, ...block };
2196
+ globalDirectives.push(...currentDirectives);
2039
2197
  } else {
2040
2198
  this.error(
2041
2199
  `Expected node after directives, got ${this.current().type}. Valid nodes: orb, template, logic, object, world, composition, scene, group`,
@@ -2051,21 +2209,6 @@ var HoloScriptPlusParser = class {
2051
2209
  throw new Error("ParseError");
2052
2210
  }
2053
2211
  }
2054
- if (this.check("IDENTIFIER") && this.current().value === "connect") {
2055
- const connection = this.parseConnectionStatement();
2056
- const node = {
2057
- type: "connection",
2058
- properties: connection,
2059
- directives: [],
2060
- children: [],
2061
- traits: /* @__PURE__ */ new Map(),
2062
- loc: {
2063
- start: { line: this.current().line, column: 0 },
2064
- end: { line: this.current().line, column: 0 }
2065
- }
2066
- };
2067
- topLevelNodes.push(node);
2068
- }
2069
2212
  } catch (e) {
2070
2213
  const errorMessage = e instanceof Error ? e.message : String(e);
2071
2214
  if (errorMessage !== "ParseError" && errorMessage !== "Unexpected token") {
@@ -2105,7 +2248,12 @@ var HoloScriptPlusParser = class {
2105
2248
  ]) || this.expect("IDENTIFIER", "Expected element type");
2106
2249
  const type = typeToken.value;
2107
2250
  if (type === "template") {
2108
- const templateName = this.expect("STRING", "Expected template name").value;
2251
+ let templateName;
2252
+ if (this.check("IDENTIFIER")) {
2253
+ templateName = this.advance().value;
2254
+ } else {
2255
+ templateName = this.expect("STRING", "Expected template name").value;
2256
+ }
2109
2257
  const templateBody = this.parseBlockContent();
2110
2258
  let version;
2111
2259
  const migrations = [];
@@ -2167,6 +2315,20 @@ var HoloScriptPlusParser = class {
2167
2315
  }
2168
2316
  };
2169
2317
  }
2318
+ if (type === "react") {
2319
+ return this.parseHsPlusReactBlock(startToken);
2320
+ }
2321
+ if (["transform", "filter", "branch", "validate"].includes(type)) {
2322
+ const savedPos = this.pos;
2323
+ let blockName = "anonymous";
2324
+ if (this.check("IDENTIFIER") || this.check("STRING")) {
2325
+ blockName = this.advance().value;
2326
+ }
2327
+ if (this.check("LBRACE")) {
2328
+ return this.parsePipelineBlock(type, blockName, startToken);
2329
+ }
2330
+ this.pos = savedPos;
2331
+ }
2170
2332
  if ([
2171
2333
  "module",
2172
2334
  "script",
@@ -2220,6 +2382,9 @@ var HoloScriptPlusParser = class {
2220
2382
  if (type === "assert" || startToken.type === "ASSERT") {
2221
2383
  return this.parseAssertNode();
2222
2384
  }
2385
+ if (type === "timeline") {
2386
+ return this.parseTimelineNode(startToken);
2387
+ }
2223
2388
  if (type === "environment") {
2224
2389
  const envBody = this.parseEnvironmentBlock();
2225
2390
  return {
@@ -2304,6 +2469,7 @@ var HoloScriptPlusParser = class {
2304
2469
  const children = [];
2305
2470
  const directives = [];
2306
2471
  const traits = /* @__PURE__ */ new Map();
2472
+ let ambiguousPreBodyTrait = null;
2307
2473
  if (templateRef) {
2308
2474
  properties.__templateRef = templateRef;
2309
2475
  }
@@ -2323,6 +2489,9 @@ var HoloScriptPlusParser = class {
2323
2489
  traits.set(directive.name, directive.config);
2324
2490
  this.hasVRTraits = true;
2325
2491
  directives.push(directive);
2492
+ if (this.blockConfigDirectives.has(directive) && this.traitConfigLooksLikeNodeBody(directive.config)) {
2493
+ ambiguousPreBodyTrait = directive;
2494
+ }
2326
2495
  } else {
2327
2496
  directives.push(directive);
2328
2497
  }
@@ -2373,6 +2542,13 @@ var HoloScriptPlusParser = class {
2373
2542
  this.synchronizeProperty();
2374
2543
  }
2375
2544
  }
2545
+ if (!this.check("LBRACE") && ambiguousPreBodyTrait) {
2546
+ this.errorAt(
2547
+ this.blockConfigDirectiveLocations.get(ambiguousPreBodyTrait) || startToken,
2548
+ `Trait @${ambiguousPreBodyTrait.name} used a block that looks like an object body, but no object body follows. Use @${ambiguousPreBodyTrait.name}(...) for trait config, or add a separate { ... } object body.`,
2549
+ "HSP101"
2550
+ );
2551
+ }
2376
2552
  if (this.check("LBRACE")) {
2377
2553
  this.advance();
2378
2554
  this.skipNewlines();
@@ -2417,7 +2593,7 @@ var HoloScriptPlusParser = class {
2417
2593
  properties[key] = spreadNode;
2418
2594
  } else {
2419
2595
  const token = this.current();
2420
- const isKeyToken = token.type === "IDENTIFIER" || token.type === "STRING" || token.type === "HASH" || token.type === "STATE" || token.type === "STATE_MACHINE" || token.type === "INITIAL" || token.type === "ON_ENTRY" || token.type === "ON_EXIT" || token.type === "TRANSITION";
2596
+ const isKeyToken = token.type === "IDENTIFIER" || token.type === "STRING" || token.type === "HASH" || token.type === "STATE" || token.type === "STATE_MACHINE" || token.type === "INITIAL" || token.type === "ON_ENTRY" || token.type === "ON_EXIT" || token.type === "ON_ERROR" || token.type === "TRANSITION";
2421
2597
  if (isKeyToken) {
2422
2598
  const saved = this.pos;
2423
2599
  const name = this.advance().value;
@@ -2449,7 +2625,9 @@ var HoloScriptPlusParser = class {
2449
2625
  "on_entry",
2450
2626
  "on_exit",
2451
2627
  "page",
2452
- "include"
2628
+ "include",
2629
+ "brain",
2630
+ "react"
2453
2631
  ];
2454
2632
  if (name === "transition" && this.check("STRING")) {
2455
2633
  const event = this.advance().value;
@@ -2528,7 +2706,12 @@ var HoloScriptPlusParser = class {
2528
2706
  }
2529
2707
  if (this.check("COLON") || this.check("EQUALS")) {
2530
2708
  this.advance();
2531
- properties[name] = this.parseValue();
2709
+ const pipeNext = this.peek(1);
2710
+ if (this.check("PIPE") && (!pipeNext || pipeNext.type === "NEWLINE" || pipeNext.type === "EOF")) {
2711
+ properties[name] = this.parseBlockScalar(this.tokens[saved]);
2712
+ } else {
2713
+ properties[name] = this.parseValue();
2714
+ }
2532
2715
  } else if (childNodeKeywords.includes(name) && (this.check("LBRACE") || this.check("STRING"))) {
2533
2716
  this.pos = saved;
2534
2717
  children.push(this.parseNode());
@@ -2585,15 +2768,24 @@ var HoloScriptPlusParser = class {
2585
2768
  const isKeyword = ["STATE_MACHINE", "STATE", "ON_ENTRY", "ON_EXIT", "TRANSITION"].includes(
2586
2769
  nameToken.type
2587
2770
  );
2588
- if (nameToken.type !== "IDENTIFIER" && !isKeyword) {
2771
+ let name;
2772
+ if (nameToken.type === "IDENTIFIER" || isKeyword) {
2773
+ this.advance();
2774
+ name = nameToken.value;
2775
+ } else if (nameToken.type === "NUMBER" && /^\d+[a-zA-Z]\w*$/.test(nameToken.value)) {
2776
+ this.advance();
2777
+ name = nameToken.value;
2778
+ const next = this.current();
2779
+ if (next.type === "IDENTIFIER" && next.line === nameToken.line && next.column === nameToken.column + nameToken.value.length) {
2780
+ name += this.advance().value;
2781
+ }
2782
+ } else {
2589
2783
  this.error(
2590
2784
  `Expected directive name, got ${nameToken.type}. Directives start with @ followed by name (e.g., @grabbable)`,
2591
2785
  "HSP201"
2592
2786
  );
2593
2787
  return null;
2594
2788
  }
2595
- this.advance();
2596
- const name = nameToken.value;
2597
2789
  if (name === "version") {
2598
2790
  this.expect("LPAREN", "Expected ( after @version");
2599
2791
  const versionToken = this.expect("NUMBER", "Expected version number");
@@ -2613,18 +2805,147 @@ var HoloScriptPlusParser = class {
2613
2805
  const body = this.check("LBRACE") ? this.parseCodeBlock() : "";
2614
2806
  return { type: "migrate", fromVersion, body };
2615
2807
  }
2808
+ if (name === "quest") {
2809
+ const config2 = this.check("LBRACE") ? this.parseBlockContent() : this.check("LPAREN") ? this.parseTraitConfig() : {};
2810
+ return { type: "quest", ...config2 };
2811
+ }
2812
+ if (name === "faction") {
2813
+ const config2 = this.check("LBRACE") ? this.parseBlockContent() : this.check("LPAREN") ? this.parseTraitConfig() : {};
2814
+ return { type: "faction", ...config2 };
2815
+ }
2816
+ if (name === "loot") {
2817
+ const config2 = this.check("LBRACE") ? this.parseBlockContent() : this.check("LPAREN") ? this.parseTraitConfig() : {};
2818
+ return { type: "loot", ...config2 };
2819
+ }
2820
+ if (name === "ability") {
2821
+ const config2 = this.check("LBRACE") ? this.parseBlockContent() : this.check("LPAREN") ? this.parseTraitConfig() : {};
2822
+ return { type: "ability", ...config2 };
2823
+ }
2824
+ if (name === "authority") {
2825
+ const config2 = this.check("LBRACE") ? this.parseBlockContent() : this.check("LPAREN") ? this.parseTraitConfig() : {};
2826
+ return { type: "authority", ...config2 };
2827
+ }
2828
+ if (name === "wallet_gated") {
2829
+ const config2 = this.check("LBRACE") ? this.parseBlockContent() : this.check("LPAREN") ? this.parseTraitConfig() : {};
2830
+ return { type: "wallet_gated", ...config2 };
2831
+ }
2832
+ if (name === "world_chunk") {
2833
+ const config2 = this.check("LBRACE") ? this.parseBlockContent() : this.check("LPAREN") ? this.parseTraitConfig() : {};
2834
+ return { type: "world_chunk", ...config2 };
2835
+ }
2836
+ if (name === "personality") {
2837
+ let value;
2838
+ if (this.check("IDENTIFIER")) {
2839
+ value = this.advance().value;
2840
+ } else if (this.check("STRING")) {
2841
+ value = this.advance().value;
2842
+ } else {
2843
+ value = "neutral";
2844
+ }
2845
+ return { type: "personality", value };
2846
+ }
2847
+ if (name === "faction_alignment") {
2848
+ let value;
2849
+ if (this.check("IDENTIFIER")) {
2850
+ value = this.advance().value;
2851
+ if (this.check("IDENTIFIER")) {
2852
+ value += "_" + this.advance().value;
2853
+ }
2854
+ } else if (this.check("STRING")) {
2855
+ value = this.advance().value;
2856
+ } else {
2857
+ value = "true_neutral";
2858
+ }
2859
+ return { type: "faction_alignment", value };
2860
+ }
2861
+ if (name === "memory_persistence") {
2862
+ let value = true;
2863
+ if (this.check("BOOLEAN")) {
2864
+ value = this.advance().value === "true";
2865
+ } else if (this.check("IDENTIFIER")) {
2866
+ const raw = this.advance().value;
2867
+ value = raw !== "false";
2868
+ }
2869
+ return { type: "memory_persistence", value };
2870
+ }
2871
+ if (name === "preferred_ability") {
2872
+ let abilityName;
2873
+ if (this.check("STRING")) {
2874
+ abilityName = this.advance().value;
2875
+ } else if (this.check("IDENTIFIER")) {
2876
+ abilityName = this.advance().value;
2877
+ } else {
2878
+ abilityName = "";
2879
+ }
2880
+ let when;
2881
+ if (this.check("AT")) {
2882
+ const saved = this.pos;
2883
+ this.advance();
2884
+ if (this.check("IDENTIFIER") && this.current().value === "when") {
2885
+ this.advance();
2886
+ if (this.check("LBRACE")) {
2887
+ when = this.parseCodeBlock();
2888
+ }
2889
+ } else {
2890
+ this.pos = saved;
2891
+ }
2892
+ }
2893
+ return { type: "preferred_ability", ability: abilityName, when };
2894
+ }
2895
+ if (name === "flee_threshold") {
2896
+ let value = 0.25;
2897
+ if (this.check("NUMBER")) {
2898
+ value = parseFloat(this.advance().value);
2899
+ }
2900
+ return { type: "flee_threshold", value };
2901
+ }
2902
+ if (name === "patrol_speed") {
2903
+ let value = 1;
2904
+ if (this.check("NUMBER")) {
2905
+ value = parseFloat(this.advance().value);
2906
+ } else if (this.check("IDENTIFIER")) {
2907
+ value = this.advance().value;
2908
+ }
2909
+ return { type: "patrol_speed", value };
2910
+ }
2911
+ if (name === "waypoints") {
2912
+ let points = [];
2913
+ if (this.check("LBRACKET")) {
2914
+ points = this.parseValue();
2915
+ } else if (this.check("LBRACE")) {
2916
+ points = this.parseBlockContent();
2917
+ }
2918
+ return { type: "waypoints", points };
2919
+ }
2616
2920
  if (VR_TRAITS.includes(name)) {
2617
2921
  if (!this.options.enableVRTraits) {
2618
2922
  this.warn(`VR trait @${name} is disabled`);
2619
2923
  return null;
2620
2924
  }
2621
2925
  let config2 = {};
2926
+ let configStyle2 = "none";
2622
2927
  if (this.check("LPAREN")) {
2928
+ configStyle2 = "paren";
2623
2929
  config2 = this.parseTraitConfig();
2624
2930
  } else if (this.check("LBRACE")) {
2931
+ configStyle2 = "block";
2625
2932
  config2 = this.parseBlockContent();
2933
+ } else if (this.check("COLON")) {
2934
+ configStyle2 = "colon";
2935
+ this.advance();
2936
+ if (this.check("LPAREN")) {
2937
+ config2 = this.parseTraitConfig();
2938
+ } else if (this.check("LBRACE")) {
2939
+ config2 = this.parseBlockContent();
2940
+ } else {
2941
+ config2 = { value: this.parseValue() };
2942
+ }
2943
+ }
2944
+ const directive2 = { type: "trait", name, config: config2 };
2945
+ if (configStyle2 === "block") {
2946
+ this.markBlockConfigDirective(directive2, nameToken);
2626
2947
  }
2627
- return this.parseTraitSumTail({ type: "trait", name, config: config2 });
2948
+ return this.parseTraitSumTail(directive2);
2628
2949
  }
2629
2950
  if (LIFECYCLE_HOOKS.includes(name)) {
2630
2951
  const params = [];
@@ -2731,15 +3052,22 @@ var HoloScriptPlusParser = class {
2731
3052
  this.warn("Expected 'from' after named import specifiers");
2732
3053
  }
2733
3054
  }
3055
+ let wildcardAlias;
2734
3056
  if (this.check("ASTERISK") || this.check("IDENTIFIER") && this.current().value === "*") {
2735
3057
  this.advance();
2736
3058
  isWildcard = true;
2737
3059
  if (this.check("IDENTIFIER") && this.current().value === "as") {
2738
3060
  this.advance();
3061
+ if (this.check("IDENTIFIER") && this.current().value !== "from") {
3062
+ wildcardAlias = this.advance().value;
3063
+ }
3064
+ }
3065
+ if (this.check("IDENTIFIER") && this.current().value === "from") {
3066
+ this.advance();
2739
3067
  }
2740
3068
  }
2741
3069
  const path = this.expect("STRING", "Expected import path string").value;
2742
- let alias = path.split("/").pop()?.replace(/\.[^.]+$/, "") || "import";
3070
+ let alias = wildcardAlias || path.split("/").pop()?.replace(/\.[^.]+$/, "") || "import";
2743
3071
  if (!namedImports && this.check("IDENTIFIER") && this.current().value === "as") {
2744
3072
  this.advance();
2745
3073
  alias = this.expect("IDENTIFIER", "Expected alias after as").value;
@@ -2949,9 +3277,12 @@ var HoloScriptPlusParser = class {
2949
3277
  this.warn(`Unknown directive @${name}`);
2950
3278
  }
2951
3279
  let config = {};
3280
+ let configStyle = "none";
2952
3281
  if (this.check("LPAREN")) {
3282
+ configStyle = "paren";
2953
3283
  config = this.parseTraitConfig();
2954
3284
  } else if (this.check("LBRACE")) {
3285
+ configStyle = "block";
2955
3286
  config = this.parseBlockContent();
2956
3287
  }
2957
3288
  if (this.check("COLON")) {
@@ -2962,7 +3293,32 @@ var HoloScriptPlusParser = class {
2962
3293
  config.body = this.parseCodeBlock();
2963
3294
  }
2964
3295
  }
2965
- return this.parseTraitSumTail({ type: "trait", name, config });
3296
+ const directive = { type: "trait", name, config };
3297
+ if (configStyle === "block") {
3298
+ this.markBlockConfigDirective(directive, nameToken);
3299
+ }
3300
+ return this.parseTraitSumTail(directive);
3301
+ }
3302
+ markBlockConfigDirective(directive, token) {
3303
+ this.blockConfigDirectives.add(directive);
3304
+ this.blockConfigDirectiveLocations.set(directive, token);
3305
+ }
3306
+ traitConfigLooksLikeNodeBody(config) {
3307
+ if (!config || typeof config !== "object") return false;
3308
+ const nodeBodyKeys = /* @__PURE__ */ new Set([
3309
+ "geometry",
3310
+ "position",
3311
+ "rotation",
3312
+ "scale",
3313
+ "size",
3314
+ "color",
3315
+ "material",
3316
+ "model",
3317
+ "mesh",
3318
+ "src",
3319
+ "url"
3320
+ ]);
3321
+ return Object.keys(config).some((key) => nodeBodyKeys.has(key));
2966
3322
  }
2967
3323
  parseTraitSumTail(first) {
2968
3324
  if (!this.check("PLUS")) {
@@ -3001,95 +3357,962 @@ var HoloScriptPlusParser = class {
3001
3357
  this.expect("RPAREN", "Expected )");
3002
3358
  return value;
3003
3359
  }
3004
- parseConnectionStatement() {
3005
- this.expect("IDENTIFIER", "Expected connect");
3006
- const from = this.expect("IDENTIFIER", "Expected from name").value;
3007
- this.expect("IDENTIFIER", "Expected to");
3008
- const to = this.expect("IDENTIFIER", "Expected to name").value;
3009
- return { from, to };
3010
- }
3360
+ // ===========================================================================
3361
+ // Brain Declaration Parsing
3362
+ // ===========================================================================
3011
3363
  /**
3012
- * Parse block content: { key: value, ... }
3364
+ * Parse a top-level `brain` declaration.
3365
+ *
3366
+ * Grammar:
3367
+ * brain <Name> [ : @<brainType> ] {
3368
+ * @personality <value>
3369
+ * @faction_alignment <value>
3370
+ * @memory_persistence true|false
3371
+ * @preferred_ability "<Name>" [ @when { <expr> } ]
3372
+ * @flee_threshold <number>
3373
+ * @patrol_speed <number|identifier>
3374
+ * @waypoints [...]
3375
+ * state <stateName> {
3376
+ * transition to <target> [ @when { <expr> } ]
3377
+ * <action statements ...>
3378
+ * }
3379
+ * }
3013
3380
  */
3014
- parseBlockContent() {
3015
- const content = {};
3381
+ parseBrainDeclaration() {
3382
+ this.advance();
3383
+ const name = this.check("IDENTIFIER") ? this.advance().value : "unnamed_brain";
3384
+ let brainType = "behavior_tree";
3385
+ if (this.check("COLON")) {
3386
+ this.advance();
3387
+ if (this.check("AT")) {
3388
+ this.advance();
3389
+ if (this.check("IDENTIFIER")) {
3390
+ const raw = this.advance().value;
3391
+ if (raw === "behavior_tree" || raw === "decision_tree" || raw === "neural" || raw === "scripted") {
3392
+ brainType = raw;
3393
+ }
3394
+ }
3395
+ }
3396
+ }
3397
+ const brain = {
3398
+ type: "brain",
3399
+ name,
3400
+ brainType,
3401
+ states: [],
3402
+ traits: {}
3403
+ };
3016
3404
  if (!this.check("LBRACE")) {
3017
- return content;
3405
+ this.warn(`brain "${name}" missing body block { }`);
3406
+ return brain;
3018
3407
  }
3019
3408
  this.advance();
3020
3409
  this.skipNewlines();
3021
3410
  while (!this.check("RBRACE") && !this.check("EOF")) {
3022
3411
  this.skipNewlines();
3023
3412
  if (this.check("RBRACE") || this.check("EOF")) break;
3024
- this.skipNewlines();
3025
- if (this.check("RBRACE") || this.check("EOF")) break;
3026
- const token = this.current();
3027
- const isKeyToken = token.type === "IDENTIFIER" || token.type === "STRING" || token.type === "STATE" || token.type === "STATE_MACHINE" || token.type === "INITIAL" || token.type === "ON_ENTRY" || token.type === "ON_EXIT" || token.type === "TRANSITION";
3028
- if (isKeyToken) {
3029
- const next = this.peek(1);
3030
- if (next.type === "COLON" || next.type === "EQUALS") {
3031
- const key = this.advance().value;
3032
- this.advance();
3033
- content[key] = this.parseValue();
3034
- } else if (next.type === "STRING" || next.type === "LBRACE" || next.type === "IDENTIFIER") {
3035
- const node = this.parseNode();
3036
- const type = node.type;
3037
- const name = node.name || `unnamed_${type}_${Object.keys(content).length}`;
3038
- content[name] = node;
3039
- } else {
3040
- const key = this.advance().value;
3041
- content[key] = true;
3042
- }
3043
- } else if (this.check("SPREAD")) {
3413
+ if (this.check("AT")) {
3044
3414
  this.advance();
3045
- const val = this.parseValue();
3046
- const spreadKey = `__spread_${Object.keys(content).length}`;
3047
- content[spreadKey] = { type: "spread", argument: val };
3048
- } else if (this.check("AT")) {
3049
- const directive = this.parseDirective();
3050
- if (directive) {
3051
- const dirKey = ("name" in directive ? directive.name : void 0) || directive.type;
3052
- content[`@${dirKey}`] = directive;
3415
+ if (!this.check("IDENTIFIER")) {
3416
+ this.warn("Expected directive name after @");
3417
+ continue;
3053
3418
  }
3054
- } else if (this.check("LBRACE")) {
3055
- let depth = 1;
3419
+ const dirName = this.advance().value;
3420
+ if (dirName === "personality") {
3421
+ const val = this.check("IDENTIFIER") ? this.advance().value : this.check("STRING") ? this.advance().value : "neutral";
3422
+ brain.personality = val;
3423
+ } else if (dirName === "faction_alignment") {
3424
+ let val = this.check("IDENTIFIER") ? this.advance().value : this.check("STRING") ? this.advance().value : "true_neutral";
3425
+ if (this.check("IDENTIFIER")) val += "_" + this.advance().value;
3426
+ brain.factionAlignment = val;
3427
+ } else if (dirName === "memory_persistence") {
3428
+ const raw = this.check("BOOLEAN") ? this.advance().value : this.check("IDENTIFIER") ? this.advance().value : "true";
3429
+ brain.memoryPersistence = raw !== "false";
3430
+ } else if (dirName === "preferred_ability") {
3431
+ const abilityName = this.check("STRING") ? this.advance().value : this.check("IDENTIFIER") ? this.advance().value : "";
3432
+ let when;
3433
+ if (this.check("AT")) {
3434
+ const saved = this.pos;
3435
+ this.advance();
3436
+ if (this.check("IDENTIFIER") && this.current().value === "when") {
3437
+ this.advance();
3438
+ if (this.check("LBRACE")) when = this.parseCodeBlock();
3439
+ } else {
3440
+ this.pos = saved;
3441
+ }
3442
+ }
3443
+ brain.preferredAbility = { name: abilityName, when };
3444
+ } else if (dirName === "flee_threshold") {
3445
+ brain.fleeThreshold = this.check("NUMBER") ? parseFloat(this.advance().value) : 0.25;
3446
+ } else if (dirName === "patrol_speed") {
3447
+ brain.patrolSpeed = this.check("NUMBER") ? parseFloat(this.advance().value) : this.check("IDENTIFIER") ? this.advance().value : 1;
3448
+ } else if (dirName === "waypoints") {
3449
+ brain.waypoints = this.check("LBRACKET") ? this.parseValue() : [];
3450
+ } else if (dirName === "goal") {
3451
+ const cfg = this.check("LBRACE") ? this.parseBlockContent() : {};
3452
+ if (!brain.goals) brain.goals = [];
3453
+ brain.goals.push({
3454
+ name: typeof cfg.name === "string" ? cfg.name : String(cfg.name ?? `goal_${brain.goals.length}`),
3455
+ desiredState: cfg.desiredState && typeof cfg.desiredState === "object" ? cfg.desiredState : void 0,
3456
+ priority: typeof cfg.priority === "number" ? cfg.priority : void 0
3457
+ });
3458
+ } else if (dirName === "escalation") {
3459
+ const cfg = this.check("LBRACE") ? this.parseBlockContent() : {};
3460
+ if (!brain.escalations) brain.escalations = [];
3461
+ brain.escalations.push({
3462
+ on: typeof cfg.on === "string" ? cfg.on : String(cfg.on ?? ""),
3463
+ action: typeof cfg.action === "string" ? cfg.action : "notify"
3464
+ });
3465
+ } else if (dirName === "provider_policy") {
3466
+ const cfg = this.check("LBRACE") ? this.parseBlockContent() : {};
3467
+ brain.providerPolicy = {
3468
+ prefer: typeof cfg.prefer === "string" ? cfg.prefer : void 0,
3469
+ fallback: typeof cfg.fallback === "string" ? cfg.fallback : void 0,
3470
+ requires: typeof cfg.requires === "string" ? cfg.requires : void 0
3471
+ };
3472
+ } else if (dirName === "frame_declaration") {
3473
+ const cfg = this.check("LBRACE") ? this.parseBlockContent() : {};
3474
+ brain.frameDeclaration = coerceConfig(cfg);
3475
+ } else if (dirName === "behavior_tree") {
3476
+ const config = this.check("LBRACE") ? this.parseBlockContent() : {};
3477
+ brain.traits["behavior_tree"] = config;
3478
+ } else {
3479
+ const config = this.check("LBRACE") ? this.parseBlockContent() : this.check("LPAREN") ? this.parseTraitConfig() : {};
3480
+ brain.traits[dirName] = config;
3481
+ }
3482
+ this.skipNewlines();
3483
+ continue;
3484
+ }
3485
+ if (this.check("STATE") || this.check("IDENTIFIER") && this.current().value === "state") {
3056
3486
  this.advance();
3057
- while (depth > 0 && !this.check("EOF")) {
3058
- if (this.check("LBRACE")) depth++;
3059
- if (this.check("RBRACE")) depth--;
3487
+ const stateName = this.check("IDENTIFIER") ? this.advance().value : "unnamed";
3488
+ const brainState = {
3489
+ name: stateName,
3490
+ transitions: [],
3491
+ actions: [],
3492
+ traits: {}
3493
+ };
3494
+ if (this.check("LBRACE")) {
3060
3495
  this.advance();
3496
+ this.skipNewlines();
3497
+ while (!this.check("RBRACE") && !this.check("EOF")) {
3498
+ this.skipNewlines();
3499
+ if (this.check("RBRACE") || this.check("EOF")) break;
3500
+ if (this.check("TRANSITION") || this.check("IDENTIFIER") && this.current().value === "transition") {
3501
+ this.advance();
3502
+ let to = "";
3503
+ if (this.check("IDENTIFIER") && this.current().value === "to") {
3504
+ this.advance();
3505
+ }
3506
+ if (this.check("IDENTIFIER")) to = this.advance().value;
3507
+ else if (this.check("STRING")) to = this.advance().value;
3508
+ let when;
3509
+ if (this.check("AT")) {
3510
+ const saved = this.pos;
3511
+ this.advance();
3512
+ if (this.check("IDENTIFIER") && this.current().value === "when") {
3513
+ this.advance();
3514
+ if (this.check("LBRACE")) when = this.parseCodeBlock();
3515
+ } else {
3516
+ this.pos = saved;
3517
+ }
3518
+ }
3519
+ brainState.transitions.push({ to, when });
3520
+ } else if (this.check("AT")) {
3521
+ this.advance();
3522
+ const innerDir = this.check("IDENTIFIER") ? this.advance().value : "";
3523
+ const config = this.check("LBRACE") ? this.parseBlockContent() : this.check("LPAREN") ? this.parseTraitConfig() : {};
3524
+ brainState.traits[innerDir] = config;
3525
+ } else if (this.check("IDENTIFIER") && isCognitiveVerb(this.current().value)) {
3526
+ const saved = this.pos;
3527
+ const verb = this.advance().value;
3528
+ if (this.check("LBRACE")) {
3529
+ const config = this.parseBlockContent();
3530
+ if (!brainState.cognitiveActions) brainState.cognitiveActions = [];
3531
+ brainState.cognitiveActions.push({ kind: "cognitive", verb, config });
3532
+ } else {
3533
+ this.pos = saved;
3534
+ const parts = [this.advance().value];
3535
+ while (!this.check("NEWLINE") && !this.check("EOF") && !this.check("RBRACE")) {
3536
+ parts.push(this.advance().value);
3537
+ }
3538
+ brainState.actions.push(parts.join(" "));
3539
+ }
3540
+ } else if (this.check("IDENTIFIER") && this.tokens[this.pos + 1]?.type === "LBRACE" && nearestCognitiveVerb(this.current().value)) {
3541
+ const typo = this.current().value;
3542
+ const suggestion = nearestCognitiveVerb(typo);
3543
+ this.warn(`Unknown cognitive verb '${typo}' \u2014 did you mean '${suggestion}'?`);
3544
+ this.advance();
3545
+ this.parseBlockContent();
3546
+ brainState.actions.push(typo);
3547
+ } else if (this.check("IDENTIFIER")) {
3548
+ const parts = [this.advance().value];
3549
+ while (!this.check("NEWLINE") && !this.check("EOF") && !this.check("RBRACE")) {
3550
+ parts.push(this.advance().value);
3551
+ }
3552
+ brainState.actions.push(parts.join(" "));
3553
+ } else {
3554
+ this.advance();
3555
+ }
3556
+ this.skipNewlines();
3557
+ }
3558
+ this.expect("RBRACE", "Expected } to close state block");
3061
3559
  }
3062
- } else if (this.check("LPAREN")) {
3063
- this.skipParens();
3560
+ brain.states.push(brainState);
3561
+ this.skipNewlines();
3562
+ continue;
3563
+ }
3564
+ if (this.check("IDENTIFIER")) {
3565
+ const parts = [this.advance().value];
3566
+ while (!this.check("NEWLINE") && !this.check("EOF") && !this.check("RBRACE")) {
3567
+ parts.push(this.advance().value);
3568
+ }
3569
+ brain.traits["_actions"] = [
3570
+ ...brain.traits["_actions"] || [],
3571
+ parts.join(" ")
3572
+ ];
3064
3573
  } else {
3065
3574
  this.advance();
3066
3575
  }
3067
- if (this.check("COMMA")) this.advance();
3068
3576
  this.skipNewlines();
3069
3577
  }
3070
- this.expect("RBRACE", "Expected }");
3071
- return content;
3578
+ this.expect("RBRACE", "Expected } to close brain declaration");
3579
+ return brain;
3072
3580
  }
3073
3581
  /**
3074
- * Parse bindings block: { bind(expr) -> target, ... }
3582
+ * Parse a .hs `connect` statement.
3583
+ * Syntax (full .hs format):
3584
+ * connect <source> -> <target>
3585
+ * where <source> and <target> are dotted member chains, optionally followed
3586
+ * by assignment for state connections:
3587
+ * connect alarm_bell.alarm_triggered -> guard_captain.state.alert_level = 3
3588
+ *
3589
+ * Strategy: consume the `connect` keyword, then collect all tokens up to
3590
+ * (but not including) the next NEWLINE / EOF as a raw string. This is
3591
+ * intentionally permissive — the connect statement has a rich surface syntax
3592
+ * (dotted paths, ARROW, assignments) that the generic property parser cannot
3593
+ * handle. Semantic validation of the wiring is a separate compilation phase.
3075
3594
  */
3076
- parseBindingsBlock() {
3077
- const bindings = [];
3078
- if (!this.check("LBRACE")) {
3079
- return bindings;
3595
+ parseHsConnectStatement() {
3596
+ const startToken = this.current();
3597
+ this.advance();
3598
+ const toks = [];
3599
+ while (!this.check("NEWLINE") && !this.check("EOF")) {
3600
+ toks.push(this.advance());
3080
3601
  }
3602
+ const arrowIdx = toks.findIndex((t) => t.type === "ARROW");
3603
+ const fromToks = arrowIdx >= 0 ? toks.slice(0, arrowIdx) : toks;
3604
+ const toToks = arrowIdx >= 0 ? toks.slice(arrowIdx + 1) : [];
3605
+ return {
3606
+ type: "connection",
3607
+ properties: {
3608
+ from: this.joinTokensSmart(fromToks),
3609
+ to: this.joinTokensSmart(toToks),
3610
+ raw: this.joinTokensSmart(toks)
3611
+ },
3612
+ directives: [],
3613
+ children: [],
3614
+ traits: /* @__PURE__ */ new Map(),
3615
+ loc: {
3616
+ start: { line: startToken.line, column: startToken.column },
3617
+ end: { line: this.current().line, column: this.current().column }
3618
+ }
3619
+ };
3620
+ }
3621
+ /**
3622
+ * Parse a .hs `execute` statement.
3623
+ * Syntax:
3624
+ * execute <target>(<args>) [repeat forever | every <interval>]
3625
+ * Examples:
3626
+ * execute guard_captain.patrol() repeat forever
3627
+ * execute temp_sensor_A.read() every 1000ms
3628
+ *
3629
+ * Strategy: consume `execute` then collect all tokens to end of line.
3630
+ * The target is the leading member chain plus one balanced call-paren
3631
+ * group; everything after it is the schedule clause.
3632
+ */
3633
+ parseHsExecuteStatement() {
3634
+ const startToken = this.current();
3081
3635
  this.advance();
3082
- this.skipNewlines();
3083
- while (!this.check("RBRACE") && !this.check("EOF")) {
3084
- this.skipNewlines();
3085
- if (this.check("RBRACE") || this.check("EOF")) break;
3086
- if (this.check("IDENTIFIER") && this.current().value === "bind") {
3087
- this.advance();
3088
- if (this.check("LPAREN")) {
3089
- this.advance();
3090
- let source = "";
3091
- let parenDepth = 1;
3092
- while (parenDepth > 0 && !this.check("EOF")) {
3636
+ const toks = [];
3637
+ while (!this.check("NEWLINE") && !this.check("EOF")) {
3638
+ toks.push(this.advance());
3639
+ }
3640
+ let end = toks.length;
3641
+ let depth = 0;
3642
+ for (let i = 0; i < toks.length; i++) {
3643
+ const t = toks[i];
3644
+ if (t.type === "LPAREN") {
3645
+ depth++;
3646
+ } else if (t.type === "RPAREN") {
3647
+ depth--;
3648
+ if (depth === 0) {
3649
+ end = i + 1;
3650
+ break;
3651
+ }
3652
+ } else if (depth === 0 && t.type !== "IDENTIFIER" && t.type !== "DOT" && t.type !== "NUMBER") {
3653
+ end = i;
3654
+ break;
3655
+ }
3656
+ }
3657
+ const target = this.joinTokensSmart(toks.slice(0, end));
3658
+ const schedule = this.joinTokensSmart(toks.slice(end));
3659
+ return {
3660
+ type: "execute",
3661
+ properties: { target, schedule, raw: this.joinTokensSmart(toks) },
3662
+ directives: [],
3663
+ children: [],
3664
+ traits: /* @__PURE__ */ new Map(),
3665
+ loc: {
3666
+ start: { line: startToken.line, column: startToken.column },
3667
+ end: { line: this.current().line, column: this.current().column }
3668
+ }
3669
+ };
3670
+ }
3671
+ // ===========================================================================
3672
+ // Pipeline DSL block parsing (transform / filter / branch / validate)
3673
+ // ===========================================================================
3674
+ /**
3675
+ * Reassemble token values into readable source text: no spaces around
3676
+ * member/call/index punctuation, string literals re-quoted.
3677
+ */
3678
+ joinTokensSmart(tokens) {
3679
+ const NO_SPACE_BEFORE = /* @__PURE__ */ new Set([
3680
+ "DOT",
3681
+ "COMMA",
3682
+ "LPAREN",
3683
+ "RPAREN",
3684
+ "LBRACKET",
3685
+ "RBRACKET",
3686
+ "OPTIONAL_DOT"
3687
+ ]);
3688
+ const NO_SPACE_AFTER = /* @__PURE__ */ new Set(["DOT", "LPAREN", "LBRACKET", "OPTIONAL_DOT", "EXCLAMATION"]);
3689
+ let out = "";
3690
+ let prev = null;
3691
+ for (const t of tokens) {
3692
+ const text = t.type === "STRING" ? JSON.stringify(t.value) : t.value;
3693
+ if (prev && !NO_SPACE_BEFORE.has(t.type) && !NO_SPACE_AFTER.has(prev.type)) {
3694
+ out += " ";
3695
+ }
3696
+ out += text;
3697
+ prev = t;
3698
+ }
3699
+ return out;
3700
+ }
3701
+ /** Capture a dotted/indexed path: sku, a.b.c, results[0].id, entries[] */
3702
+ capturePipelinePath() {
3703
+ const toks = [];
3704
+ if (this.check("IDENTIFIER") || this.check("STRING") || this.check("UNDERSCORE")) {
3705
+ toks.push(this.advance());
3706
+ }
3707
+ for (; ; ) {
3708
+ if (this.check("DOT")) {
3709
+ toks.push(this.advance());
3710
+ if (this.check("IDENTIFIER") || this.check("NUMBER") || this.check("UNDERSCORE")) {
3711
+ toks.push(this.advance());
3712
+ }
3713
+ continue;
3714
+ }
3715
+ if (this.check("LBRACKET")) {
3716
+ let depth = 0;
3717
+ do {
3718
+ const t = this.advance();
3719
+ toks.push(t);
3720
+ if (t.type === "LBRACKET") depth++;
3721
+ else if (t.type === "RBRACKET") depth--;
3722
+ } while (depth > 0 && !this.check("EOF"));
3723
+ continue;
3724
+ }
3725
+ break;
3726
+ }
3727
+ return { raw: this.joinTokensSmart(toks), tokens: toks };
3728
+ }
3729
+ /** Capture a transform op call: trim() / multiply(100) / increment_if(a == b.c) */
3730
+ capturePipelineOp() {
3731
+ const toks = [];
3732
+ if (this.check("IDENTIFIER")) {
3733
+ toks.push(this.advance());
3734
+ }
3735
+ if (this.check("LPAREN")) {
3736
+ let depth = 0;
3737
+ do {
3738
+ const t = this.advance();
3739
+ toks.push(t);
3740
+ if (t.type === "LPAREN") depth++;
3741
+ else if (t.type === "RPAREN") depth--;
3742
+ } while (depth > 0 && !this.check("EOF"));
3743
+ }
3744
+ return this.joinTokensSmart(toks);
3745
+ }
3746
+ /** True when an ARROW appears before the end of the current statement line. */
3747
+ hasArrowBeforeEOL() {
3748
+ for (let look = this.pos; look < this.tokens.length; look++) {
3749
+ const t = this.tokens[look].type;
3750
+ if (t === "ARROW") return true;
3751
+ if (t === "NEWLINE" || t === "EOF" || t === "RBRACE" || t === "LBRACE") return false;
3752
+ }
3753
+ return false;
3754
+ }
3755
+ /**
3756
+ * Capture expression tokens to end of line, honoring `||` / `&&`
3757
+ * line continuations:
3758
+ * where: stock != previous.stock
3759
+ * || costCents != previous.costCents
3760
+ */
3761
+ captureExpressionToEOL() {
3762
+ const toks = [];
3763
+ for (; ; ) {
3764
+ while (!this.check("NEWLINE") && !this.check("EOF") && !this.check("RBRACE")) {
3765
+ toks.push(this.advance());
3766
+ }
3767
+ if (this.check("NEWLINE")) {
3768
+ let look = this.pos;
3769
+ while (look < this.tokens.length && ["NEWLINE", "INDENT", "DEDENT"].includes(this.tokens[look].type)) {
3770
+ look++;
3771
+ }
3772
+ const next = this.tokens[look];
3773
+ if (next && (next.type === "OR" || next.type === "AND")) {
3774
+ this.pos = look;
3775
+ continue;
3776
+ }
3777
+ }
3778
+ break;
3779
+ }
3780
+ return toks;
3781
+ }
3782
+ /**
3783
+ * YAML-style block scalar property value:
3784
+ * prompt: |
3785
+ * Extract wisdom from this content.
3786
+ * Return JSON.
3787
+ * Captures the following lines from raw source while they are blank or
3788
+ * indented deeper than the key line, dedents, and skips their tokens.
3789
+ */
3790
+ parseBlockScalar(keyToken) {
3791
+ this.advance();
3792
+ const keyIndent = keyToken.column - 1;
3793
+ const lines = this.source.split("\n");
3794
+ const collected = [];
3795
+ let lastLine = keyToken.line;
3796
+ for (let ln = keyToken.line + 1; ln <= lines.length; ln++) {
3797
+ const text = lines[ln - 1];
3798
+ if (text.trim() === "") {
3799
+ collected.push("");
3800
+ lastLine = ln;
3801
+ continue;
3802
+ }
3803
+ const indent = text.length - text.trimStart().length;
3804
+ if (indent <= keyIndent) break;
3805
+ collected.push(text);
3806
+ lastLine = ln;
3807
+ }
3808
+ while (!this.check("EOF") && this.current().line <= lastLine) {
3809
+ this.advance();
3810
+ }
3811
+ const nonBlank = collected.filter((l) => l.trim() !== "");
3812
+ const minIndent = nonBlank.length ? Math.min(...nonBlank.map((l) => l.length - l.trimStart().length)) : 0;
3813
+ return collected.map((l) => l.slice(minIndent)).join("\n").replace(/\s+$/, "");
3814
+ }
3815
+ /** Property value inside a pipeline DSL block. */
3816
+ parsePipelinePropertyValue(keyToken) {
3817
+ if (this.check("PIPE")) {
3818
+ const next = this.peek(1);
3819
+ if (!next || next.type === "NEWLINE" || next.type === "EOF") {
3820
+ return this.parseBlockScalar(keyToken);
3821
+ }
3822
+ }
3823
+ if (this.check("LBRACE") || this.check("LBRACKET")) {
3824
+ return this.parseValue();
3825
+ }
3826
+ const toks = this.captureExpressionToEOL();
3827
+ if (toks.length === 1) {
3828
+ const t = toks[0];
3829
+ if (t.type === "STRING") return t.value;
3830
+ if (t.type === "NUMBER") {
3831
+ const n = Number(t.value);
3832
+ return Number.isNaN(n) ? t.value : n;
3833
+ }
3834
+ if (t.value === "true") return true;
3835
+ if (t.value === "false") return false;
3836
+ return t.value;
3837
+ }
3838
+ return this.joinTokensSmart(toks);
3839
+ }
3840
+ /**
3841
+ * Body parser for pipeline DSL blocks. Handles, per statement:
3842
+ * key: value → property (incl. block scalars)
3843
+ * src.path[0] -> dst.path : op() : op2(...) → mapping child
3844
+ * when <cond> -> <target> → when child (branch)
3845
+ * default -> <target> → default child (branch)
3846
+ * field.path : rule, rule(...) → constraint child (validate /
3847
+ * dotted fields anywhere)
3848
+ * Statements are captured structurally with their raw text preserved;
3849
+ * semantic validation of the wiring is a separate compilation phase.
3850
+ */
3851
+ parsePipelineBlock(kind, name, startToken) {
3852
+ this.expect("LBRACE", `Expected { after ${kind} ${name}`);
3853
+ const properties = {};
3854
+ const children = [];
3855
+ const directives = [];
3856
+ const traits = /* @__PURE__ */ new Map();
3857
+ const childLoc = (tok) => ({
3858
+ start: { line: tok.line, column: tok.column },
3859
+ end: { line: this.current().line, column: this.current().column }
3860
+ });
3861
+ while (!this.check("RBRACE") && !this.check("EOF")) {
3862
+ try {
3863
+ this.skipNewlines();
3864
+ if (this.check("RBRACE") || this.check("EOF")) break;
3865
+ if (this.check("AT")) {
3866
+ const directive = this.parseDirective();
3867
+ if (directive) {
3868
+ if (directive.type === "trait") {
3869
+ traits.set(directive.name, directive.config);
3870
+ this.hasVRTraits = true;
3871
+ }
3872
+ directives.push(directive);
3873
+ }
3874
+ } else if (kind === "branch" && this.check("IDENTIFIER") && this.current().value === "when") {
3875
+ const stmtStart = this.advance();
3876
+ const condToks = [];
3877
+ while (!this.check("ARROW") && !this.check("NEWLINE") && !this.check("EOF")) {
3878
+ condToks.push(this.advance());
3879
+ }
3880
+ let target = "";
3881
+ if (this.check("ARROW")) {
3882
+ this.advance();
3883
+ const targetToks = [];
3884
+ while (!this.check("NEWLINE") && !this.check("EOF") && !this.check("RBRACE")) {
3885
+ targetToks.push(this.advance());
3886
+ }
3887
+ target = this.joinTokensSmart(targetToks);
3888
+ }
3889
+ children.push({
3890
+ type: "when",
3891
+ condition: this.joinTokensSmart(condToks),
3892
+ target,
3893
+ loc: childLoc(stmtStart)
3894
+ });
3895
+ } else if (this.hasArrowBeforeEOL()) {
3896
+ const stmtStart = this.current();
3897
+ const lhsToks = [];
3898
+ while (!this.check("ARROW") && !this.check("NEWLINE") && !this.check("EOF")) {
3899
+ lhsToks.push(this.advance());
3900
+ }
3901
+ if (this.check("ARROW")) this.advance();
3902
+ const from = this.joinTokensSmart(lhsToks);
3903
+ if (kind === "branch") {
3904
+ const targetToks = [];
3905
+ while (!this.check("NEWLINE") && !this.check("EOF") && !this.check("RBRACE")) {
3906
+ targetToks.push(this.advance());
3907
+ }
3908
+ children.push({
3909
+ type: from === "default" ? "default" : "route",
3910
+ from,
3911
+ target: this.joinTokensSmart(targetToks),
3912
+ loc: childLoc(stmtStart)
3913
+ });
3914
+ } else {
3915
+ const to = this.capturePipelinePath();
3916
+ const ops = [];
3917
+ while (this.check("COLON")) {
3918
+ this.advance();
3919
+ ops.push(this.capturePipelineOp());
3920
+ }
3921
+ children.push({
3922
+ type: "mapping",
3923
+ from,
3924
+ to: to.raw,
3925
+ ops,
3926
+ loc: childLoc(stmtStart)
3927
+ });
3928
+ }
3929
+ } else if (this.check("IDENTIFIER") || this.check("STRING")) {
3930
+ const keyToken = this.current();
3931
+ const path = this.capturePipelinePath();
3932
+ if (this.check("COLON")) {
3933
+ this.advance();
3934
+ const pathIsDotted = path.tokens.some((t) => t.type === "DOT" || t.type === "LBRACKET");
3935
+ if (kind === "validate" || pathIsDotted) {
3936
+ const ruleToks = this.captureExpressionToEOL();
3937
+ children.push({
3938
+ type: "constraint",
3939
+ field: path.raw,
3940
+ rules: this.joinTokensSmart(ruleToks),
3941
+ loc: childLoc(keyToken)
3942
+ });
3943
+ } else {
3944
+ properties[path.raw] = this.parsePipelinePropertyValue(keyToken);
3945
+ }
3946
+ } else {
3947
+ properties[path.raw] = true;
3948
+ }
3949
+ } else if (this.check("COMMA")) {
3950
+ this.advance();
3951
+ } else {
3952
+ this.error(
3953
+ `Unexpected token ${this.current().type} "${this.current().value}" in ${kind} block`,
3954
+ "HSP001"
3955
+ );
3956
+ this.synchronizeProperty();
3957
+ }
3958
+ this.skipNewlines();
3959
+ } catch (e) {
3960
+ const errorMessage = e instanceof Error ? e.message : String(e);
3961
+ if (errorMessage !== "ParseError") console.error(e);
3962
+ this.synchronizeProperty();
3963
+ }
3964
+ }
3965
+ this.expect("RBRACE", "Expected }");
3966
+ return {
3967
+ type: kind,
3968
+ name,
3969
+ id: name,
3970
+ properties,
3971
+ directives,
3972
+ children,
3973
+ traits,
3974
+ loc: {
3975
+ start: { line: startToken.line, column: startToken.column },
3976
+ end: { line: this.current().line, column: this.current().column }
3977
+ }
3978
+ };
3979
+ }
3980
+ /** @deprecated Use parseHsConnectStatement for .hs connect syntax */
3981
+ parseConnectionStatement() {
3982
+ this.expect("IDENTIFIER", "Expected connect");
3983
+ const from = this.expect("IDENTIFIER", "Expected from name").value;
3984
+ this.expect("IDENTIFIER", "Expected to");
3985
+ const to = this.expect("IDENTIFIER", "Expected to name").value;
3986
+ return { from, to };
3987
+ }
3988
+ /**
3989
+ * Map a reaction trigger to its routing bucket (mirrors the .hs parser's
3990
+ * reactionCategory; the movement-trigger list comes from the locomotion SSOT).
3991
+ */
3992
+ hsplusReactionCategory(trigger) {
3993
+ if (isLocomotionReactionTrigger(trigger)) return "movement";
3994
+ switch (trigger) {
3995
+ case "on_interact":
3996
+ case "on_grab":
3997
+ case "on_release":
3998
+ case "on_use":
3999
+ case "on_hover":
4000
+ return "interaction";
4001
+ case "on_collision":
4002
+ case "on_collide":
4003
+ case "on_proximity":
4004
+ return "collision";
4005
+ case "on_spawn":
4006
+ case "on_death":
4007
+ case "on_enter":
4008
+ case "on_exit":
4009
+ case "on_tick":
4010
+ return "lifecycle";
4011
+ case "on_combat":
4012
+ case "on_cast":
4013
+ return "combat";
4014
+ case "on_signal":
4015
+ case "on_input":
4016
+ return "signal";
4017
+ default:
4018
+ return "custom";
4019
+ }
4020
+ }
4021
+ /**
4022
+ * Parse a declarative reaction block attached to an entity:
4023
+ * react {
4024
+ * on_grab(hand) => highlight()
4025
+ * on_proximity(player) => alert(player)
4026
+ * on_use => { open(); play_sound("creak") }
4027
+ * }
4028
+ * A leading `on` word is optional (`on collide(...)` == `on_collide(...)`).
4029
+ * Each entry becomes a `reaction` child node carrying trigger/params/body and
4030
+ * an inferred ReactionCategory. Body is captured raw (an expression or block).
4031
+ */
4032
+ parseHsPlusReactBlock(startToken) {
4033
+ const startLoc = { line: startToken.line, column: startToken.column };
4034
+ const reactions = [];
4035
+ this.skipNewlines();
4036
+ if (this.check("LBRACE")) {
4037
+ this.advance();
4038
+ this.skipNewlines();
4039
+ while (!this.check("RBRACE") && !this.check("EOF")) {
4040
+ this.skipNewlines();
4041
+ if (this.check("RBRACE") || this.check("EOF")) break;
4042
+ let triggerPrefix = "";
4043
+ if (this.check("IDENTIFIER") && this.current().value === "on" && this.tokens[this.pos + 1]?.type === "IDENTIFIER") {
4044
+ this.advance();
4045
+ triggerPrefix = "on_";
4046
+ }
4047
+ if (!this.check("IDENTIFIER")) {
4048
+ this.advance();
4049
+ continue;
4050
+ }
4051
+ const trigger = triggerPrefix + this.advance().value;
4052
+ const params = [];
4053
+ if (this.check("LPAREN")) {
4054
+ this.advance();
4055
+ while (!this.check("RPAREN") && !this.check("EOF")) {
4056
+ if (this.check("IDENTIFIER")) params.push(this.advance().value);
4057
+ else this.advance();
4058
+ if (this.check("COMMA")) this.advance();
4059
+ }
4060
+ if (this.check("RPAREN")) this.advance();
4061
+ }
4062
+ if (this.check("ARROW")) this.advance();
4063
+ let body = "";
4064
+ if (this.check("LBRACE")) {
4065
+ body = this.parseRawBlock();
4066
+ } else {
4067
+ const parts = [];
4068
+ while (!this.check("NEWLINE") && !this.check("EOF") && !this.check("RBRACE")) {
4069
+ parts.push(this.advance().value);
4070
+ }
4071
+ body = parts.join(" ").trim();
4072
+ }
4073
+ reactions.push({
4074
+ type: "reaction",
4075
+ name: trigger,
4076
+ event: trigger,
4077
+ body,
4078
+ properties: {
4079
+ trigger,
4080
+ params,
4081
+ category: this.hsplusReactionCategory(trigger)
4082
+ }
4083
+ });
4084
+ this.skipNewlines();
4085
+ }
4086
+ if (this.check("RBRACE")) this.advance();
4087
+ }
4088
+ return {
4089
+ type: "react-block",
4090
+ name: "react",
4091
+ children: reactions,
4092
+ properties: { count: reactions.length },
4093
+ directives: [],
4094
+ traits: /* @__PURE__ */ new Map(),
4095
+ loc: {
4096
+ start: startLoc,
4097
+ end: { line: this.current().line, column: this.current().column }
4098
+ }
4099
+ };
4100
+ }
4101
+ /**
4102
+ * Parse a `timeline <name> { …properties, track-blocks, children }` node.
4103
+ * (Theatre.js harvest S1.) Produces the same shape as the canonical Rust
4104
+ * grammar: a `timeline` node whose `children` include structured `track`
4105
+ * nodes (`{ type: 'track', target, keyframes: [...] }`). Non-track lines are
4106
+ * handled the generic way: `key: value` → property, nested block keyword →
4107
+ * child node. The `timeline` keyword has already been consumed by the caller.
4108
+ */
4109
+ parseTimelineNode(startToken) {
4110
+ let name;
4111
+ if (this.check("STRING") || this.check("IDENTIFIER")) {
4112
+ name = this.advance().value;
4113
+ }
4114
+ const properties = {};
4115
+ const children = [];
4116
+ const directives = [];
4117
+ const traits = /* @__PURE__ */ new Map();
4118
+ if (this.check("LBRACE")) {
4119
+ this.advance();
4120
+ this.skipNewlines();
4121
+ while (!this.check("RBRACE") && !this.check("EOF")) {
4122
+ this.skipNewlines();
4123
+ if (this.check("RBRACE") || this.check("EOF")) break;
4124
+ if (this.check("AT")) {
4125
+ const directive = this.parseDirective();
4126
+ if (directive) {
4127
+ if (directive.type === "trait") {
4128
+ traits.set(directive.name, directive.config);
4129
+ this.hasVRTraits = true;
4130
+ }
4131
+ directives.push(directive);
4132
+ }
4133
+ this.skipNewlines();
4134
+ continue;
4135
+ }
4136
+ const token = this.current();
4137
+ const next = this.peek(1);
4138
+ if (token.type === "IDENTIFIER" && token.value === "track" && (next.type === "STRING" || next.type === "IDENTIFIER") && this.peek(2).type === "LBRACE") {
4139
+ this.advance();
4140
+ children.push(this.parseTrackNode(token));
4141
+ if (this.check("COMMA")) this.advance();
4142
+ this.skipNewlines();
4143
+ continue;
4144
+ }
4145
+ if ((token.type === "IDENTIFIER" || token.type === "STRING") && (next.type === "COLON" || next.type === "EQUALS")) {
4146
+ const key = this.advance().value;
4147
+ this.advance();
4148
+ properties[key] = this.parseValue();
4149
+ if (this.check("COMMA")) this.advance();
4150
+ this.skipNewlines();
4151
+ continue;
4152
+ }
4153
+ if (token.type === "IDENTIFIER" || token.type === "STRING") {
4154
+ children.push(this.parseNode());
4155
+ if (this.check("COMMA")) this.advance();
4156
+ this.skipNewlines();
4157
+ continue;
4158
+ }
4159
+ this.advance();
4160
+ this.skipNewlines();
4161
+ }
4162
+ this.expect("RBRACE", "Expected } to close timeline block");
4163
+ }
4164
+ return {
4165
+ type: "timeline",
4166
+ name,
4167
+ id: name,
4168
+ properties,
4169
+ directives,
4170
+ children,
4171
+ traits,
4172
+ loc: {
4173
+ start: { line: startToken.line, column: startToken.column },
4174
+ end: { line: this.current().line, column: this.current().column }
4175
+ }
4176
+ };
4177
+ }
4178
+ /**
4179
+ * Parse a keyframe-track inside a timeline:
4180
+ * track "<target>" { key <time> { <value> } [easing <ease>] ; … }
4181
+ * Mirrors the Rust `TrackNode`/`KeyframeNode`. `;` is skipped by the lexer,
4182
+ * so keyframe separators are implicit. The `track` keyword has already been
4183
+ * consumed by the caller. Returns `{ type: 'track', target, keyframes }`.
4184
+ */
4185
+ parseTrackNode(startToken) {
4186
+ const target = this.check("STRING") ? this.advance().value : this.expect("IDENTIFIER", "Expected track target name").value;
4187
+ const keyframes = [];
4188
+ this.expect("LBRACE", "Expected { to open track block");
4189
+ this.skipNewlines();
4190
+ while (!this.check("RBRACE") && !this.check("EOF")) {
4191
+ this.skipNewlines();
4192
+ if (this.check("RBRACE") || this.check("EOF")) break;
4193
+ if (this.check("COMMA")) {
4194
+ this.advance();
4195
+ continue;
4196
+ }
4197
+ if (this.check("IDENTIFIER") && this.current().value === "key") {
4198
+ this.advance();
4199
+ let timeSign = 1;
4200
+ if (this.check("MINUS")) {
4201
+ this.advance();
4202
+ timeSign = -1;
4203
+ }
4204
+ const timeTok = this.expect("NUMBER", "Expected numeric time after key");
4205
+ const time = timeSign * Number(timeTok.value);
4206
+ this.expect("LBRACE", "Expected { value block after key time");
4207
+ const value = this.parseValue();
4208
+ this.expect("RBRACE", "Expected } to close key value block");
4209
+ let easing;
4210
+ if (this.check("IDENTIFIER") && this.current().value === "easing") {
4211
+ this.advance();
4212
+ if (this.check("IDENTIFIER") || this.check("STRING")) {
4213
+ easing = this.advance().value;
4214
+ }
4215
+ }
4216
+ keyframes.push(easing === void 0 ? { time, value } : { time, value, easing });
4217
+ this.skipNewlines();
4218
+ continue;
4219
+ }
4220
+ this.advance();
4221
+ this.skipNewlines();
4222
+ }
4223
+ this.expect("RBRACE", "Expected } to close track block");
4224
+ return {
4225
+ type: "track",
4226
+ target,
4227
+ keyframes,
4228
+ loc: {
4229
+ start: { line: startToken.line, column: startToken.column },
4230
+ end: { line: this.current().line, column: this.current().column }
4231
+ }
4232
+ };
4233
+ }
4234
+ /**
4235
+ * Parse block content: { key: value, ... }
4236
+ */
4237
+ parseBlockContent() {
4238
+ const content = {};
4239
+ if (!this.check("LBRACE")) {
4240
+ return content;
4241
+ }
4242
+ this.advance();
4243
+ this.skipNewlines();
4244
+ while (!this.check("RBRACE") && !this.check("EOF")) {
4245
+ this.skipNewlines();
4246
+ if (this.check("RBRACE") || this.check("EOF")) break;
4247
+ this.skipNewlines();
4248
+ if (this.check("RBRACE") || this.check("EOF")) break;
4249
+ const token = this.current();
4250
+ const isKeyToken = token.type === "IDENTIFIER" || token.type === "STRING" || token.type === "STATE" || token.type === "STATE_MACHINE" || token.type === "INITIAL" || token.type === "ON_ENTRY" || token.type === "ON_EXIT" || token.type === "TRANSITION";
4251
+ if (isKeyToken) {
4252
+ const next = this.peek(1);
4253
+ if (next.type === "COLON" || next.type === "EQUALS") {
4254
+ const key = this.advance().value;
4255
+ this.advance();
4256
+ content[key] = this.parseValue();
4257
+ } else if (next.type === "STRING" || next.type === "LBRACE" || next.type === "IDENTIFIER") {
4258
+ const node = this.parseNode();
4259
+ const type = node.type;
4260
+ const name = node.name || `unnamed_${type}_${Object.keys(content).length}`;
4261
+ content[name] = node;
4262
+ } else {
4263
+ const key = this.advance().value;
4264
+ content[key] = true;
4265
+ }
4266
+ } else if (this.check("SPREAD")) {
4267
+ this.advance();
4268
+ const val = this.parseValue();
4269
+ const spreadKey = `__spread_${Object.keys(content).length}`;
4270
+ content[spreadKey] = { type: "spread", argument: val };
4271
+ } else if (this.check("AT")) {
4272
+ const directive = this.parseDirective();
4273
+ if (directive) {
4274
+ const dirKey = ("name" in directive ? directive.name : void 0) || directive.type;
4275
+ content[`@${dirKey}`] = directive;
4276
+ }
4277
+ } else if (this.check("LBRACE")) {
4278
+ let depth = 1;
4279
+ this.advance();
4280
+ while (depth > 0 && !this.check("EOF")) {
4281
+ if (this.check("LBRACE")) depth++;
4282
+ if (this.check("RBRACE")) depth--;
4283
+ this.advance();
4284
+ }
4285
+ } else if (this.check("LPAREN")) {
4286
+ this.skipParens();
4287
+ } else {
4288
+ this.advance();
4289
+ }
4290
+ if (this.check("COMMA")) this.advance();
4291
+ this.skipNewlines();
4292
+ }
4293
+ this.expect("RBRACE", "Expected }");
4294
+ return content;
4295
+ }
4296
+ /**
4297
+ * Parse bindings block: { bind(expr) -> target, ... }
4298
+ */
4299
+ parseBindingsBlock() {
4300
+ const bindings = [];
4301
+ if (!this.check("LBRACE")) {
4302
+ return bindings;
4303
+ }
4304
+ this.advance();
4305
+ this.skipNewlines();
4306
+ while (!this.check("RBRACE") && !this.check("EOF")) {
4307
+ this.skipNewlines();
4308
+ if (this.check("RBRACE") || this.check("EOF")) break;
4309
+ if (this.check("IDENTIFIER") && this.current().value === "bind") {
4310
+ this.advance();
4311
+ if (this.check("LPAREN")) {
4312
+ this.advance();
4313
+ let source = "";
4314
+ let parenDepth = 1;
4315
+ while (parenDepth > 0 && !this.check("EOF")) {
3093
4316
  if (this.check("LPAREN")) parenDepth++;
3094
4317
  if (this.check("RPAREN")) {
3095
4318
  parenDepth--;
@@ -3194,7 +4417,8 @@ var HoloScriptPlusParser = class {
3194
4417
  "camera",
3195
4418
  "timeline",
3196
4419
  "page",
3197
- "include"
4420
+ "include",
4421
+ "react"
3198
4422
  ];
3199
4423
  while (!this.check("RBRACE") && !this.check("EOF")) {
3200
4424
  const currentDirectives = [];
@@ -3713,7 +4937,9 @@ var HoloScriptPlusParser = class {
3713
4937
  if (startToken.type === "STATE_MACHINE") {
3714
4938
  this.advance();
3715
4939
  }
3716
- const name = this.expect("IDENTIFIER", "Expected state machine name").value;
4940
+ const name = this.parseStateMachineIdentifier("Expected state machine name");
4941
+ const inputs = [];
4942
+ const listeners = [];
3717
4943
  const states = [];
3718
4944
  const transitions = [];
3719
4945
  let initialState = "";
@@ -3726,7 +4952,13 @@ var HoloScriptPlusParser = class {
3726
4952
  if (current.type === "INITIAL" || current.value === "initial") {
3727
4953
  this.advance();
3728
4954
  this.expect("COLON", "Expected : after initial");
3729
- initialState = this.expect("STRING", "Expected initial state name").value;
4955
+ initialState = this.parseStateMachineIdentifier("Expected initial state name");
4956
+ } else if (current.value === "input") {
4957
+ inputs.push(this.parseStateMachineInputDeclaration());
4958
+ } else if (current.value === "listen") {
4959
+ listeners.push(this.parseStateMachineListenerDeclaration());
4960
+ } else if (this.isStateMachineTransitionShorthandStart()) {
4961
+ transitions.push(this.parseStateMachineTransitionShorthand());
3730
4962
  } else if (current.type === "STATE" || current.value === "state") {
3731
4963
  states.push(this.parseStateNode());
3732
4964
  } else if (current.type === "TRANSITION" || current.value === "transitions") {
@@ -3741,6 +4973,8 @@ var HoloScriptPlusParser = class {
3741
4973
  type: "state-machine",
3742
4974
  name,
3743
4975
  initialState,
4976
+ inputs,
4977
+ listeners,
3744
4978
  states,
3745
4979
  transitions,
3746
4980
  loc: {
@@ -3751,9 +4985,10 @@ var HoloScriptPlusParser = class {
3751
4985
  }
3752
4986
  parseStateNode() {
3753
4987
  this.advance();
3754
- const name = this.expect("IDENTIFIER", "Expected state name").value;
4988
+ const name = this.parseStateMachineIdentifier("Expected state name");
3755
4989
  let onEntry;
3756
4990
  let onExit;
4991
+ const metadata = {};
3757
4992
  this.expect("LBRACE", "Expected { after state name");
3758
4993
  this.skipNewlines();
3759
4994
  while (!this.check("RBRACE") && !this.check("EOF")) {
@@ -3766,19 +5001,39 @@ var HoloScriptPlusParser = class {
3766
5001
  } else if (current.type === "ON_EXIT" || current.value === "on_exit") {
3767
5002
  this.advance();
3768
5003
  onExit = this.parseCodeBlock();
5004
+ } else if (this.isStateMachineStateMetadataKey(current.value) && this.peek(1).type === "COLON") {
5005
+ const key = this.advance().value;
5006
+ this.expect("COLON", `Expected : after ${key}`);
5007
+ metadata[key] = this.parseValue();
3769
5008
  } else {
3770
5009
  this.advance();
3771
5010
  }
3772
5011
  this.skipNewlines();
3773
5012
  }
3774
5013
  this.expect("RBRACE", "Expected }");
3775
- return { name, onEntry, onExit };
5014
+ return { name, onEntry, onExit, ...metadata };
5015
+ }
5016
+ isStateMachineStateMetadataKey(key) {
5017
+ return key === "clip" || key === "clips" || key === "parameter" || key === "parameters" || key === "thresholds" || key === "blendType" || key === "blend_type" || key === "blend" || key === "blendMode" || key === "blend_mode" || key === "mask" || key === "baseline";
3776
5018
  }
3777
5019
  parseOnErrorNode() {
3778
5020
  const startToken = this.previous();
5021
+ const params = [];
5022
+ if (this.check("LPAREN")) {
5023
+ this.advance();
5024
+ while (!this.check("RPAREN") && !this.check("EOF") && !this.check("LBRACE")) {
5025
+ if (this.check("IDENTIFIER")) {
5026
+ params.push(this.advance().value);
5027
+ } else {
5028
+ this.advance();
5029
+ }
5030
+ }
5031
+ if (this.check("RPAREN")) this.advance();
5032
+ }
3779
5033
  const body = this.parseControlFlowBody();
3780
5034
  return {
3781
5035
  type: "on_error",
5036
+ params,
3782
5037
  body,
3783
5038
  loc: {
3784
5039
  start: { line: startToken.line, column: startToken.column },
@@ -3813,17 +5068,153 @@ var HoloScriptPlusParser = class {
3813
5068
  while (!this.check("RBRACE") && !this.check("EOF")) {
3814
5069
  this.skipNewlines();
3815
5070
  if (this.check("RBRACE") || this.check("EOF")) break;
3816
- const from = this.expect("IDENTIFIER", "Expected source state").value;
3817
- this.expect("ARROW", "Expected ->");
3818
- const to = this.expect("IDENTIFIER", "Expected target state").value;
3819
- this.expect("COLON", "Expected :");
3820
- const event = this.expect("IDENTIFIER", "Expected event name").value;
3821
- transitions.push({ from, to, event });
5071
+ if (this.isStateMachineTransitionShorthandStart()) {
5072
+ transitions.push(this.parseStateMachineTransitionShorthand());
5073
+ } else {
5074
+ const from = this.parseStateMachineIdentifier("Expected source state");
5075
+ this.expect("ARROW", "Expected ->");
5076
+ const to = this.parseStateMachineIdentifier("Expected target state");
5077
+ this.expect("COLON", "Expected :");
5078
+ const event = this.parseStateMachineIdentifier("Expected event name");
5079
+ transitions.push({ from, to, event });
5080
+ }
3822
5081
  this.skipNewlines();
3823
5082
  }
3824
5083
  this.expect("RBRACE", "Expected }");
3825
5084
  return transitions;
3826
5085
  }
5086
+ parseStateMachineInputDeclaration() {
5087
+ this.advance();
5088
+ const name = this.parseStateMachineIdentifier("Expected input name");
5089
+ this.expect("COLON", "Expected : after input name");
5090
+ const rawType = this.parseStateMachineIdentifier("Expected input type");
5091
+ const inputType = rawType === "number" ? "float" : rawType === "boolean" ? "bool" : rawType;
5092
+ let defaultValue;
5093
+ if (this.check("EQUALS")) {
5094
+ this.advance();
5095
+ defaultValue = this.parseValue();
5096
+ }
5097
+ return {
5098
+ type: "animation-input",
5099
+ name,
5100
+ inputType,
5101
+ rawType,
5102
+ default: defaultValue
5103
+ };
5104
+ }
5105
+ parseStateMachineListenerDeclaration() {
5106
+ this.advance();
5107
+ const event = this.parseStateMachineDottedIdentifier("Expected listener event");
5108
+ this.expect("ARROW", "Expected -> after listener event");
5109
+ if (this.current().value === "fire") {
5110
+ this.advance();
5111
+ return {
5112
+ type: "animation-listener",
5113
+ event,
5114
+ action: "fire",
5115
+ parameter: this.parseStateMachineDottedIdentifier("Expected trigger input")
5116
+ };
5117
+ }
5118
+ if (this.current().value === "reset") {
5119
+ this.advance();
5120
+ return {
5121
+ type: "animation-listener",
5122
+ event,
5123
+ action: "reset",
5124
+ parameter: this.parseStateMachineDottedIdentifier("Expected trigger input")
5125
+ };
5126
+ }
5127
+ const parameter = this.parseStateMachineDottedIdentifier("Expected animation input");
5128
+ this.expect("EQUALS", "Expected = in listener binding");
5129
+ const parsedValue = this.parseValue();
5130
+ const listener = {
5131
+ type: "animation-listener",
5132
+ event,
5133
+ action: "set",
5134
+ parameter
5135
+ };
5136
+ if (parsedValue && typeof parsedValue === "object" && "__ref" in parsedValue) {
5137
+ const ref = parsedValue.__ref;
5138
+ if (typeof ref === "string" && (ref === "event" || ref.startsWith("event."))) {
5139
+ listener.source = ref;
5140
+ } else if (typeof ref === "string") {
5141
+ listener.value = ref;
5142
+ } else {
5143
+ listener.value = String(ref ?? "");
5144
+ }
5145
+ } else if (typeof parsedValue === "number" || typeof parsedValue === "boolean" || typeof parsedValue === "string") {
5146
+ listener.value = parsedValue;
5147
+ } else {
5148
+ listener.value = String(parsedValue ?? "");
5149
+ }
5150
+ return listener;
5151
+ }
5152
+ isStateMachineTransitionShorthandStart() {
5153
+ return this.isStateMachineIdentifierToken(0) && this.peek(1).type === "ARROW";
5154
+ }
5155
+ parseStateMachineTransitionShorthand() {
5156
+ const fromRaw = this.parseStateMachineIdentifier("Expected source state");
5157
+ const from = fromRaw.toLowerCase() === "any" ? "any" : fromRaw;
5158
+ this.expect("ARROW", "Expected ->");
5159
+ const to = this.parseStateMachineIdentifier("Expected target state");
5160
+ const transition = { from, to };
5161
+ while (!this.check("NEWLINE") && !this.check("RBRACE") && !this.check("COMMA") && !this.check("EOF")) {
5162
+ if (!this.isStateMachineIdentifierToken(0)) break;
5163
+ const clause = this.current().value.toLowerCase();
5164
+ this.advance();
5165
+ if (clause === "when") {
5166
+ transition.when = this.parseExpression();
5167
+ } else if (clause === "on") {
5168
+ transition.event = this.parseStateMachineIdentifier("Expected trigger input");
5169
+ } else if (clause === "over" || clause === "duration") {
5170
+ transition.duration = this.parseValue();
5171
+ } else if (clause === "easing") {
5172
+ transition.easing = this.parseStateMachineIdentifier("Expected easing name");
5173
+ } else if (clause === "exittime" || clause === "exit_time") {
5174
+ transition.exitTime = this.parseValue();
5175
+ transition.hasExitTime = true;
5176
+ } else if (clause === "pausewhenexiting" || clause === "pause_when_exiting") {
5177
+ transition.pauseWhenExiting = this.parseOptionalStateMachineBoolean();
5178
+ } else if (clause === "priority") {
5179
+ transition.priority = this.parseValue();
5180
+ } else if (clause === "cantransitiontoself" || clause === "can_transition_to_self") {
5181
+ transition.canTransitionToSelf = this.parseOptionalStateMachineBoolean();
5182
+ } else {
5183
+ break;
5184
+ }
5185
+ }
5186
+ return transition;
5187
+ }
5188
+ parseOptionalStateMachineBoolean() {
5189
+ if (this.check("COLON")) this.advance();
5190
+ if (this.check("BOOLEAN")) return this.advance().value === "true";
5191
+ if (this.check("IDENTIFIER")) {
5192
+ const value = this.current().value.toLowerCase();
5193
+ if (value === "true" || value === "false") {
5194
+ this.advance();
5195
+ return value === "true";
5196
+ }
5197
+ }
5198
+ return true;
5199
+ }
5200
+ parseStateMachineIdentifier(message) {
5201
+ if (this.check("IDENTIFIER") || this.check("STRING")) {
5202
+ return this.advance().value;
5203
+ }
5204
+ return this.expect("IDENTIFIER", message).value;
5205
+ }
5206
+ parseStateMachineDottedIdentifier(message) {
5207
+ let value = this.parseStateMachineIdentifier(message);
5208
+ while (this.check("DOT")) {
5209
+ this.advance();
5210
+ value += `.${this.parseStateMachineIdentifier("Expected property name after dot")}`;
5211
+ }
5212
+ return value;
5213
+ }
5214
+ isStateMachineIdentifierToken(offset) {
5215
+ const token = this.peek(offset);
5216
+ return token.type === "IDENTIFIER" || token.type === "STRING";
5217
+ }
3827
5218
  parseControlFlowBody() {
3828
5219
  const nodes = [];
3829
5220
  if (this.check("LBRACE")) {
@@ -3867,41 +5258,10 @@ var HoloScriptPlusParser = class {
3867
5258
  return nodes;
3868
5259
  }
3869
5260
  parseCodeBlock() {
3870
- let code = "";
3871
- let braceDepth = 0;
3872
- if (this.check("LBRACE")) {
3873
- this.advance();
3874
- braceDepth = 1;
3875
- try {
3876
- while (braceDepth > 0 && !this.check("EOF")) {
3877
- const token = this.advance();
3878
- if (token.type === "LBRACE") {
3879
- braceDepth++;
3880
- code += "{";
3881
- } else if (token.type === "RBRACE") {
3882
- braceDepth--;
3883
- if (braceDepth > 0) {
3884
- code += "}";
3885
- }
3886
- } else if (token.type === "STRING") {
3887
- code += `"${token.value}"`;
3888
- code += " ";
3889
- } else {
3890
- code += token.value;
3891
- if (token.type === "NEWLINE") {
3892
- code += "\n";
3893
- } else {
3894
- code += " ";
3895
- }
3896
- }
3897
- }
3898
- } catch (e) {
3899
- const errorMessage = e instanceof Error ? e.message : String(e);
3900
- if (errorMessage !== "ParseError") console.error(e);
3901
- this.synchronize();
3902
- }
5261
+ if (!this.check("LBRACE")) {
5262
+ return "";
3903
5263
  }
3904
- return code.trim();
5264
+ return this.parseRawBlock();
3905
5265
  }
3906
5266
  parseInlineExpression() {
3907
5267
  let expr = "";
@@ -4153,6 +5513,9 @@ var HoloScriptPlusParser = class {
4153
5513
  }
4154
5514
  return this.parseParenExpression();
4155
5515
  }
5516
+ if (token.type === "ARROW" && token.value === "->") {
5517
+ return this.parseTransitionShorthand();
5518
+ }
4156
5519
  if (token.type === "MATCH") {
4157
5520
  return this.parseMatchExpression();
4158
5521
  }
@@ -4208,6 +5571,36 @@ var HoloScriptPlusParser = class {
4208
5571
  err.message = "ParseError";
4209
5572
  throw err;
4210
5573
  }
5574
+ /**
5575
+ * Parse a state-machine transition shorthand value:
5576
+ * -> "target"
5577
+ * -> "target" action(callback_name)
5578
+ * -> "target" guard(state.credits >= 10) action(begin)
5579
+ * Used as the value of on_<event> keys inside @state_machine states config.
5580
+ * Modifiers (action / guard) may appear in any order, each at most once
5581
+ * meaningfully (last one wins, matching general object semantics).
5582
+ */
5583
+ parseTransitionShorthand() {
5584
+ this.advance();
5585
+ let target = "";
5586
+ if (this.check("STRING") || this.check("IDENTIFIER")) {
5587
+ target = this.advance().value;
5588
+ } else {
5589
+ this.error(
5590
+ `Expected target state after -> in transition. Got ${this.current().type} "${this.current().value}"`,
5591
+ "HSP300"
5592
+ );
5593
+ }
5594
+ const transition = { type: "transition", target };
5595
+ while (this.check("IDENTIFIER") && (this.current().value === "action" || this.current().value === "guard") && this.peek(1).type === "LPAREN") {
5596
+ const kind = this.advance().value;
5597
+ this.advance();
5598
+ const arg = this.parseExpression();
5599
+ this.expect("RPAREN", `Expected ) after ${kind} argument in transition`);
5600
+ transition[kind] = arg;
5601
+ }
5602
+ return transition;
5603
+ }
4211
5604
  parseParenExpression() {
4212
5605
  this.expect("LPAREN", "Expected (");
4213
5606
  const items = [];
@@ -4592,6 +5985,14 @@ var HoloScriptPlusParser = class {
4592
5985
  })
4593
5986
  );
4594
5987
  }
5988
+ errorAt(token, message, code = "HSP001") {
5989
+ this.errors.push(
5990
+ createRichError(code, message, token.line, token.column, {
5991
+ source: this.source,
5992
+ severity: "error"
5993
+ })
5994
+ );
5995
+ }
4595
5996
  errorWithSuggestion(message, suggestion, code = "HSP001") {
4596
5997
  const token = this.current();
4597
5998
  this.errors.push(
@@ -4846,5 +6247,5 @@ function parse(source, options) {
4846
6247
  }
4847
6248
 
4848
6249
  export { ChunkBasedIncrementalParser, HSPLUS_ERROR_CODES, HoloScriptPlusParser, ParseCache, createKeywordError, createParser, createRichError, createTraitError, findSimilarKeyword, findSimilarTrait, formatRichError, formatRichErrors, getErrorCodeDocumentation, getSourceContext, globalParseCache, parse, parseIncrementalChunks };
4849
- //# sourceMappingURL=chunk-FNHJRI32.js.map
4850
- //# sourceMappingURL=chunk-FNHJRI32.js.map
6250
+ //# sourceMappingURL=chunk-DBH3D2MU.js.map
6251
+ //# sourceMappingURL=chunk-DBH3D2MU.js.map