@buoy-gg/shared-ui 7.0.35 → 7.0.38

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 (332) hide show
  1. package/README.md +33 -26
  2. package/floating.d.ts +1 -0
  3. package/floating.js +2 -0
  4. package/lib/commonjs/JsModal.js +16 -1
  5. package/lib/commonjs/JsModal.web.js +16 -1
  6. package/lib/commonjs/dataViewer/CyberpunkInput.js +6 -1
  7. package/lib/commonjs/dataViewer/LiveExplorer.js +28 -16
  8. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  9. package/lib/commonjs/icons/ReactQueryIcon.js +9 -161
  10. package/lib/commonjs/icons/SentryBugIcon.js +9 -171
  11. package/lib/commonjs/icons/index.js +7 -0
  12. package/lib/commonjs/index.js +105 -6
  13. package/lib/commonjs/license/AccountSetup.js +56 -0
  14. package/lib/commonjs/license/AccountSetup.web.js +81 -0
  15. package/lib/commonjs/license/FeatureGate.js +10 -43
  16. package/lib/commonjs/license/FreeAccountPrompt.js +35 -12
  17. package/lib/commonjs/license/index.js +0 -6
  18. package/lib/commonjs/license/licenseModule.js +56 -36
  19. package/lib/commonjs/license/tierLimits.js +12 -63
  20. package/lib/commonjs/storage/devToolsStorageKeys.js +8 -1
  21. package/lib/commonjs/stores/BaseEventStore.js +21 -5
  22. package/lib/commonjs/stores/createToolStore.js +21 -3
  23. package/lib/commonjs/stores/floatingToolStore.js +71 -0
  24. package/lib/commonjs/stores/floatingToolStore.test.js +112 -0
  25. package/lib/commonjs/sync/keyedArrays.js +209 -0
  26. package/lib/commonjs/sync/toolActionRegistry.js +85 -2
  27. package/lib/commonjs/sync/toolAttention.test.js +51 -0
  28. package/lib/commonjs/sync/toolUiBridge.js +58 -0
  29. package/lib/commonjs/sync/typedEdit.js +228 -0
  30. package/lib/commonjs/sync/valuePath.js +337 -0
  31. package/lib/commonjs/sync/valueShape.js +161 -0
  32. package/lib/commonjs/ui/backgrounds/README.md +52 -0
  33. package/lib/commonjs/ui/backgrounds/ToolBackground.js +4 -2
  34. package/lib/commonjs/ui/backgrounds/index.js +8 -1
  35. package/lib/commonjs/ui/backgrounds/registry.js +8 -0
  36. package/lib/commonjs/ui/backgrounds/variants/wormhole/motion.js +48 -0
  37. package/lib/commonjs/ui/backgrounds/variants/wormhole/textures.js +3 -0
  38. package/lib/commonjs/ui/backgrounds/variants/wormhole.js +133 -0
  39. package/lib/commonjs/ui/components/ClipboardHintBanner.js +5 -6
  40. package/lib/commonjs/ui/components/CompactRow.js +94 -74
  41. package/lib/commonjs/ui/components/DraggableHeader.js +16 -1
  42. package/lib/commonjs/ui/components/ExpandablePopover.js +38 -6
  43. package/lib/commonjs/ui/components/HeaderSearchButton.js +17 -4
  44. package/lib/commonjs/ui/floating/FloatingTool.js +1010 -0
  45. package/lib/commonjs/ui/floating/FloatingTool.web.js +591 -0
  46. package/lib/commonjs/ui/floating/FloatingToolHost.js +32 -0
  47. package/lib/commonjs/ui/floating/FloatingToolHost.web.js +28 -0
  48. package/lib/commonjs/ui/floating/index.js +105 -0
  49. package/lib/commonjs/ui/floating/index.web.js +105 -0
  50. package/lib/commonjs/ui/floating/onboardingConstants.js +70 -0
  51. package/lib/commonjs/ui/floating/onboardingConstants.web.js +34 -0
  52. package/lib/commonjs/ui/gameUI/constants/gameUIColors.js +3 -1
  53. package/lib/commonjs/ui/index.js +118 -2
  54. package/lib/commonjs/ui/night/nightTheme.js +10 -0
  55. package/lib/commonjs/ui/night/primitives.js +22 -5
  56. package/lib/commonjs/ui/otto/OttoSceneView.js +69 -0
  57. package/lib/commonjs/ui/otto/index.js +22 -0
  58. package/lib/commonjs/ui/otto/ottoEngine.js +10 -7
  59. package/lib/commonjs/ui/otto/ottoScenes.js +949 -0
  60. package/lib/commonjs/utils/minimalSuperJSON.js +13 -3
  61. package/lib/module/JsModal.js +16 -1
  62. package/lib/module/JsModal.web.js +16 -1
  63. package/lib/module/dataViewer/CyberpunkInput.js +6 -1
  64. package/lib/module/dataViewer/LiveExplorer.js +28 -16
  65. package/lib/module/hooks/safe-area-impl.js +1 -1
  66. package/lib/module/icons/ReactQueryIcon.js +9 -161
  67. package/lib/module/icons/SentryBugIcon.js +9 -171
  68. package/lib/module/icons/index.js +3 -1
  69. package/lib/module/index.js +8 -1
  70. package/lib/module/license/AccountSetup.js +52 -0
  71. package/lib/module/license/AccountSetup.web.js +77 -0
  72. package/lib/module/license/FeatureGate.js +10 -43
  73. package/lib/module/license/FreeAccountPrompt.js +35 -12
  74. package/lib/module/license/index.js +1 -1
  75. package/lib/module/license/licenseModule.js +50 -35
  76. package/lib/module/license/tierLimits.js +13 -63
  77. package/lib/module/storage/devToolsStorageKeys.js +8 -1
  78. package/lib/module/stores/BaseEventStore.js +21 -5
  79. package/lib/module/stores/createToolStore.js +21 -3
  80. package/lib/module/stores/floatingToolStore.js +61 -0
  81. package/lib/module/stores/floatingToolStore.test.js +111 -0
  82. package/lib/module/sync/keyedArrays.js +204 -0
  83. package/lib/module/sync/toolActionRegistry.js +84 -2
  84. package/lib/module/sync/toolAttention.test.js +50 -0
  85. package/lib/module/sync/toolUiBridge.js +55 -0
  86. package/lib/module/sync/typedEdit.js +220 -0
  87. package/lib/module/sync/valuePath.js +330 -0
  88. package/lib/module/sync/valueShape.js +153 -0
  89. package/lib/module/ui/backgrounds/README.md +52 -0
  90. package/lib/module/ui/backgrounds/ToolBackground.js +4 -2
  91. package/lib/module/ui/backgrounds/index.js +2 -1
  92. package/lib/module/ui/backgrounds/registry.js +8 -0
  93. package/lib/module/ui/backgrounds/variants/wormhole/motion.js +44 -0
  94. package/lib/module/ui/backgrounds/variants/wormhole/textures.js +3 -0
  95. package/lib/module/ui/backgrounds/variants/wormhole.js +129 -0
  96. package/lib/module/ui/components/ClipboardHintBanner.js +5 -6
  97. package/lib/module/ui/components/CompactRow.js +94 -74
  98. package/lib/module/ui/components/DraggableHeader.js +16 -1
  99. package/lib/module/ui/components/ExpandablePopover.js +38 -6
  100. package/lib/module/ui/components/HeaderSearchButton.js +17 -4
  101. package/lib/module/ui/floating/FloatingTool.js +1003 -0
  102. package/lib/module/ui/floating/FloatingTool.web.js +590 -0
  103. package/lib/module/ui/floating/FloatingToolHost.js +29 -0
  104. package/lib/module/ui/floating/FloatingToolHost.web.js +25 -0
  105. package/lib/module/ui/floating/index.js +6 -0
  106. package/lib/module/ui/floating/index.web.js +6 -0
  107. package/lib/module/ui/floating/onboardingConstants.js +64 -0
  108. package/lib/module/ui/floating/onboardingConstants.web.js +27 -0
  109. package/lib/module/ui/gameUI/constants/gameUIColors.js +3 -1
  110. package/lib/module/ui/index.js +5 -1
  111. package/lib/module/ui/night/nightTheme.js +10 -0
  112. package/lib/module/ui/night/primitives.js +22 -5
  113. package/lib/module/ui/otto/OttoSceneView.js +64 -0
  114. package/lib/module/ui/otto/index.js +3 -1
  115. package/lib/module/ui/otto/ottoEngine.js +10 -7
  116. package/lib/module/ui/otto/ottoScenes.js +942 -0
  117. package/lib/module/utils/minimalSuperJSON.js +13 -3
  118. package/lib/typescript/commonjs/JsModal.d.ts +43 -2
  119. package/lib/typescript/commonjs/JsModal.d.ts.map +1 -1
  120. package/lib/typescript/commonjs/JsModal.web.d.ts +5 -2
  121. package/lib/typescript/commonjs/JsModal.web.d.ts.map +1 -1
  122. package/lib/typescript/commonjs/dataViewer/CyberpunkInput.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/dataViewer/LiveExplorer.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  125. package/lib/typescript/commonjs/icons/ReactQueryIcon.d.ts +1 -2
  126. package/lib/typescript/commonjs/icons/ReactQueryIcon.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/icons/SentryBugIcon.d.ts +1 -2
  128. package/lib/typescript/commonjs/icons/SentryBugIcon.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/icons/index.d.ts +1 -1
  130. package/lib/typescript/commonjs/icons/index.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/index.d.ts +8 -1
  132. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/license/AccountSetup.d.ts +4 -0
  134. package/lib/typescript/commonjs/license/AccountSetup.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/license/AccountSetup.web.d.ts +2 -0
  136. package/lib/typescript/commonjs/license/AccountSetup.web.d.ts.map +1 -0
  137. package/lib/typescript/commonjs/license/FeatureGate.d.ts +3 -19
  138. package/lib/typescript/commonjs/license/FeatureGate.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/license/FreeAccountPrompt.d.ts +8 -11
  140. package/lib/typescript/commonjs/license/FreeAccountPrompt.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/license/index.d.ts +1 -1
  142. package/lib/typescript/commonjs/license/index.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/license/licenseModule.d.ts +18 -31
  144. package/lib/typescript/commonjs/license/licenseModule.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/license/tierLimits.d.ts +9 -52
  146. package/lib/typescript/commonjs/license/tierLimits.d.ts.map +1 -1
  147. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts +6 -0
  148. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/stores/BaseEventStore.d.ts.map +1 -1
  150. package/lib/typescript/commonjs/stores/createToolStore.d.ts.map +1 -1
  151. package/lib/typescript/commonjs/stores/floatingToolStore.d.ts +27 -0
  152. package/lib/typescript/commonjs/stores/floatingToolStore.d.ts.map +1 -0
  153. package/lib/typescript/commonjs/stores/floatingToolStore.test.d.ts +2 -0
  154. package/lib/typescript/commonjs/stores/floatingToolStore.test.d.ts.map +1 -0
  155. package/lib/typescript/commonjs/sync/keyedArrays.d.ts +35 -0
  156. package/lib/typescript/commonjs/sync/keyedArrays.d.ts.map +1 -0
  157. package/lib/typescript/commonjs/sync/toolActionRegistry.d.ts +4 -32
  158. package/lib/typescript/commonjs/sync/toolActionRegistry.d.ts.map +1 -1
  159. package/lib/typescript/commonjs/sync/toolAttention.test.d.ts +2 -0
  160. package/lib/typescript/commonjs/sync/toolAttention.test.d.ts.map +1 -0
  161. package/lib/typescript/commonjs/sync/toolUiBridge.d.ts +25 -0
  162. package/lib/typescript/commonjs/sync/toolUiBridge.d.ts.map +1 -1
  163. package/lib/typescript/commonjs/sync/typedEdit.d.ts +70 -0
  164. package/lib/typescript/commonjs/sync/typedEdit.d.ts.map +1 -0
  165. package/lib/typescript/commonjs/sync/valuePath.d.ts +52 -0
  166. package/lib/typescript/commonjs/sync/valuePath.d.ts.map +1 -0
  167. package/lib/typescript/commonjs/sync/valueShape.d.ts +77 -0
  168. package/lib/typescript/commonjs/sync/valueShape.d.ts.map +1 -0
  169. package/lib/typescript/commonjs/ui/backgrounds/ToolBackground.d.ts +7 -1
  170. package/lib/typescript/commonjs/ui/backgrounds/ToolBackground.d.ts.map +1 -1
  171. package/lib/typescript/commonjs/ui/backgrounds/index.d.ts +2 -1
  172. package/lib/typescript/commonjs/ui/backgrounds/index.d.ts.map +1 -1
  173. package/lib/typescript/commonjs/ui/backgrounds/registry.d.ts.map +1 -1
  174. package/lib/typescript/commonjs/ui/backgrounds/types.d.ts +11 -1
  175. package/lib/typescript/commonjs/ui/backgrounds/types.d.ts.map +1 -1
  176. package/lib/typescript/commonjs/ui/backgrounds/variants/wormhole/motion.d.ts +24 -0
  177. package/lib/typescript/commonjs/ui/backgrounds/variants/wormhole/motion.d.ts.map +1 -0
  178. package/lib/typescript/commonjs/ui/backgrounds/variants/wormhole/textures.d.ts +13 -0
  179. package/lib/typescript/commonjs/ui/backgrounds/variants/wormhole/textures.d.ts.map +1 -0
  180. package/lib/typescript/commonjs/ui/backgrounds/variants/wormhole.d.ts +3 -0
  181. package/lib/typescript/commonjs/ui/backgrounds/variants/wormhole.d.ts.map +1 -0
  182. package/lib/typescript/commonjs/ui/components/ClipboardHintBanner.d.ts.map +1 -1
  183. package/lib/typescript/commonjs/ui/components/CompactRow.d.ts +8 -1
  184. package/lib/typescript/commonjs/ui/components/CompactRow.d.ts.map +1 -1
  185. package/lib/typescript/commonjs/ui/components/DraggableHeader.d.ts +4 -1
  186. package/lib/typescript/commonjs/ui/components/DraggableHeader.d.ts.map +1 -1
  187. package/lib/typescript/commonjs/ui/components/ExpandablePopover.d.ts +14 -1
  188. package/lib/typescript/commonjs/ui/components/ExpandablePopover.d.ts.map +1 -1
  189. package/lib/typescript/commonjs/ui/components/HeaderSearchButton.d.ts +7 -1
  190. package/lib/typescript/commonjs/ui/components/HeaderSearchButton.d.ts.map +1 -1
  191. package/lib/typescript/commonjs/ui/floating/FloatingTool.d.ts +79 -0
  192. package/lib/typescript/commonjs/ui/floating/FloatingTool.d.ts.map +1 -0
  193. package/lib/typescript/commonjs/ui/floating/FloatingTool.web.d.ts +64 -0
  194. package/lib/typescript/commonjs/ui/floating/FloatingTool.web.d.ts.map +1 -0
  195. package/lib/typescript/commonjs/ui/floating/FloatingToolHost.d.ts +3 -0
  196. package/lib/typescript/commonjs/ui/floating/FloatingToolHost.d.ts.map +1 -0
  197. package/lib/typescript/commonjs/ui/floating/FloatingToolHost.web.d.ts +3 -0
  198. package/lib/typescript/commonjs/ui/floating/FloatingToolHost.web.d.ts.map +1 -0
  199. package/lib/typescript/commonjs/ui/floating/index.d.ts +7 -0
  200. package/lib/typescript/commonjs/ui/floating/index.d.ts.map +1 -0
  201. package/lib/typescript/commonjs/ui/floating/index.web.d.ts +7 -0
  202. package/lib/typescript/commonjs/ui/floating/index.web.d.ts.map +1 -0
  203. package/lib/typescript/commonjs/ui/floating/onboardingConstants.d.ts +30 -0
  204. package/lib/typescript/commonjs/ui/floating/onboardingConstants.d.ts.map +1 -0
  205. package/lib/typescript/commonjs/ui/floating/onboardingConstants.web.d.ts +13 -0
  206. package/lib/typescript/commonjs/ui/floating/onboardingConstants.web.d.ts.map +1 -0
  207. package/lib/typescript/commonjs/ui/gameUI/constants/gameUIColors.d.ts +2 -0
  208. package/lib/typescript/commonjs/ui/gameUI/constants/gameUIColors.d.ts.map +1 -1
  209. package/lib/typescript/commonjs/ui/index.d.ts +6 -0
  210. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  211. package/lib/typescript/commonjs/ui/night/nightTheme.d.ts +10 -0
  212. package/lib/typescript/commonjs/ui/night/nightTheme.d.ts.map +1 -1
  213. package/lib/typescript/commonjs/ui/night/primitives.d.ts +10 -3
  214. package/lib/typescript/commonjs/ui/night/primitives.d.ts.map +1 -1
  215. package/lib/typescript/commonjs/ui/otto/OttoSceneView.d.ts +30 -0
  216. package/lib/typescript/commonjs/ui/otto/OttoSceneView.d.ts.map +1 -0
  217. package/lib/typescript/commonjs/ui/otto/index.d.ts +2 -0
  218. package/lib/typescript/commonjs/ui/otto/index.d.ts.map +1 -1
  219. package/lib/typescript/commonjs/ui/otto/ottoEngine.d.ts +1 -1
  220. package/lib/typescript/commonjs/ui/otto/ottoEngine.d.ts.map +1 -1
  221. package/lib/typescript/commonjs/ui/otto/ottoScenes.d.ts +75 -0
  222. package/lib/typescript/commonjs/ui/otto/ottoScenes.d.ts.map +1 -0
  223. package/lib/typescript/commonjs/utils/minimalSuperJSON.d.ts.map +1 -1
  224. package/lib/typescript/module/JsModal.d.ts +43 -2
  225. package/lib/typescript/module/JsModal.d.ts.map +1 -1
  226. package/lib/typescript/module/JsModal.web.d.ts +5 -2
  227. package/lib/typescript/module/JsModal.web.d.ts.map +1 -1
  228. package/lib/typescript/module/dataViewer/CyberpunkInput.d.ts.map +1 -1
  229. package/lib/typescript/module/dataViewer/LiveExplorer.d.ts.map +1 -1
  230. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  231. package/lib/typescript/module/icons/ReactQueryIcon.d.ts +1 -2
  232. package/lib/typescript/module/icons/ReactQueryIcon.d.ts.map +1 -1
  233. package/lib/typescript/module/icons/SentryBugIcon.d.ts +1 -2
  234. package/lib/typescript/module/icons/SentryBugIcon.d.ts.map +1 -1
  235. package/lib/typescript/module/icons/index.d.ts +1 -1
  236. package/lib/typescript/module/icons/index.d.ts.map +1 -1
  237. package/lib/typescript/module/index.d.ts +8 -1
  238. package/lib/typescript/module/index.d.ts.map +1 -1
  239. package/lib/typescript/module/license/AccountSetup.d.ts +4 -0
  240. package/lib/typescript/module/license/AccountSetup.d.ts.map +1 -0
  241. package/lib/typescript/module/license/AccountSetup.web.d.ts +2 -0
  242. package/lib/typescript/module/license/AccountSetup.web.d.ts.map +1 -0
  243. package/lib/typescript/module/license/FeatureGate.d.ts +3 -19
  244. package/lib/typescript/module/license/FeatureGate.d.ts.map +1 -1
  245. package/lib/typescript/module/license/FreeAccountPrompt.d.ts +8 -11
  246. package/lib/typescript/module/license/FreeAccountPrompt.d.ts.map +1 -1
  247. package/lib/typescript/module/license/index.d.ts +1 -1
  248. package/lib/typescript/module/license/index.d.ts.map +1 -1
  249. package/lib/typescript/module/license/licenseModule.d.ts +18 -31
  250. package/lib/typescript/module/license/licenseModule.d.ts.map +1 -1
  251. package/lib/typescript/module/license/tierLimits.d.ts +9 -52
  252. package/lib/typescript/module/license/tierLimits.d.ts.map +1 -1
  253. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts +6 -0
  254. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts.map +1 -1
  255. package/lib/typescript/module/stores/BaseEventStore.d.ts.map +1 -1
  256. package/lib/typescript/module/stores/createToolStore.d.ts.map +1 -1
  257. package/lib/typescript/module/stores/floatingToolStore.d.ts +27 -0
  258. package/lib/typescript/module/stores/floatingToolStore.d.ts.map +1 -0
  259. package/lib/typescript/module/stores/floatingToolStore.test.d.ts +2 -0
  260. package/lib/typescript/module/stores/floatingToolStore.test.d.ts.map +1 -0
  261. package/lib/typescript/module/sync/keyedArrays.d.ts +35 -0
  262. package/lib/typescript/module/sync/keyedArrays.d.ts.map +1 -0
  263. package/lib/typescript/module/sync/toolActionRegistry.d.ts +4 -32
  264. package/lib/typescript/module/sync/toolActionRegistry.d.ts.map +1 -1
  265. package/lib/typescript/module/sync/toolAttention.test.d.ts +2 -0
  266. package/lib/typescript/module/sync/toolAttention.test.d.ts.map +1 -0
  267. package/lib/typescript/module/sync/toolUiBridge.d.ts +25 -0
  268. package/lib/typescript/module/sync/toolUiBridge.d.ts.map +1 -1
  269. package/lib/typescript/module/sync/typedEdit.d.ts +70 -0
  270. package/lib/typescript/module/sync/typedEdit.d.ts.map +1 -0
  271. package/lib/typescript/module/sync/valuePath.d.ts +52 -0
  272. package/lib/typescript/module/sync/valuePath.d.ts.map +1 -0
  273. package/lib/typescript/module/sync/valueShape.d.ts +77 -0
  274. package/lib/typescript/module/sync/valueShape.d.ts.map +1 -0
  275. package/lib/typescript/module/ui/backgrounds/ToolBackground.d.ts +7 -1
  276. package/lib/typescript/module/ui/backgrounds/ToolBackground.d.ts.map +1 -1
  277. package/lib/typescript/module/ui/backgrounds/index.d.ts +2 -1
  278. package/lib/typescript/module/ui/backgrounds/index.d.ts.map +1 -1
  279. package/lib/typescript/module/ui/backgrounds/registry.d.ts.map +1 -1
  280. package/lib/typescript/module/ui/backgrounds/types.d.ts +11 -1
  281. package/lib/typescript/module/ui/backgrounds/types.d.ts.map +1 -1
  282. package/lib/typescript/module/ui/backgrounds/variants/wormhole/motion.d.ts +24 -0
  283. package/lib/typescript/module/ui/backgrounds/variants/wormhole/motion.d.ts.map +1 -0
  284. package/lib/typescript/module/ui/backgrounds/variants/wormhole/textures.d.ts +13 -0
  285. package/lib/typescript/module/ui/backgrounds/variants/wormhole/textures.d.ts.map +1 -0
  286. package/lib/typescript/module/ui/backgrounds/variants/wormhole.d.ts +3 -0
  287. package/lib/typescript/module/ui/backgrounds/variants/wormhole.d.ts.map +1 -0
  288. package/lib/typescript/module/ui/components/ClipboardHintBanner.d.ts.map +1 -1
  289. package/lib/typescript/module/ui/components/CompactRow.d.ts +8 -1
  290. package/lib/typescript/module/ui/components/CompactRow.d.ts.map +1 -1
  291. package/lib/typescript/module/ui/components/DraggableHeader.d.ts +4 -1
  292. package/lib/typescript/module/ui/components/DraggableHeader.d.ts.map +1 -1
  293. package/lib/typescript/module/ui/components/ExpandablePopover.d.ts +14 -1
  294. package/lib/typescript/module/ui/components/ExpandablePopover.d.ts.map +1 -1
  295. package/lib/typescript/module/ui/components/HeaderSearchButton.d.ts +7 -1
  296. package/lib/typescript/module/ui/components/HeaderSearchButton.d.ts.map +1 -1
  297. package/lib/typescript/module/ui/floating/FloatingTool.d.ts +79 -0
  298. package/lib/typescript/module/ui/floating/FloatingTool.d.ts.map +1 -0
  299. package/lib/typescript/module/ui/floating/FloatingTool.web.d.ts +64 -0
  300. package/lib/typescript/module/ui/floating/FloatingTool.web.d.ts.map +1 -0
  301. package/lib/typescript/module/ui/floating/FloatingToolHost.d.ts +3 -0
  302. package/lib/typescript/module/ui/floating/FloatingToolHost.d.ts.map +1 -0
  303. package/lib/typescript/module/ui/floating/FloatingToolHost.web.d.ts +3 -0
  304. package/lib/typescript/module/ui/floating/FloatingToolHost.web.d.ts.map +1 -0
  305. package/lib/typescript/module/ui/floating/index.d.ts +7 -0
  306. package/lib/typescript/module/ui/floating/index.d.ts.map +1 -0
  307. package/lib/typescript/module/ui/floating/index.web.d.ts +7 -0
  308. package/lib/typescript/module/ui/floating/index.web.d.ts.map +1 -0
  309. package/lib/typescript/module/ui/floating/onboardingConstants.d.ts +30 -0
  310. package/lib/typescript/module/ui/floating/onboardingConstants.d.ts.map +1 -0
  311. package/lib/typescript/module/ui/floating/onboardingConstants.web.d.ts +13 -0
  312. package/lib/typescript/module/ui/floating/onboardingConstants.web.d.ts.map +1 -0
  313. package/lib/typescript/module/ui/gameUI/constants/gameUIColors.d.ts +2 -0
  314. package/lib/typescript/module/ui/gameUI/constants/gameUIColors.d.ts.map +1 -1
  315. package/lib/typescript/module/ui/index.d.ts +6 -0
  316. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  317. package/lib/typescript/module/ui/night/nightTheme.d.ts +10 -0
  318. package/lib/typescript/module/ui/night/nightTheme.d.ts.map +1 -1
  319. package/lib/typescript/module/ui/night/primitives.d.ts +10 -3
  320. package/lib/typescript/module/ui/night/primitives.d.ts.map +1 -1
  321. package/lib/typescript/module/ui/otto/OttoSceneView.d.ts +30 -0
  322. package/lib/typescript/module/ui/otto/OttoSceneView.d.ts.map +1 -0
  323. package/lib/typescript/module/ui/otto/index.d.ts +2 -0
  324. package/lib/typescript/module/ui/otto/index.d.ts.map +1 -1
  325. package/lib/typescript/module/ui/otto/ottoEngine.d.ts +1 -1
  326. package/lib/typescript/module/ui/otto/ottoEngine.d.ts.map +1 -1
  327. package/lib/typescript/module/ui/otto/ottoScenes.d.ts +75 -0
  328. package/lib/typescript/module/ui/otto/ottoScenes.d.ts.map +1 -0
  329. package/lib/typescript/module/utils/minimalSuperJSON.d.ts.map +1 -1
  330. package/package.json +25 -5
  331. package/scripts/generate-wormhole-textures.cjs +261 -0
  332. package/scripts/test-wormhole-motion.cjs +59 -0
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
 
