@witchcraft/spellcraft 0.0.1

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 (367) hide show
  1. package/README.md +579 -0
  2. package/dist/core/EmulatedEvent.d.ts +10 -0
  3. package/dist/core/EmulatedEvent.js +19 -0
  4. package/dist/core/Emulator.d.ts +74 -0
  5. package/dist/core/Emulator.js +153 -0
  6. package/dist/core/ShortcutManagerManager.d.ts +103 -0
  7. package/dist/core/ShortcutManagerManager.js +267 -0
  8. package/dist/core/addCommand.d.ts +7 -0
  9. package/dist/core/addCommand.js +7 -0
  10. package/dist/core/addKey.d.ts +7 -0
  11. package/dist/core/addKey.js +7 -0
  12. package/dist/core/addShortcut.d.ts +7 -0
  13. package/dist/core/addShortcut.js +7 -0
  14. package/dist/core/attach.d.ts +10 -0
  15. package/dist/core/attach.js +13 -0
  16. package/dist/core/createCommand.d.ts +4 -0
  17. package/dist/core/createCommand.js +11 -0
  18. package/dist/core/createCommands.d.ts +11 -0
  19. package/dist/core/createCommands.js +20 -0
  20. package/dist/core/createCondition.d.ts +7 -0
  21. package/dist/core/createCondition.js +11 -0
  22. package/dist/core/createContext.d.ts +2 -0
  23. package/dist/core/createContext.js +8 -0
  24. package/dist/core/createKey.d.ts +10 -0
  25. package/dist/core/createKey.js +35 -0
  26. package/dist/core/createKeys.d.ts +5 -0
  27. package/dist/core/createKeys.js +36 -0
  28. package/dist/core/createManager.d.ts +81 -0
  29. package/dist/core/createManager.js +83 -0
  30. package/dist/core/createManagerEventListeners.d.ts +4 -0
  31. package/dist/core/createManagerEventListeners.js +130 -0
  32. package/dist/core/createManagerOptions.d.ts +8 -0
  33. package/dist/core/createManagerOptions.js +17 -0
  34. package/dist/core/createShortcut.d.ts +8 -0
  35. package/dist/core/createShortcut.js +24 -0
  36. package/dist/core/createShortcuts.d.ts +13 -0
  37. package/dist/core/createShortcuts.js +20 -0
  38. package/dist/core/detach.d.ts +10 -0
  39. package/dist/core/detach.js +6 -0
  40. package/dist/core/index.d.ts +32 -0
  41. package/dist/core/index.js +32 -0
  42. package/dist/core/removeCommand.d.ts +7 -0
  43. package/dist/core/removeCommand.js +7 -0
  44. package/dist/core/removeKey.d.ts +7 -0
  45. package/dist/core/removeKey.js +7 -0
  46. package/dist/core/removeShortcut.d.ts +7 -0
  47. package/dist/core/removeShortcut.js +7 -0
  48. package/dist/core/setCommandProp.d.ts +17 -0
  49. package/dist/core/setCommandProp.js +96 -0
  50. package/dist/core/setCommandsProp.d.ts +15 -0
  51. package/dist/core/setCommandsProp.js +94 -0
  52. package/dist/core/setKeyProp.d.ts +16 -0
  53. package/dist/core/setKeyProp.js +47 -0
  54. package/dist/core/setKeysProp.d.ts +10 -0
  55. package/dist/core/setKeysProp.js +166 -0
  56. package/dist/core/setManagerProp.d.ts +34 -0
  57. package/dist/core/setManagerProp.js +54 -0
  58. package/dist/core/setShortcutProp.d.ts +9 -0
  59. package/dist/core/setShortcutProp.js +50 -0
  60. package/dist/core/setShortcutsProp.d.ts +12 -0
  61. package/dist/core/setShortcutsProp.js +93 -0
  62. package/dist/defaults/KeysSorter.d.ts +35 -0
  63. package/dist/defaults/KeysSorter.js +20 -0
  64. package/dist/defaults/Stringifier.d.ts +66 -0
  65. package/dist/defaults/Stringifier.js +119 -0
  66. package/dist/defaults/defaultConditionEquals.d.ts +19 -0
  67. package/dist/defaults/defaultConditionEquals.js +5 -0
  68. package/dist/defaults/defaultManagerCallback.d.ts +7 -0
  69. package/dist/defaults/defaultManagerCallback.js +5 -0
  70. package/dist/helpers/KnownError.d.ts +8 -0
  71. package/dist/helpers/KnownError.js +3 -0
  72. package/dist/helpers/calculateAndSetPositionAndWidth.d.ts +13 -0
  73. package/dist/helpers/calculateAndSetPositionAndWidth.js +18 -0
  74. package/dist/helpers/calculateLayoutSize.d.ts +9 -0
  75. package/dist/helpers/calculateLayoutSize.js +13 -0
  76. package/dist/helpers/doesShortcutConflict.d.ts +18 -0
  77. package/dist/helpers/doesShortcutConflict.js +42 -0
  78. package/dist/helpers/equalsCommand.d.ts +7 -0
  79. package/dist/helpers/equalsCommand.js +4 -0
  80. package/dist/helpers/equalsContext.d.ts +7 -0
  81. package/dist/helpers/equalsContext.js +19 -0
  82. package/dist/helpers/equalsShortcut.d.ts +9 -0
  83. package/dist/helpers/equalsShortcut.js +7 -0
  84. package/dist/helpers/forceClear.d.ts +12 -0
  85. package/dist/helpers/forceClear.js +16 -0
  86. package/dist/helpers/forceUpdateNativeKeysState.d.ts +5 -0
  87. package/dist/helpers/forceUpdateNativeKeysState.js +4 -0
  88. package/dist/helpers/generateKeyShortcutMap.d.ts +23 -0
  89. package/dist/helpers/generateKeyShortcutMap.js +64 -0
  90. package/dist/helpers/getKeyFromEventCode.d.ts +15 -0
  91. package/dist/helpers/getKeyFromEventCode.js +36 -0
  92. package/dist/helpers/getKeyFromIdOrVariant.d.ts +4 -0
  93. package/dist/helpers/getKeyFromIdOrVariant.js +26 -0
  94. package/dist/helpers/getKeyboardLayoutMap.d.ts +5 -0
  95. package/dist/helpers/getKeyboardLayoutMap.js +7 -0
  96. package/dist/helpers/getLabel.d.ts +9 -0
  97. package/dist/helpers/getLabel.js +6 -0
  98. package/dist/helpers/getTriggerableShortcut.d.ts +6 -0
  99. package/dist/helpers/getTriggerableShortcut.js +25 -0
  100. package/dist/helpers/index.d.ts +28 -0
  101. package/dist/helpers/index.js +28 -0
  102. package/dist/helpers/isValidManager.d.ts +3 -0
  103. package/dist/helpers/isValidManager.js +20 -0
  104. package/dist/helpers/isValidShortcut.d.ts +3 -0
  105. package/dist/helpers/isValidShortcut.js +10 -0
  106. package/dist/helpers/labelWithEvent.d.ts +13 -0
  107. package/dist/helpers/labelWithEvent.js +20 -0
  108. package/dist/helpers/labelWithKeyboardMap.d.ts +15 -0
  109. package/dist/helpers/labelWithKeyboardMap.js +17 -0
  110. package/dist/helpers/managerToStorableClone.d.ts +12 -0
  111. package/dist/helpers/managerToStorableClone.js +13 -0
  112. package/dist/helpers/onKeyboardLayoutChange.d.ts +10 -0
  113. package/dist/helpers/onKeyboardLayoutChange.js +7 -0
  114. package/dist/helpers/safeSetManagerChain.d.ts +20 -0
  115. package/dist/helpers/safeSetManagerChain.js +37 -0
  116. package/dist/helpers/shortcutCanExecuteIn.d.ts +4 -0
  117. package/dist/helpers/shortcutCanExecuteIn.js +9 -0
  118. package/dist/helpers/shortcutIsTriggerableBy.d.ts +2 -0
  119. package/dist/helpers/shortcutIsTriggerableBy.js +5 -0
  120. package/dist/helpers/shortcutSwapChords.d.ts +75 -0
  121. package/dist/helpers/shortcutSwapChords.js +118 -0
  122. package/dist/helpers/virtualPress.d.ts +13 -0
  123. package/dist/helpers/virtualPress.js +15 -0
  124. package/dist/helpers/virtualRelease.d.ts +5 -0
  125. package/dist/helpers/virtualRelease.js +15 -0
  126. package/dist/helpers/virtualToggle.d.ts +7 -0
  127. package/dist/helpers/virtualToggle.js +16 -0
  128. package/dist/internal/addToChain.d.ts +3 -0
  129. package/dist/internal/addToChain.js +27 -0
  130. package/dist/internal/areValidKeys.d.ts +7 -0
  131. package/dist/internal/areValidKeys.js +28 -0
  132. package/dist/internal/areValidVariants.d.ts +4 -0
  133. package/dist/internal/areValidVariants.js +45 -0
  134. package/dist/internal/checkTrigger.d.ts +2 -0
  135. package/dist/internal/checkTrigger.js +47 -0
  136. package/dist/internal/checkUntrigger.d.ts +2 -0
  137. package/dist/internal/checkUntrigger.js +18 -0
  138. package/dist/internal/cloneLastChord.d.ts +4 -0
  139. package/dist/internal/cloneLastChord.js +5 -0
  140. package/dist/internal/containsPossibleToggleChords.d.ts +13 -0
  141. package/dist/internal/containsPossibleToggleChords.js +65 -0
  142. package/dist/internal/errorTextAdd.d.ts +1 -0
  143. package/dist/internal/errorTextAdd.js +11 -0
  144. package/dist/internal/errorTextInUse.d.ts +1 -0
  145. package/dist/internal/errorTextInUse.js +8 -0
  146. package/dist/internal/errorTextRemove.d.ts +1 -0
  147. package/dist/internal/errorTextRemove.js +8 -0
  148. package/dist/internal/getModifierState.d.ts +2 -0
  149. package/dist/internal/getModifierState.js +7 -0
  150. package/dist/internal/getPressedKeys.d.ts +7 -0
  151. package/dist/internal/getPressedKeys.js +18 -0
  152. package/dist/internal/getPressedModifierKeys.d.ts +2 -0
  153. package/dist/internal/getPressedModifierKeys.js +10 -0
  154. package/dist/internal/getPressedNonModifierKeys.d.ts +7 -0
  155. package/dist/internal/getPressedNonModifierKeys.js +11 -0
  156. package/dist/internal/inChain.d.ts +5 -0
  157. package/dist/internal/inChain.js +14 -0
  158. package/dist/internal/isValidChain.d.ts +10 -0
  159. package/dist/internal/isValidChain.js +22 -0
  160. package/dist/internal/isValidChord.d.ts +11 -0
  161. package/dist/internal/isValidChord.js +59 -0
  162. package/dist/internal/isValidCommand.d.ts +12 -0
  163. package/dist/internal/isValidCommand.js +20 -0
  164. package/dist/internal/keyOrder.d.ts +6 -0
  165. package/dist/internal/keyOrder.js +14 -0
  166. package/dist/internal/removeFromChain.d.ts +3 -0
  167. package/dist/internal/removeFromChain.js +32 -0
  168. package/dist/internal/safeSetEmulatedToggleState.d.ts +4 -0
  169. package/dist/internal/safeSetEmulatedToggleState.js +13 -0
  170. package/dist/internal/setKeysState.d.ts +8 -0
  171. package/dist/internal/setKeysState.js +42 -0
  172. package/dist/internal/updateNativeKeysState.d.ts +14 -0
  173. package/dist/internal/updateNativeKeysState.js +58 -0
  174. package/dist/layouts/createLayout.d.ts +25 -0
  175. package/dist/layouts/createLayout.js +221 -0
  176. package/dist/module.d.mts +13 -0
  177. package/dist/module.json +9 -0
  178. package/dist/module.mjs +40 -0
  179. package/dist/runtime/composables/useLabeledByKeyboardLayoutMap.d.ts +9 -0
  180. package/dist/runtime/composables/useLabeledByKeyboardLayoutMap.js +19 -0
  181. package/dist/runtime/composables/usePointerCoords.d.ts +32 -0
  182. package/dist/runtime/composables/usePointerCoords.js +17 -0
  183. package/dist/runtime/composables/useShortcutManagerContextCount.d.ts +14 -0
  184. package/dist/runtime/composables/useShortcutManagerContextCount.js +61 -0
  185. package/dist/runtime/composables/useShortcutManagerKeysLayout.d.ts +18 -0
  186. package/dist/runtime/composables/useShortcutManagerKeysLayout.js +22 -0
  187. package/dist/runtime/composables/useShortcutManagerVirtualPress.d.ts +6 -0
  188. package/dist/runtime/composables/useShortcutManagerVirtualPress.js +24 -0
  189. package/dist/runtime/types.d.ts +10 -0
  190. package/dist/runtime/types.js +1 -0
  191. package/dist/runtime/utils/shortcutToId.d.ts +5 -0
  192. package/dist/runtime/utils/shortcutToId.js +6 -0
  193. package/dist/types/commands.d.ts +113 -0
  194. package/dist/types/commands.js +0 -0
  195. package/dist/types/condition.d.ts +29 -0
  196. package/dist/types/condition.js +0 -0
  197. package/dist/types/context.d.ts +18 -0
  198. package/dist/types/context.js +0 -0
  199. package/dist/types/enums.d.ts +186 -0
  200. package/dist/types/enums.js +70 -0
  201. package/dist/types/general.d.ts +92 -0
  202. package/dist/types/general.js +0 -0
  203. package/dist/types/index.d.ts +8 -0
  204. package/dist/types/index.js +8 -0
  205. package/dist/types/keys.d.ts +332 -0
  206. package/dist/types/keys.js +0 -0
  207. package/dist/types/manager.d.ts +249 -0
  208. package/dist/types/manager.js +0 -0
  209. package/dist/types/plugins.d.ts +1 -0
  210. package/dist/types/plugins.js +0 -0
  211. package/dist/types/shortcuts.d.ts +144 -0
  212. package/dist/types/shortcuts.js +0 -0
  213. package/dist/types/utils.d.ts +1 -0
  214. package/dist/types/utils.js +0 -0
  215. package/dist/types.d.mts +3 -0
  216. package/dist/utils/chainContainsSubset.d.ts +27 -0
  217. package/dist/utils/chainContainsSubset.js +45 -0
  218. package/dist/utils/cloneChain.d.ts +2 -0
  219. package/dist/utils/cloneChain.js +11 -0
  220. package/dist/utils/cloneKey.d.ts +3 -0
  221. package/dist/utils/cloneKey.js +26 -0
  222. package/dist/utils/containsKey.d.ts +7 -0
  223. package/dist/utils/containsKey.js +4 -0
  224. package/dist/utils/dedupeKeys.d.ts +9 -0
  225. package/dist/utils/dedupeKeys.js +9 -0
  226. package/dist/utils/equalsKey.d.ts +12 -0
  227. package/dist/utils/equalsKey.js +13 -0
  228. package/dist/utils/equalsKeys.d.ts +24 -0
  229. package/dist/utils/equalsKeys.js +15 -0
  230. package/dist/utils/index.d.ts +14 -0
  231. package/dist/utils/index.js +14 -0
  232. package/dist/utils/isAnyKey.d.ts +5 -0
  233. package/dist/utils/isAnyKey.js +5 -0
  234. package/dist/utils/isMouseKey.d.ts +5 -0
  235. package/dist/utils/isMouseKey.js +20 -0
  236. package/dist/utils/isNormalKey.d.ts +5 -0
  237. package/dist/utils/isNormalKey.js +5 -0
  238. package/dist/utils/isTriggerKey.d.ts +5 -0
  239. package/dist/utils/isTriggerKey.js +3 -0
  240. package/dist/utils/isWheelKey.d.ts +5 -0
  241. package/dist/utils/isWheelKey.js +11 -0
  242. package/dist/utils/mapKeys.d.ts +8 -0
  243. package/dist/utils/mapKeys.js +5 -0
  244. package/dist/utils/removeKeys.d.ts +7 -0
  245. package/dist/utils/removeKeys.js +4 -0
  246. package/package.json +156 -0
  247. package/src/core/EmulatedEvent.ts +29 -0
  248. package/src/core/Emulator.ts +185 -0
  249. package/src/core/ShortcutManagerManager.ts +380 -0
  250. package/src/core/addCommand.ts +24 -0
  251. package/src/core/addKey.ts +25 -0
  252. package/src/core/addShortcut.ts +24 -0
  253. package/src/core/attach.ts +27 -0
  254. package/src/core/createCommand.ts +24 -0
  255. package/src/core/createCommands.ts +45 -0
  256. package/src/core/createCondition.ts +21 -0
  257. package/src/core/createContext.ts +14 -0
  258. package/src/core/createKey.ts +59 -0
  259. package/src/core/createKeys.ts +68 -0
  260. package/src/core/createManager.ts +209 -0
  261. package/src/core/createManagerEventListeners.ts +139 -0
  262. package/src/core/createManagerOptions.ts +29 -0
  263. package/src/core/createShortcut.ts +49 -0
  264. package/src/core/createShortcuts.ts +51 -0
  265. package/src/core/detach.ts +21 -0
  266. package/src/core/index.ts +35 -0
  267. package/src/core/removeCommand.ts +25 -0
  268. package/src/core/removeKey.ts +25 -0
  269. package/src/core/removeShortcut.ts +24 -0
  270. package/src/core/setCommandProp.ts +140 -0
  271. package/src/core/setCommandsProp.ts +128 -0
  272. package/src/core/setKeyProp.ts +80 -0
  273. package/src/core/setKeysProp.ts +205 -0
  274. package/src/core/setManagerProp.ts +111 -0
  275. package/src/core/setShortcutProp.ts +89 -0
  276. package/src/core/setShortcutsProp.ts +124 -0
  277. package/src/defaults/KeysSorter.ts +55 -0
  278. package/src/defaults/Stringifier.ts +234 -0
  279. package/src/defaults/defaultConditionEquals.ts +29 -0
  280. package/src/defaults/defaultManagerCallback.ts +14 -0
  281. package/src/helpers/KnownError.ts +13 -0
  282. package/src/helpers/calculateAndSetPositionAndWidth.ts +30 -0
  283. package/src/helpers/calculateLayoutSize.ts +22 -0
  284. package/src/helpers/doesShortcutConflict.ts +72 -0
  285. package/src/helpers/equalsCommand.ts +18 -0
  286. package/src/helpers/equalsContext.ts +29 -0
  287. package/src/helpers/equalsShortcut.ts +34 -0
  288. package/src/helpers/forceClear.ts +31 -0
  289. package/src/helpers/forceUpdateNativeKeysState.ts +9 -0
  290. package/src/helpers/generateKeyShortcutMap.ts +113 -0
  291. package/src/helpers/getKeyFromEventCode.ts +67 -0
  292. package/src/helpers/getKeyFromIdOrVariant.ts +33 -0
  293. package/src/helpers/getKeyboardLayoutMap.ts +13 -0
  294. package/src/helpers/getLabel.ts +15 -0
  295. package/src/helpers/getTriggerableShortcut.ts +37 -0
  296. package/src/helpers/index.ts +30 -0
  297. package/src/helpers/isValidManager.ts +29 -0
  298. package/src/helpers/isValidShortcut.ts +20 -0
  299. package/src/helpers/labelWithEvent.ts +41 -0
  300. package/src/helpers/labelWithKeyboardMap.ts +37 -0
  301. package/src/helpers/managerToStorableClone.ts +26 -0
  302. package/src/helpers/onKeyboardLayoutChange.ts +17 -0
  303. package/src/helpers/safeSetManagerChain.ts +66 -0
  304. package/src/helpers/shortcutCanExecuteIn.ts +24 -0
  305. package/src/helpers/shortcutIsTriggerableBy.ts +15 -0
  306. package/src/helpers/shortcutSwapChords.ts +240 -0
  307. package/src/helpers/virtualPress.ts +34 -0
  308. package/src/helpers/virtualRelease.ts +25 -0
  309. package/src/helpers/virtualToggle.ts +28 -0
  310. package/src/internal/addToChain.ts +40 -0
  311. package/src/internal/areValidKeys.ts +40 -0
  312. package/src/internal/areValidVariants.ts +59 -0
  313. package/src/internal/checkTrigger.ts +55 -0
  314. package/src/internal/checkUntrigger.ts +26 -0
  315. package/src/internal/cloneLastChord.ts +10 -0
  316. package/src/internal/containsPossibleToggleChords.ts +91 -0
  317. package/src/internal/errorTextAdd.ts +18 -0
  318. package/src/internal/errorTextInUse.ts +10 -0
  319. package/src/internal/errorTextRemove.ts +10 -0
  320. package/src/internal/getModifierState.ts +15 -0
  321. package/src/internal/getPressedKeys.ts +26 -0
  322. package/src/internal/getPressedModifierKeys.ts +14 -0
  323. package/src/internal/getPressedNonModifierKeys.ts +19 -0
  324. package/src/internal/inChain.ts +25 -0
  325. package/src/internal/isValidChain.ts +42 -0
  326. package/src/internal/isValidChord.ts +87 -0
  327. package/src/internal/isValidCommand.ts +35 -0
  328. package/src/internal/keyOrder.ts +24 -0
  329. package/src/internal/removeFromChain.ts +46 -0
  330. package/src/internal/safeSetEmulatedToggleState.ts +23 -0
  331. package/src/internal/setKeysState.ts +71 -0
  332. package/src/internal/updateNativeKeysState.ts +84 -0
  333. package/src/layouts/createLayout.ts +328 -0
  334. package/src/module.ts +62 -0
  335. package/src/runtime/composables/useLabeledByKeyboardLayoutMap.ts +28 -0
  336. package/src/runtime/composables/usePointerCoords.ts +40 -0
  337. package/src/runtime/composables/useShortcutManagerContextCount.ts +81 -0
  338. package/src/runtime/composables/useShortcutManagerKeysLayout.ts +42 -0
  339. package/src/runtime/composables/useShortcutManagerVirtualPress.ts +30 -0
  340. package/src/runtime/types.ts +10 -0
  341. package/src/runtime/utils/shortcutToId.ts +14 -0
  342. package/src/types/commands.ts +148 -0
  343. package/src/types/condition.ts +35 -0
  344. package/src/types/context.ts +19 -0
  345. package/src/types/enums.ts +236 -0
  346. package/src/types/general.ts +117 -0
  347. package/src/types/index.ts +10 -0
  348. package/src/types/keys.ts +385 -0
  349. package/src/types/manager.ts +374 -0
  350. package/src/types/plugins.ts +32 -0
  351. package/src/types/shortcuts.ts +204 -0
  352. package/src/types/utils.ts +40 -0
  353. package/src/utils/chainContainsSubset.ts +97 -0
  354. package/src/utils/cloneChain.ts +13 -0
  355. package/src/utils/cloneKey.ts +33 -0
  356. package/src/utils/containsKey.ts +17 -0
  357. package/src/utils/dedupeKeys.ts +23 -0
  358. package/src/utils/equalsKey.ts +32 -0
  359. package/src/utils/equalsKeys.ts +50 -0
  360. package/src/utils/index.ts +16 -0
  361. package/src/utils/isAnyKey.ts +12 -0
  362. package/src/utils/isMouseKey.ts +27 -0
  363. package/src/utils/isNormalKey.ts +15 -0
  364. package/src/utils/isTriggerKey.ts +7 -0
  365. package/src/utils/isWheelKey.ts +18 -0
  366. package/src/utils/mapKeys.ts +21 -0
  367. package/src/utils/removeKeys.ts +16 -0
@@ -0,0 +1,140 @@
1
+ import { castType } from "@alanscodelog/utils/castType"
2
+ import { crop } from "@alanscodelog/utils/crop"
3
+ import { indent } from "@alanscodelog/utils/indent"
4
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
5
+
6
+ import { setCommandsProp } from "./setCommandsProp.js"
7
+ import { setShortcutProp } from "./setShortcutProp.js"
8
+
9
+ import { KnownError } from "../helpers/KnownError.js"
10
+ import { errorTextAdd } from "../internal/errorTextAdd.js"
11
+ import { type CanHookCommandProps, type CanHookErrors, type Command, type CommandSetEntries, type Manager, type MultipleErrors, SHORTCUT_ERROR } from "../types/index.js"
12
+
13
+
14
+ const canHookable: CanHookCommandProps[] = ["condition", "execute"]
15
+
16
+ /**
17
+ * Sets a settable command property.
18
+ *
19
+ * You should not use this to set key state the manager manages (those tagged with @Managed in the docs) unless you've forgone using the manager.
20
+ *
21
+ * Note that while the manager argument is always required, for unmanaged properties you can pass {} and for most others you can pass a partial manager if needed. This is because it's very difficult to keep the heavy per prop types and allow the manager to be optional in these cases.
22
+ *
23
+ */
24
+ export function setCommandProp<
25
+ TEntries extends CommandSetEntries,
26
+ TProp extends keyof CommandSetEntries,
27
+ TEntry extends TEntries[TProp],
28
+ THooks extends Manager["hooks"],
29
+ TCheck extends boolean | "only" = true
30
+ >(
31
+ /** Command is mutated if check is not "only". */
32
+ command: Command,
33
+ prop: TProp,
34
+ val: TEntry["val"],
35
+ manager: (TEntry["manager"] extends never ? unknown : TEntry["manager"]) & { hooks?: THooks },
36
+ {
37
+ check = true as TCheck
38
+ }: { check?: TCheck } = {}
39
+ ): Result<
40
+ TCheck extends "only" ? true : Command,
41
+ MultipleErrors<TEntry["error"]>
42
+ | CanHookErrors<Manager["hooks"] extends never ? never : THooks, "canSetCommandProp">
43
+ > {
44
+ if (check) {
45
+ switch (prop) {
46
+ case "name": {
47
+ castType<TEntries["name"]["manager"]>(manager)
48
+ castType<TEntries["name"]["val"]>(val)
49
+ const s = manager.options.stringifier
50
+ const existing = manager.commands.entries[val as string]
51
+ if (existing) {
52
+ return Err(new KnownError(
53
+ SHORTCUT_ERROR.DUPLICATE_COMMAND,
54
+ errorTextAdd(
55
+ "Command",
56
+ val as string,
57
+ s.stringify(existing),
58
+ s.stringify(command)
59
+ ),
60
+ { existing, self: manager.commands }
61
+ ))
62
+ }
63
+ // note that the old command being missing is NOT an error, see Command.name
64
+
65
+ // we artificially add the command so we don't get errors that it's unknown
66
+ const managerClone = {
67
+ ...manager,
68
+ commands: {
69
+ ...manager.commands,
70
+ entries: {
71
+ ...manager.commands.entries,
72
+ [val]: { ...command, name: val }
73
+ }
74
+ }
75
+ }
76
+
77
+ const shortcutErrors = []
78
+ for (const shortcut of manager.shortcuts.entries) {
79
+ if (shortcut.command === command.name) {
80
+ // we should only receive hook errors
81
+ const res = setShortcutProp(shortcut, "command", val, managerClone, { check: "only" })
82
+ if (res.isError) shortcutErrors.push(res.error)
83
+ }
84
+ }
85
+ if (shortcutErrors.length > 0) {
86
+ return Err(new KnownError(
87
+ SHORTCUT_ERROR.MULTIPLE_ERRORS,
88
+ crop`
89
+ Received multiple errors attempting to change command name from ${command.name} to ${val}:
90
+ ${indent(shortcutErrors.map(_ => _.message).join("\n"), 7)}
91
+ `,
92
+ { errors: shortcutErrors }
93
+ ))
94
+ }
95
+ break
96
+ }
97
+
98
+ default: break
99
+ }
100
+
101
+ if (manager?.hooks && "canSetCommandProp" in manager.hooks && canHookable.includes(prop as any)) {
102
+ const canHook = manager.hooks.canSetCommandProp?.(command, prop as any, val)
103
+ if (canHook instanceof Error) {
104
+ return Err(canHook) as any
105
+ }
106
+ }
107
+ }
108
+
109
+ if (check === "only") {
110
+ return Ok(true) satisfies Result<true, never> as any
111
+ }
112
+
113
+
114
+ if (prop === "name") {
115
+ castType<TEntries["name"]["manager"]>(manager)
116
+ castType<TEntries["name"]["val"]>(val)
117
+
118
+
119
+ setCommandsProp("entries@add", { ...command, name: val }, manager).unwrap()
120
+ const old = command.name
121
+ const oldCommand = manager.commands.entries[old]
122
+ for (const shortcut of manager.shortcuts.entries) {
123
+ if (shortcut.command === old) {
124
+ setShortcutProp(shortcut, "command", val, manager).unwrap()
125
+ }
126
+ }
127
+ // only remove if it existed
128
+ if (manager.commands.entries[old]) {
129
+ setCommandsProp("entries@remove", oldCommand, manager).unwrap()
130
+ }
131
+ } else {
132
+ (command as any)[prop] = val
133
+ }
134
+ if (manager?.hooks && "onSetCommandProp" in manager.hooks!) {
135
+ manager?.hooks?.onSetCommandProp?.(command, prop as any, val)
136
+ }
137
+
138
+ return Ok(command) satisfies Result<Command, never> as any
139
+ }
140
+
@@ -0,0 +1,128 @@
1
+ import { castType } from "@alanscodelog/utils/castType"
2
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
3
+
4
+ import { KnownError } from "../helpers/KnownError.js"
5
+ import { errorTextAdd } from "../internal/errorTextAdd.js"
6
+ import { errorTextInUse } from "../internal/errorTextInUse.js"
7
+ import { errorTextRemove } from "../internal/errorTextRemove.js"
8
+ import { type CanHookCommandsProps, type CanHookErrors, type Command, type Commands, type CommandsSetEntries, type Manager, type MultipleErrors, SHORTCUT_ERROR } from "../types/index.js"
9
+
10
+
11
+ const canHookable: CanHookCommandsProps[] = ["entries@add", "entries@remove"]
12
+
13
+
14
+ /**
15
+ * Sets a settable command property.
16
+ *
17
+ * You should not use this to set command state the manager manages (those tagged with @Managed in the docs) unless you've forgone using the manager.
18
+ *
19
+ */
20
+ export function setCommandsProp<
21
+ TEntries extends CommandsSetEntries,
22
+ TProp extends keyof CommandsSetEntries,
23
+ TEntry extends TEntries[TProp],
24
+ THooks extends Manager["hooks"],
25
+ TCheck extends boolean | "only" = true
26
+ >(
27
+ prop: TProp,
28
+ val: TEntry["val"],
29
+ /** Commands is mutated if check is not "only". */
30
+ manager: TEntry["manager"] & { hooks?: THooks },
31
+ {
32
+ check = true as TCheck
33
+ }: { check?: TCheck } = {}
34
+ ): Result<
35
+ TCheck extends "only" ? true : Command,
36
+ MultipleErrors<TEntry["error"]>
37
+ | CanHookErrors<Manager["hooks"] extends never ? never : THooks, "canSetCommandsProp">
38
+ > {
39
+ const commands = manager.commands
40
+ const s = manager.options.stringifier
41
+ if (check) {
42
+ switch (prop) {
43
+ case "entries@add": {
44
+ castType<TEntries["entries@add"]["val"]>(val)
45
+ castType<TEntries["entries@add"]["manager"]>(manager)
46
+ const command = val
47
+ const existing = commands.entries[command.name]
48
+
49
+ if (existing) {
50
+ return Err(new KnownError(
51
+ SHORTCUT_ERROR.DUPLICATE_COMMAND,
52
+ errorTextAdd(
53
+ "Command",
54
+ s.stringifyCommand(existing.name, manager),
55
+ s.stringifyCommand(existing.name, manager),
56
+ s.stringifyCommand(command.name, manager)
57
+ ),
58
+ { existing, self: commands }
59
+ ))
60
+ }
61
+
62
+ break
63
+ }
64
+ case "entries@remove": {
65
+ castType<TEntries["entries@remove"]["val"]>(val)
66
+ castType<TEntries["entries@remove"]["manager"]>(manager)
67
+
68
+ const command = val
69
+ if (!commands.entries[command.name]) {
70
+ return Err(new KnownError(
71
+ SHORTCUT_ERROR.MISSING,
72
+ errorTextRemove(
73
+ "Command",
74
+ s.stringify(command),
75
+ s.stringifyList("commands", Object.values(commands.entries))
76
+ ),
77
+ { entry: command.name, self: commands }
78
+ ))
79
+ }
80
+
81
+ const inUseShortcuts = manager.shortcuts.entries.filter(shortcut => shortcut.command === command.name)
82
+ if (inUseShortcuts.length > 0) {
83
+ return Err(new KnownError(
84
+ SHORTCUT_ERROR.COMMAND_IN_USE,
85
+ errorTextInUse(
86
+ "command",
87
+ s.stringify(command),
88
+ s.stringifyList("shortcuts", inUseShortcuts, manager)
89
+ ),
90
+ { inUseShortcuts }))
91
+ }
92
+ break
93
+ }
94
+ default: break
95
+ }
96
+ if (manager?.hooks && "canSetCommandsProp" in manager.hooks && canHookable.includes(prop as any)) {
97
+ const canHook = manager.hooks?.canSetCommandsProp?.(commands, prop as any, val as any)
98
+ if (canHook instanceof Error) {
99
+ return Err(canHook) as any
100
+ }
101
+ }
102
+ }
103
+
104
+ if (check === "only") {
105
+ return Ok(true) satisfies Result<true, never> as any
106
+ }
107
+
108
+ switch (prop) {
109
+ case "entries@add": {
110
+ const command = val as any as Command
111
+ commands.entries[command.name] = command
112
+ break
113
+ }
114
+ case "entries@remove": {
115
+ const command = val as any as Command
116
+ delete commands.entries[command.name]
117
+ break
118
+ }
119
+ default:
120
+ (commands as any)[prop] = val
121
+ break
122
+ }
123
+
124
+ manager.hooks?.onSetCommandsProp?.(commands, prop as any, val as any)
125
+
126
+ return Ok(commands) satisfies Result<Commands, never> as any
127
+ }
128
+
@@ -0,0 +1,80 @@
1
+ import { castType } from "@alanscodelog/utils/castType"
2
+ import { crop } from "@alanscodelog/utils/crop"
3
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
4
+ import { set } from "@alanscodelog/utils/set"
5
+
6
+ import { KnownError } from "../helpers/KnownError.js"
7
+ import type { CanHookErrors, Key, KeySetEntries, Manager, MultipleErrors, OnHookKeyProps } from "../types/index.js"
8
+ import { SHORTCUT_ERROR } from "../types/index.js"
9
+
10
+
11
+ const canHookable: OnHookKeyProps[] = ["x", "y", "width", "height", "label", "enabled", "render", "classes"]
12
+ /**
13
+ * Sets a settable key property.
14
+ *
15
+ * You should not use this to set key state the manager manages (those tagged with @Managed in the docs) unless you've forgone using the manager.
16
+ *
17
+ * Note that while the manager argument is always required, for unmanaged properties you can pass {} and for most others you can pass a partial manager if needed. This is because it's very difficult to keep the heavy per prop types and allow the manager to be optional in these cases.
18
+ */
19
+ export function setKeyProp<
20
+ TEntries extends KeySetEntries,
21
+ TProp extends keyof KeySetEntries,
22
+ TEntry extends TEntries[TProp],
23
+ THooks extends Manager["hooks"],
24
+ TCheck extends boolean | "only" = true
25
+ >(
26
+ /** Key is mutated if check is not "only". */
27
+ key: Key,
28
+ prop: TProp,
29
+ val: TEntry["val"],
30
+ manager: (TEntry["manager"] extends never ? unknown : TEntry["manager"]) & { hooks?: THooks },
31
+ {
32
+ check = true as TCheck
33
+ }: { check?: TCheck } = {}
34
+ ): Result<
35
+ TCheck extends "only" ? true : Key,
36
+ MultipleErrors<TEntry["error"]>
37
+ | CanHookErrors<THooks extends never ? never : THooks, "canSetKeyProp">
38
+ > {
39
+ if (check) {
40
+ switch (prop) {
41
+ case "toggleOnPressed":
42
+ case "toggleOffPressed":
43
+ case "pressed": {
44
+ castType<TEntries["pressed"]["manager"]>(manager)
45
+ const s = manager.options.stringifier
46
+ if (!key.enabled && val === true) {
47
+ return Err(
48
+ new KnownError(SHORTCUT_ERROR.CANNOT_SET_WHILE_DISABLED, crop`
49
+ The "${prop}" property cannot be set to true while a key is disabled. (Key: ${s.stringify(key, manager)})
50
+ `, { key })
51
+ )
52
+ }
53
+ break
54
+ }
55
+ default: break
56
+ }
57
+
58
+ if (manager?.hooks && "canSetKeyProp" in manager.hooks && canHookable.includes(prop as any)) {
59
+ const canHook = manager.hooks.canSetKeyProp?.(key, prop as any, val as any)
60
+ if (canHook instanceof Error) {
61
+ return Err(canHook) as any
62
+ }
63
+ }
64
+ }
65
+
66
+ if (check === "only") {
67
+ return Ok(true) satisfies Result<true, never> as any
68
+ }
69
+
70
+ if (prop.includes(".")) {
71
+ set(key, prop.split("."), val)
72
+ } else {
73
+ (key as any)[prop] = val
74
+ }
75
+
76
+ manager?.hooks?.onSetKeyProp?.(key, prop, val)
77
+
78
+ return Ok(key) satisfies Result<Key, never> as any
79
+ }
80
+
@@ -0,0 +1,205 @@
1
+ import { castType } from "@alanscodelog/utils/castType"
2
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
3
+
4
+ import { calculateLayoutSize } from "../helpers/calculateLayoutSize.js"
5
+ import { KnownError } from "../helpers/KnownError.js"
6
+ import { areValidVariants } from "../internal/areValidVariants.js"
7
+ import { errorTextAdd } from "../internal/errorTextAdd.js"
8
+ import { errorTextInUse } from "../internal/errorTextInUse.js"
9
+ import { errorTextRemove } from "../internal/errorTextRemove.js"
10
+ import { type CanHookErrors, type CanHookKeysProps, type Key, type Keys, type KeysSetEntries, type Manager, type MultipleErrors, SHORTCUT_ERROR } from "../types/index.js"
11
+ import { containsKey } from "../utils/containsKey.js"
12
+
13
+
14
+ const canHookable: CanHookKeysProps[] = ["entries@add", "entries@remove"]
15
+
16
+ /**
17
+ * Sets a settable {@link Keys} property.
18
+ */
19
+ export function setKeysProp<
20
+ TEntries extends KeysSetEntries,
21
+ TProp extends keyof KeysSetEntries,
22
+ TEntry extends TEntries[TProp],
23
+ THooks extends Manager["hooks"],
24
+ TCheck extends boolean | "only" = true
25
+ >(
26
+ prop: TProp,
27
+ val: TEntry["val"],
28
+ manager: TEntry["manager"] & { hooks?: THooks },
29
+ {
30
+ check = true as TCheck
31
+ }: { check?: TCheck } = {}
32
+ ): Result<
33
+ TCheck extends "only" ? true : Key,
34
+ MultipleErrors<TEntry["error"]>
35
+ | CanHookErrors<Manager["hooks"] extends never ? never : THooks, "canSetKeysProp">
36
+ > {
37
+ const keys = manager.keys
38
+ if (check) {
39
+ switch (prop) {
40
+ case "entries@add": {
41
+ castType<TEntries["entries@add"]["val"]>(val)
42
+ castType<TEntries["entries@add"]["manager"]>(manager)
43
+
44
+ const s = manager.options.stringifier
45
+ const key = val
46
+ // we only need to check .entries at this point
47
+ const existing = keys.entries[key.id]
48
+
49
+ if (existing) {
50
+ return Err(new KnownError(
51
+ SHORTCUT_ERROR.DUPLICATE_KEY,
52
+ errorTextAdd(
53
+ "Key",
54
+ s.stringify(existing, manager),
55
+ s.stringify(existing, manager),
56
+ s.stringify(key, manager)
57
+ ),
58
+ { existing, self: keys }
59
+ ))
60
+ }
61
+ const areValidVariantsRes = areValidVariants(key, manager)
62
+ if (areValidVariantsRes.isError) return areValidVariantsRes
63
+
64
+ break
65
+ }
66
+ case "entries@remove": {
67
+ castType<TEntries["entries@remove"]["val"]>(val)
68
+ castType<TEntries["entries@remove"]["manager"]>(manager)
69
+ const s = manager.options.stringifier
70
+
71
+ const key = val
72
+ if (!keys.entries[key.id]) {
73
+ return Err(new KnownError(
74
+ SHORTCUT_ERROR.MISSING,
75
+ errorTextRemove(
76
+ "Key",
77
+ s.stringify(key),
78
+ s.stringifyList("keys", Object.values(keys.entries))
79
+ ),
80
+ { entry: key.id, self: keys }
81
+ )) as any
82
+ }
83
+
84
+ const inUseShortcuts = manager.shortcuts.entries.filter(shortcut => containsKey(shortcut.chain, key.id, keys))
85
+
86
+ if (inUseShortcuts.length > 0) {
87
+ return Err(new KnownError(
88
+ SHORTCUT_ERROR.KEY_IN_USE,
89
+ errorTextInUse(
90
+ "key",
91
+ s.stringify(key),
92
+ s.stringifyList("shortcuts", inUseShortcuts, manager)),
93
+
94
+ { inUseShortcuts }))
95
+ }
96
+
97
+ break
98
+ }
99
+ default: break
100
+ }
101
+ if (manager.hooks && "canSetKeysProp" in manager.hooks && canHookable.includes(prop as any)) {
102
+ const canHook = manager.hooks.canSetKeysProp?.(keys, prop as any, val as any)
103
+ if (canHook instanceof Error) {
104
+ return Err(canHook) as any
105
+ }
106
+ }
107
+ }
108
+
109
+ if (check === "only") {
110
+ return Ok(true) satisfies Result<true, never> as any
111
+ }
112
+
113
+ switch (prop) {
114
+ case "entries@add": {
115
+ castType<TEntries["entries@add"]["val"]>(val)
116
+ castType<TEntries["entries@add"]["manager"]>(manager)
117
+
118
+ const key = val
119
+ keys.entries[key.id] = key
120
+ if (key.isToggle === "native") {
121
+ setKeysProp("nativeToggleKeys", [
122
+ ...keys.nativeToggleKeys,
123
+ key.id
124
+ ], manager).unwrap()
125
+ }
126
+ if (key.isModifier === "native") {
127
+ setKeysProp("nativeModifierKeys", [
128
+ ...keys.nativeModifierKeys,
129
+ key.id
130
+ ], manager).unwrap()
131
+ }
132
+ if (key.isToggle) {
133
+ setKeysProp(`toggles@add@${key.toggleOnId}`, key, manager).unwrap()
134
+ setKeysProp(`toggles@add@${key.toggleOffId}`, key, manager).unwrap()
135
+ }
136
+ if (key.variants) {
137
+ for (const variant of key.variants) {
138
+ setKeysProp(`variants@add@${variant}`, key.id, manager).unwrap()
139
+ }
140
+ }
141
+ if (keys.autoManageLayout) {
142
+ setKeysProp("layout", calculateLayoutSize(keys), manager).unwrap()
143
+ }
144
+ break
145
+ }
146
+ case "entries@remove": {
147
+ const key = val as any as Key
148
+ delete keys.entries[key.id]
149
+ if (key.isToggle === "native") {
150
+ setKeysProp("nativeToggleKeys", keys.nativeToggleKeys.filter(k => k !== key.id), manager).unwrap()
151
+ }
152
+ if (key.isModifier === "native") {
153
+ setKeysProp("nativeModifierKeys", keys.nativeToggleKeys.filter(k => k !== key.id), manager).unwrap()
154
+ }
155
+
156
+ if (key.isToggle) {
157
+ setKeysProp(`toggles@remove@${key.toggleOnId}`, key, manager).unwrap()
158
+ setKeysProp(`toggles@remove@${key.toggleOffId}`, key, manager).unwrap()
159
+ }
160
+ if (key.variants) {
161
+ for (const variant of key.variants) {
162
+ setKeysProp(`variants@remove@${variant}`, key.id, manager).unwrap()
163
+ }
164
+ }
165
+ if (keys.autoManageLayout) {
166
+ setKeysProp("layout", calculateLayoutSize(keys), manager).unwrap()
167
+ }
168
+ break
169
+ }
170
+ default:
171
+ if (prop.startsWith("toggles@add@")) {
172
+ const name = prop.slice("toggles@add@".length)
173
+ keys.toggles[name] = val as Key
174
+ } else if (prop.startsWith("toggles@remove@")) {
175
+ const name = prop.slice("toggles@remove@".length)
176
+ delete keys.toggles[name]
177
+ } else if (prop.startsWith("variants@add@")) {
178
+ const variant = prop.slice("variants@add@".length)
179
+ const existing = keys.variants[variant]
180
+ ? [...keys.variants[variant]]
181
+ : []
182
+ existing.push(val as string)
183
+ keys.variants[variant] = existing
184
+ } else if (prop.startsWith("variants@remove@")) {
185
+ const variant = prop.slice("variants@remove@".length)
186
+ const existing = keys.variants[variant]
187
+ ? [...keys.variants[variant]]
188
+ : []
189
+ existing.splice(existing.indexOf(val as string), 1)
190
+ if (existing.length > 0) {
191
+ keys.variants[variant] = existing
192
+ } else {
193
+ delete keys.variants[variant]
194
+ }
195
+ } else {
196
+ (keys as any)[prop] = val
197
+ }
198
+ break
199
+ }
200
+
201
+ manager.hooks?.onSetKeysProp?.(keys, prop as any, val as any)
202
+
203
+ return Ok(keys) satisfies Result<Keys, never> as any
204
+ }
205
+
@@ -0,0 +1,111 @@
1
+ import { castType } from "@alanscodelog/utils/castType"
2
+ import { Err, Ok, type Result } from "@alanscodelog/utils/Result"
3
+ import { set } from "@alanscodelog/utils/set"
4
+ import { unreachable } from "@alanscodelog/utils/unreachable"
5
+
6
+ import { isValidManager } from "../helpers/isValidManager.js"
7
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
8
+ import type { safeSetManagerChain } from "../helpers/safeSetManagerChain.js"
9
+ import { areValidKeys } from "../internal/areValidKeys.js"
10
+ import { type CanHookErrors, type CanHookManagerProps, type ChainError, type Manager, type ManagerSetEntries, type MultipleErrors, SHORTCUT_ERROR } from "../types/index.js"
11
+ import { cloneChain } from "../utils/cloneChain.js"
12
+
13
+
14
+ const canHookable: CanHookManagerProps[] = ["state.chain", "shortcuts", "commands", "keys"]
15
+
16
+ /**
17
+ * Safely sets a settable manager property.
18
+ *
19
+ * You should not use this to set properties the manager manages (those tagged with @Managed in the docs) unless you've forgone using the manager.
20
+ *
21
+ * If you need to set the chain, use {@link safeSetManagerChain}.
22
+ *
23
+ * If enabling/disabling {@link Manager.state.isRecording} it's suggested you use {@link safeSetManagerChain} just before.
24
+ *
25
+ * # How to set multiple manager properties safely (i.e. batch replace shortcuts/commands/keys)
26
+ *
27
+ * This can be an issue because there isn't a way to tell the manager you want to replace *multiple* properties and it might be impossible to, for example, replace commands with a smaller subset but not have it error even if you're planning to replace the shortcuts so they don't contain missing commands.
28
+ *
29
+ * To achieve this, you can shallow clone the manager, change all the properties you want directly, then validate it's state by using isValidManager which is what this function does internally.
30
+ *
31
+ * Once you know it's valid, detach the old manager and attach the new one.
32
+ * ```ts
33
+ * const clone = {...manager, keys: newKeys, shortcuts: newShortcuts}
34
+ *
35
+ * if (isValidManager(manager)) {
36
+ * detach(manager, ...)
37
+ * attach(clone, ...)
38
+ * }
39
+ * ```
40
+ */
41
+
42
+ export function setManagerProp<
43
+ TEntries extends ManagerSetEntries,
44
+ TProp extends keyof ManagerSetEntries,
45
+ TEntry extends TEntries[TProp],
46
+ THooks extends Manager["hooks"],
47
+ TCheck extends boolean | "only" = true
48
+
49
+ >(
50
+ /** Manager is mutate if check is not "only" */
51
+ manager: TEntry["manager"] & { hooks?: THooks },
52
+ prop: TProp,
53
+ val: TEntry["val"],
54
+ { check = true as TCheck }: { check?: TCheck } = {}
55
+ ): Result<
56
+ TCheck extends "only" ? true : Manager,
57
+ MultipleErrors<TEntry["error"]>
58
+ | CanHookErrors<Manager["hooks"] extends never ? never : THooks, "canSetManagerProp">
59
+ > {
60
+ // castType is used extensively because of https://github.com/microsoft/TypeScript/issues/50652
61
+ if (check) {
62
+ switch (prop) {
63
+ case "state.chain": {
64
+ castType<TEntries["state.chain"]["val"]>(val)
65
+ castType<TEntries["state.chain"]["manager"]>(manager)
66
+
67
+ const resIsValid = areValidKeys(val, manager)
68
+ if (resIsValid.isError) return resIsValid satisfies Result<never, MultipleErrors<TEntries["state.chain"]["error"]>>
69
+ break
70
+ }
71
+ case "commands":
72
+ case "keys":
73
+ case "shortcuts": {
74
+ castType<TEntries["commands" | "shortcuts" | "keys"]["val"]>(val)
75
+ castType<TEntries["commands" | "shortcuts" | "keys"]["manager"]>(manager)
76
+
77
+ const managerClone = { ...manager, [prop as any]: val }
78
+ const res = isValidManager(managerClone)
79
+ if (res.isError && res.error.code === SHORTCUT_ERROR.INVALID_MANAGER) unreachable()
80
+ if (res.isError) return res satisfies Result<any, MultipleErrors<typeof SHORTCUT_ERROR.UNKNOWN_COMMAND | ChainError | typeof SHORTCUT_ERROR.INVALID_MANAGER>> as any
81
+ break
82
+ }
83
+ default:
84
+ break
85
+ }
86
+ if (manager?.hooks && "canSetManagerProp" in manager.hooks && canHookable.includes(prop as any)) {
87
+ const canHook = manager.hooks?.canSetManagerProp?.(manager, prop as any, val as any)
88
+ if (canHook instanceof Error) {
89
+ return Err(canHook) as any
90
+ }
91
+ }
92
+ }
93
+ if (check === "only") {
94
+ return Ok(true) satisfies Result<true, never> as any
95
+ }
96
+
97
+
98
+ if ((prop as string).includes(".")) {
99
+ if (prop === "state.chain") {
100
+ val = cloneChain(val as any) satisfies Manager["state"]["chain"] as any
101
+ }
102
+ set(manager, (prop as string).split("."), val)
103
+ } else {
104
+ (manager as any)[prop] = val
105
+ }
106
+
107
+ manager.hooks?.onSetManagerProp?.(manager, prop, val)
108
+
109
+ return Ok(manager) satisfies Result<Manager, never> as any
110
+ }
111
+