3
+ import { getAccountAccess, hasAccountAccess, subscribeAccountAccess } from "../license/licenseModule.js";
3
4
  /**
4
5
  * BaseEventStore - Abstract base class for event aggregation stores
5
6
  *
@@ -65,6 +66,20 @@ export class BaseEventStore extends Subscribable {
65
66
  super();
66
67
  this.maxEvents = options.maxEvents ?? 500;
67
68
  this.storeName = options.storeName;
69
+ // Defer until subclass fields are initialized before invoking lifecycle hooks.
70
+ Promise.resolve().then(() => {
71
+ const reconcile = () => {
72
+ if (getAccountAccess().status === "locked") {
73
+ this.stopCapturing();
74
+ this.events = [];
75
+ this.notifyArrayListeners();
76
+ } else if (this.getTotalSubscriberCount() > 0 && !this.captureSuppressed && !this.isCapturing()) {
77
+ this.startCapturing();
78
+ }
79
+ };
80
+ subscribeAccountAccess(reconcile);
81
+ reconcile();
82
+ });
68
83
  }
69
84
 
70
85
  // ===========================================================================
@@ -94,7 +109,7 @@ export class BaseEventStore extends Subscribable {
94
109
  * Starts capturing if no one was subscribed.
95
110
  */
96
111
  onSubscribe() {
97
- if (this.getTotalSubscriberCount() === 1 && !this.captureSuppressed) {
112
+ if (this.getTotalSubscriberCount() === 1 && !this.captureSuppressed && getAccountAccess().status !== "locked") {
98
113
  this.startCapturing();
99
114
  }
100
115
  notifySubscriberCountChange(this.storeName);
@@ -134,7 +149,7 @@ export class BaseEventStore extends Subscribable {
134
149
  this.arrayListeners.add(listener);
135
150
 
136
151
  // Start capturing if this is the first subscriber
137
- if (wasEmpty && !this.captureSuppressed) {
152
+ if (wasEmpty && !this.captureSuppressed && getAccountAccess().status !== "locked") {
138
153
  this.startCapturing();
139
154
  }
140
155
 
@@ -181,11 +196,12 @@ export class BaseEventStore extends Subscribable {
181
196
  * @param event - The event to add
182
197
  */
183
198
  addEvent(event) {
199
+ if (getAccountAccess().status === "locked") return;
184
200
  // Add to beginning (newest first) and limit to maxEvents
185
201
  this.events = [event, ...this.events].slice(0, this.maxEvents);
186
202
 
187
203
  // Notify individual event callbacks (via Subscribable)
188
- this.notify(event);
204
+ if (hasAccountAccess()) this.notify(event);
189
205
 
190
206
  // Notify array listeners
191
207
  this.notifyArrayListeners();
@@ -209,14 +225,14 @@ export class BaseEventStore extends Subscribable {
209
225
  * Get all events (newest first)
210
226
  */
211
227
  getEvents() {
212
- return this.events;
228
+ return hasAccountAccess() ? this.events : [];
213
229
  }
214
230
 
215
231
  /**
216
232
  * Get event count
217
233
  */
218
234
  getEventCount() {
219
- return this.events.length;
235
+ return hasAccountAccess() ? this.events.length : 0;
220
236
  }
221
237
 
222
238
  /**
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
 
3
+ import { getAccountAccess, hasAccountAccess, subscribeAccountAccess } from "../license/licenseModule.js";
3
4
  /**
4
5
  * createToolStore — the one bounded, subscribable buffer every tool used to
5
6
  * hand-roll.
@@ -49,6 +50,16 @@ export function createToolStore(options) {
49
50
  let nextId = options.firstId ?? 1;
50
51
  let lastFlushAt = 0;
51
52
  let emitScheduled = false;
53
+ const empty = [];
54
+ let observing = false;
55
+ function observe() {
56
+ if (observing) return;
57
+ observing = true;
58
+ subscribeAccountAccess(() => {
59
+ if (getAccountAccess().status === "locked") items = [];
60
+ emit();
61
+ });
62
+ }
52
63
  const listeners = new Set();
53
64
  const clearListeners = new Set();
54
65
  function fire() {
@@ -79,19 +90,22 @@ export function createToolStore(options) {
79
90
  const store = {
80
91
  id,
81
92
  subscribe(listener) {
93
+ observe();
82
94
  listeners.add(listener);
83
95
  return () => {
84
96
  listeners.delete(listener);
85
97
  };
86
98
  },
87
99
  getSnapshot() {
88
- return items;
100
+ observe();
101
+ return hasAccountAccess() ? items : empty;
89
102
  },
90
103
  size() {
91
- return items.length;
104
+ return hasAccountAccess() ? items.length : 0;
92
105
  },
93
106
  add(item) {
94
- if (paused) return;
107
+ observe();
108
+ if (paused || getAccountAccess().status === "locked") return;
95
109
  if (order === "append") {
96
110
  items = items.length >= max ? [...items.slice(items.length - max + 1), item] : [...items, item];
97
111
  } else {
@@ -100,10 +114,14 @@ export function createToolStore(options) {
100
114
  emit();
101
115
  },
102
116
  set(next) {
117
+ observe();
118
+ if (!hasAccountAccess()) return;
103
119
  items = trim(next);
104
120
  emit();
105
121
  },
106
122
  update(fn) {
123
+ observe();
124
+ if (getAccountAccess().status === "locked") return;
107
125
  items = trim(fn(items));
108
126
  emit();
109
127
  },
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ let entries = [];
4
+ let revision = 0;
5
+ const listeners = new Set();
6
+ const notify = () => listeners.forEach(listener => listener());
7
+ export const getFloatingTools = () => entries;
8
+ export const subscribeFloatingTools = listener => {
9
+ listeners.add(listener);
10
+ return () => {
11
+ listeners.delete(listener);
12
+ };
13
+ };
14
+
15
+ /** Open or restore one tool. Mount FloatingToolHost once at the application root. */
16
+ export function openFloatingTool(tool) {
17
+ if (!tool.id.trim()) throw new Error("Floating tools need a non-empty id");
18
+ const entry = {
19
+ ...tool,
20
+ revealToken: ++revision
21
+ };
22
+ entries = entries.some(value => value.id === tool.id) ? entries.map(value => value.id === tool.id ? entry : value) : [...entries, entry];
23
+ notify();
24
+ }
25
+
26
+ /** Removes the UI only. Tool actions such as Stop belong to the tool's own controller. */
27
+ export function dismissFloatingTool(id) {
28
+ if (!entries.some(entry => entry.id === id)) return;
29
+ entries = entries.filter(entry => entry.id !== id);
30
+ notify();
31
+ }
32
+
33
+ /** Register at app startup to restore an open tool using its existing position storage. */
34
+ export function registerPersistentFloatingTool(tool, storage) {
35
+ if (!tool.id.trim()) throw new Error("Floating tools need a non-empty id");
36
+ const key = `@buoy/floating-tool/${tool.id}/open`;
37
+ let disposed = false;
38
+ let changed = false;
39
+ let wasOpen = entries.some(entry => entry.id === tool.id);
40
+ let writes = Promise.resolve();
41
+ const unsubscribe = subscribeFloatingTools(() => {
42
+ const isOpen = entries.some(entry => entry.id === tool.id);
43
+ if (isOpen === wasOpen) return;
44
+ wasOpen = isOpen;
45
+ changed = true;
46
+ writes = writes.then(() => storage.setItem(key, isOpen ? "1" : "0")).then(() => {}, () => {});
47
+ });
48
+ void storage.getItem(key).then(saved => {
49
+ if (disposed || changed || wasOpen || saved !== "1") return;
50
+ // Zero restores the saved hidden state; explicit opens still reveal the tool.
51
+ entries = [...entries, {
52
+ ...tool,
53
+ revealToken: 0
54
+ }];
55
+ notify();
56
+ }).catch(() => {});
57
+ return () => {
58
+ disposed = true;
59
+ unsubscribe();
60
+ };
61
+ }
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+
3
+ import { afterEach, describe, expect, it, vi } from "vitest";
4
+ import { dismissFloatingTool, getFloatingTools, openFloatingTool, subscribeFloatingTools } from "./floatingToolStore.js";
5
+ afterEach(() => getFloatingTools().forEach(tool => dismissFloatingTool(tool.id)));
6
+ describe("floating tool registry", () => {
7
+ it("restores an existing id and keeps other tools intact", () => {
8
+ openFloatingTool({
9
+ id: "network",
10
+ render: () => null
11
+ });
12
+ openFloatingTool({
13
+ id: "recorder",
14
+ render: () => null
15
+ });
16
+ const first = getFloatingTools()[0].revealToken;
17
+ const other = getFloatingTools()[1];
18
+ openFloatingTool({
19
+ id: "network",
20
+ render: () => "updated"
21
+ });
22
+ expect(getFloatingTools()).toHaveLength(2);
23
+ expect(getFloatingTools()[0].revealToken).toBeGreaterThan(first);
24
+ expect(getFloatingTools()[1]).toBe(other);
25
+ });
26
+ it("publishes stable snapshots and isolates dismissal", () => {
27
+ const listener = vi.fn();
28
+ const unsubscribe = subscribeFloatingTools(listener);
29
+ openFloatingTool({
30
+ id: "one",
31
+ render: () => null
32
+ });
33
+ const snapshot = getFloatingTools();
34
+ expect(getFloatingTools()).toBe(snapshot);
35
+ dismissFloatingTool("missing");
36
+ expect(listener).toHaveBeenCalledTimes(1);
37
+ openFloatingTool({
38
+ id: "two",
39
+ render: () => null
40
+ });
41
+ dismissFloatingTool("one");
42
+ expect(getFloatingTools().map(tool => tool.id)).toEqual(["two"]);
43
+ unsubscribe();
44
+ dismissFloatingTool("two");
45
+ expect(listener).toHaveBeenCalledTimes(3);
46
+ });
47
+ it("rejects missing tool identities", () => {
48
+ expect(() => openFloatingTool({
49
+ id: " ",
50
+ render: () => null
51
+ })).toThrow("non-empty id");
52
+ expect(getFloatingTools()).toHaveLength(0);
53
+ });
54
+ });
55
+ describe("persistent floating tools", () => {
56
+ it("restores after a fresh registry loads and remembers dismissal", async () => {
57
+ const values = new Map();
58
+ const storage = {
59
+ getItem: vi.fn(async key => values.get(key) ?? null),
60
+ setItem: vi.fn(async (key, value) => {
61
+ values.set(key, value);
62
+ })
63
+ };
64
+ const tool = {
65
+ id: "persisted",
66
+ render: () => null
67
+ };
68
+ vi.resetModules();
69
+ const first = await import("./floatingToolStore");
70
+ const disposeFirst = first.registerPersistentFloatingTool(tool, storage);
71
+ first.openFloatingTool(tool);
72
+ await vi.waitFor(() => expect(values.get("@buoy/floating-tool/persisted/open")).toBe("1"));
73
+ disposeFirst();
74
+ vi.resetModules();
75
+ const second = await import("./floatingToolStore");
76
+ const disposeSecond = second.registerPersistentFloatingTool(tool, storage);
77
+ await vi.waitFor(() => expect(second.getFloatingTools()).toHaveLength(1));
78
+ expect(second.getFloatingTools()[0].revealToken).toBe(0);
79
+ second.dismissFloatingTool(tool.id);
80
+ await vi.waitFor(() => expect(values.get("@buoy/floating-tool/persisted/open")).toBe("0"));
81
+ disposeSecond();
82
+ vi.resetModules();
83
+ const third = await import("./floatingToolStore");
84
+ const disposeThird = third.registerPersistentFloatingTool(tool, storage);
85
+ await Promise.resolve();
86
+ expect(third.getFloatingTools()).toHaveLength(0);
87
+ disposeThird();
88
+ });
89
+ it("does not reopen a tool closed while its saved state was loading", async () => {
90
+ vi.resetModules();
91
+ const registry = await import("./floatingToolStore");
92
+ let resolve;
93
+ const storage = {
94
+ getItem: () => new Promise(done => {
95
+ resolve = done;
96
+ }),
97
+ setItem: vi.fn(async () => {})
98
+ };
99
+ const tool = {
100
+ id: "race",
101
+ render: () => null
102
+ };
103
+ const dispose = registry.registerPersistentFloatingTool(tool, storage);
104
+ registry.openFloatingTool(tool);
105
+ registry.dismissFloatingTool(tool.id);
106
+ resolve("1");
107
+ await vi.waitFor(() => expect(storage.setItem).toHaveBeenLastCalledWith("@buoy/floating-tool/race/open", "0"));
108
+ expect(registry.getFloatingTools()).toHaveLength(0);
109
+ dispose();
110
+ });
111
+ });
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Id-addressed list edits — one implementation, shared by every adapter that
5
+ * takes a merge patch.
6
+ *
7
+ * WHY. Resending a whole array is correct only when the caller has SEEN the
8
+ * whole array, and reads are capped. Measured with a 20-item list truncated to
9
+ * 5 (`pnpm ai:array --truncate=5`): the whole-array form scored 0/9 across three
10
+ * models — two refused outright, and one silently sent back only the 5 items it
11
+ * had been shown, destroying the other 15. Addressed edits scored 9/9.
12
+ *
13
+ * It also makes mass deletion impossible BY OMISSION: an addressed edit can only
14
+ * remove ids it names. Sending a real array still replaces the list, which is
15
+ * how a caller deliberately empties one.
16
+ *
17
+ * This lives here rather than in one adapter because react-query, zustand and
18
+ * network all take merge patches, and a second copy of a data-destroying safety
19
+ * rule is a copy that drifts.
20
+ */
21
+ const isPlainObject = v => typeof v === "object" && v !== null && !Array.isArray(v);
22
+
23
+ /** Fields we will treat as an item's stable identity, best first. */
24
+ const ID_FIELDS = ["id", "lineId", "key", "uuid", "_id", "slug", "name"];
25
+
26
+ /**
27
+ * The field that identifies items in this list, or undefined if there isn't one.
28
+ *
29
+ * Every item must carry it, and every value must be a distinct string/number —
30
+ * a duplicated id would make an addressed edit ambiguous, and silently picking
31
+ * the first match is exactly the kind of "helpful" guess that loses data.
32
+ */
33
+ export function identityField(items) {
34
+ if (!items.length || !items.every(isPlainObject)) return undefined;
35
+ const first = items[0];
36
+ for (const f of ID_FIELDS) {
37
+ if (!(f in first)) continue;
38
+ const values = items.map(it => it[f]);
39
+ const usable = values.every(v => typeof v === "string" || typeof v === "number");
40
+ if (!usable) continue;
41
+ if (new Set(values.map(String)).size !== values.length) continue;
42
+ return f;
43
+ }
44
+ return undefined;
45
+ }
46
+
47
+ /**
48
+ * The identity an ADDED item should carry.
49
+ *
50
+ * `Object.entries` hands back STRING keys, so `{"9901": {id: 9901, …}}` was
51
+ * written as `{id: "9901"}` — the caller's own numeric id replaced by text, and
52
+ * then refused by the typed-edit guard as `[2].id: number → string`. The caller
53
+ * recovers by resending the WHOLE array, which is exactly the truncation-unsafe
54
+ * move this module exists to prevent. Measured on a live QA run: one added
55
+ * offer cost a refusal, a full-list retransmission and 35 seconds.
56
+ *
57
+ * So the id the caller WROTE wins. With no id in the body, the key is coerced
58
+ * to the type the existing items use, and only when the text round-trips
59
+ * exactly: "007" is not 7, and inventing that identity is worse than a refusal
60
+ * the caller can read.
61
+ */
62
+ function identityForAdd(current, idField, key, edit) {
63
+ const supplied = edit[idField];
64
+ if (typeof supplied === "string" || typeof supplied === "number") {
65
+ // An item whose id disagrees with the address it was sent under is
66
+ // ambiguous — either could be the one the caller meant. Say so rather than
67
+ // silently picking one and writing an item nobody asked for.
68
+ if (String(supplied) !== key) {
69
+ return {
70
+ ok: false,
71
+ error: `The new item is addressed as \`${key}\` but carries \`${idField}\`: ${JSON.stringify(supplied)}. Use the same value for both — address it by the id it carries.`
72
+ };
73
+ }
74
+ return {
75
+ ok: true,
76
+ value: supplied
77
+ };
78
+ }
79
+ const numericList = current.every(it => typeof it[idField] === "number");
80
+ if (numericList && key.trim() !== "" && String(Number(key)) === key) {
81
+ return {
82
+ ok: true,
83
+ value: Number(key)
84
+ };
85
+ }
86
+ return {
87
+ ok: true,
88
+ value: key
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Let a caller address list items by their own id instead of resending the list.
94
+ *
95
+ * { lines: { "seed-1": { qty: 5 } } } change one field of one item
96
+ * { lines: { "seed-2": null } } remove that item
97
+ * { lines: { "new-1": { …all fields… } } } append (id not already present)
98
+ *
99
+ * WHY THIS EXISTS. Resending the whole array is correct only when the caller has
100
+ * SEEN the whole array, and reads are capped — so on a long list the honest
101
+ * agents refuse and the careless ones send back only the items they were shown,
102
+ * silently destroying the rest. Measured: with a 20-item bag truncated to 5, the
103
+ * whole-array form scored 0/9 across three models and one of them deleted 15
104
+ * lines without a word. Addressed edits scored 9/9. (`pnpm ai:array`.)
105
+ *
106
+ * It also makes mass deletion impossible BY OMISSION: `lines: []` empties a bag,
107
+ * but a keyed edit can only remove ids it names.
108
+ *
109
+ * Returns the patch with keyed objects expanded into real arrays, so everything
110
+ * downstream — the typed-edit guard, the receipt, the store write — is unchanged
111
+ * and keeps all of its protections.
112
+ */
113
+ export function expandKeyedArrays(current, patch, path = "", notes = []) {
114
+ if (Array.isArray(current) && isPlainObject(patch)) {
115
+ const idField = identityField(current);
116
+ if (!idField) {
117
+ // No stable identity: leave it alone and let the normal guard report the
118
+ // object-vs-array type change, which says something truer than we could.
119
+ return {
120
+ patch,
121
+ notes
122
+ };
123
+ }
124
+ const byId = new Map(current.map(it => [String(it[idField]), it]));
125
+ const next = current.slice();
126
+ let removed = 0;
127
+ let changed = 0;
128
+ let added = 0;
129
+ for (const [id, edit] of Object.entries(patch)) {
130
+ const i = next.findIndex(it => String(it[idField]) === id);
131
+ if (edit === null) {
132
+ if (i === -1) return {
133
+ patch,
134
+ notes
135
+ };
136
+ next.splice(i, 1);
137
+ removed++;
138
+ } else if (i === -1) {
139
+ if (!isPlainObject(edit)) return {
140
+ patch,
141
+ notes
142
+ };
143
+ const identity = identityForAdd(current, idField, id, edit);
144
+ if (!identity.ok) {
145
+ return {
146
+ patch,
147
+ notes,
148
+ error: identity.error
149
+ };
150
+ }
151
+ next.push({
152
+ ...edit,
153
+ [idField]: identity.value
154
+ });
155
+ added++;
156
+ } else {
157
+ const merged = expandKeyedArrays(byId.get(id), edit, `${path}[${id}]`, notes);
158
+ if (merged.error) return {
159
+ patch,
160
+ notes,
161
+ error: merged.error
162
+ };
163
+ const base = next[i];
164
+ next[i] = isPlainObject(base) && isPlainObject(merged.patch) ? {
165
+ ...base,
166
+ ...merged.patch
167
+ } : merged.patch;
168
+ changed++;
169
+ }
170
+ }
171
+ notes.push(`${path || "(root)"}: addressed by \`${idField}\` — ${changed} changed, ${added} added, ${removed} removed, ${next.length} item(s) remain`);
172
+ return {
173
+ patch: next,
174
+ notes
175
+ };
176
+ }
177
+ if (isPlainObject(current) && isPlainObject(patch)) {
178
+ const outPatch = {};
179
+ for (const [k, v] of Object.entries(patch)) {
180
+ const p = path ? `${path}.${k}` : k;
181
+ if (!(k in current)) {
182
+ outPatch[k] = v;
183
+ continue;
184
+ }
185
+ const inner = expandKeyedArrays(current[k], v, p, notes);
186
+ // A refusal deep in the tree is the whole patch's refusal: writing the
187
+ // rest of it would apply half of what the caller asked for.
188
+ if (inner.error) return {
189
+ patch,
190
+ notes,
191
+ error: inner.error
192
+ };
193
+ outPatch[k] = inner.patch;
194
+ }
195
+ return {
196
+ patch: outPatch,
197
+ notes
198
+ };
199
+ }
200
+ return {
201
+ patch,
202
+ notes
203
+ };
204
+ }
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
 
3
+ import { assertAccountAccess, hasAccountAccess, subscribeAccountAccess } from "../license/licenseModule.js";
3
4
  /**
4
5
  * toolActionRegistry — the on-device twin of the broker's action dispatch.
5
6
  *
@@ -56,13 +57,45 @@ function sources() {
56
57
  export function registerToolAdapters(getTools) {
57
58
  const set = sources();
58
59
  set.add(getTools);
60
+ let disposed = false;
61
+ const dispose = () => {
62
+ if (disposed) return;
63
+ disposed = true;
64
+ const raw = getTools();
65
+ // Private inverse operations only. No credential bypass reaches public dispatch.
66
+ const cleanup = async (toolId, action, params) => {
67
+ if (!(toolId === "network" && action === "deleteOverrideRule" || toolId === "impersonate" && action === "stopImpersonation")) {
68
+ throw new Error("Unsupported account-lock cleanup operation");
69
+ }
70
+ if (toolId === "impersonate") return raw[toolId]?.dispose?.(cleanup);
71
+ const fn = raw[toolId]?.actions?.[action];
72
+ if (!fn) throw new Error(`Cleanup unavailable for ${toolId}`);
73
+ return fn(params);
74
+ };
75
+ for (const adapter of Object.values(raw)) {
76
+ try {
77
+ Promise.resolve(adapter.dispose?.(cleanup)).catch(error => console.warn("[Buoy] Tool cleanup failed", error));
78
+ } catch (error) {
79
+ console.warn("[Buoy] Tool cleanup failed", error);
80
+ }
81
+ }
82
+ };
83
+ const off = subscribeAccountAccess(() => {
84
+ if (!hasAccountAccess()) dispose();else disposed = false;
85
+ });
59
86
  return () => {
87
+ off();
60
88
  set.delete(getTools);
89
+ // A second provider still owns these adapters during Strict Mode/remount.
90
+ Promise.resolve().then(() => {
91
+ if (set.size === 0) dispose();
92
+ });
61
93
  };
62
94
  }
63
95
 
64
96
  /** The merged adapter map across every live source. */
65
97
  export function getToolAdapters() {
98
+ if (!hasAccountAccess()) return {};
66
99
  const merged = {};
67
100
  for (const get of sources()) {
68
101
  try {
@@ -71,7 +104,7 @@ export function getToolAdapters() {
71
104
  // A source that throws must not take the whole registry down.
72
105
  }
73
106
  }
74
- return merged;
107
+ return Object.fromEntries(Object.entries(merged).map(([id, adapter]) => [id, guardToolAdapter(adapter)]));
75
108
  }
76
109
 
77
110
  /** Tool ids currently dispatchable. */
@@ -111,6 +144,7 @@ export function listSnapshotToolIds() {
111
144
  * an adapter is free to make this a promise later without breaking them.
112
145
  */
113
146
  export async function readToolSnapshot(toolId) {
147
+ assertAccountAccess();
114
148
  const tools = getToolAdapters();
115
149
  const adapter = tools[toolId];
116
150
  if (!adapter) throw new Error(`Unknown tool "${toolId}"`);
@@ -120,7 +154,7 @@ export async function readToolSnapshot(toolId) {
120
154
  return await adapter.getSnapshot();
121
155
  }
122
156
  export function isToolRegistryReady() {
123
- return sources().size > 0;
157
+ return hasAccountAccess() && sources().size > 0;
124
158
  }
125
159
 
126
160
  /**
@@ -129,6 +163,7 @@ export function isToolRegistryReady() {
129
163
  * surface as plain messages rather than objects nobody can read on a phone.
130
164
  */
131
165
  export async function dispatchToolAction(toolId, action, params) {
166
+ assertAccountAccess();
132
167
  const tools = getToolAdapters();
133
168
  const tool = tools[toolId];
134
169
  const fn = tool?.actions?.[action];
@@ -151,4 +186,51 @@ export async function waitForToolRegistry(timeoutMs = 2000) {
151
186
  if (isToolRegistryReady()) return true;
152
187
  }
153
188
  return false;
189
+ }
190
+
191
+ /** Guard retained references too, and discard results completed after revocation. */
192
+ export function guardToolAdapter(adapter) {
193
+ const guard = fn => (...args) => {
194
+ assertAccountAccess();
195
+ let revoked = false;
196
+ const off = subscribeAccountAccess(() => {
197
+ if (!hasAccountAccess()) revoked = true;
198
+ });
199
+ const check = () => {
200
+ if (revoked) throw new Error("Buoy account access ended while the operation was pending.");
201
+ assertAccountAccess();
202
+ };
203
+ try {
204
+ const result = fn.apply(adapter, args);
205
+ if (result && typeof result.then === "function") {
206
+ return Promise.resolve(result).then(value => {
207
+ check();
208
+ return value;
209
+ }).finally(off);
210
+ }
211
+ check();
212
+ off();
213
+ return result;
214
+ } catch (error) {
215
+ off();
216
+ throw error;
217
+ }
218
+ };
219
+ return {
220
+ ...adapter,
221
+ ...(adapter.getSnapshot ? {
222
+ getSnapshot: guard(adapter.getSnapshot)
223
+ } : {}),
224
+ ...(adapter.actions ? {
225
+ actions: Object.fromEntries(Object.entries(adapter.actions).map(([name, action]) => [name, guard(action)]))
226
+ } : {}),
227
+ ...(adapter.subscribe ? {
228
+ subscribe: listener => {
229
+ assertAccountAccess();
230
+ return adapter.subscribe(() => {
231
+ if (hasAccountAccess()) listener();
232
+ });
233
+ }
234
+ } : {})
235
+ };
154
236
  }