@dxos/plugin-automation 0.8.4-main.a4bbb77 → 0.8.4-main.abd8ff62ef

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 (287) hide show
  1. package/dist/lib/browser/chunk-CHUTFU3D.mjs +54 -0
  2. package/dist/lib/browser/chunk-CHUTFU3D.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  4. package/dist/lib/browser/chunk-RE64VLSV.mjs +44 -0
  5. package/dist/lib/browser/chunk-RE64VLSV.mjs.map +7 -0
  6. package/dist/lib/browser/create-trigger-from-template-JLBSJRVB.mjs +70 -0
  7. package/dist/lib/browser/create-trigger-from-template-JLBSJRVB.mjs.map +7 -0
  8. package/dist/lib/browser/hooks/index.mjs +123 -6
  9. package/dist/lib/browser/hooks/index.mjs.map +4 -4
  10. package/dist/lib/browser/index.mjs +17 -102
  11. package/dist/lib/browser/index.mjs.map +4 -4
  12. package/dist/lib/browser/meta.json +1 -1
  13. package/dist/lib/browser/operations/index.mjs +14 -0
  14. package/dist/lib/browser/operations/index.mjs.map +7 -0
  15. package/dist/lib/browser/translations.mjs +47 -0
  16. package/dist/lib/browser/translations.mjs.map +7 -0
  17. package/dist/lib/browser/types/index.mjs +10 -4
  18. package/dist/lib/node-esm/chunk-HHIHREME.mjs +45 -0
  19. package/dist/lib/node-esm/chunk-HHIHREME.mjs.map +7 -0
  20. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  21. package/dist/lib/node-esm/chunk-NWQMBY32.mjs +56 -0
  22. package/dist/lib/node-esm/chunk-NWQMBY32.mjs.map +7 -0
  23. package/dist/lib/node-esm/create-trigger-from-template-FUQ3LUIF.mjs +71 -0
  24. package/dist/lib/node-esm/create-trigger-from-template-FUQ3LUIF.mjs.map +7 -0
  25. package/dist/lib/node-esm/hooks/index.mjs +123 -6
  26. package/dist/lib/node-esm/hooks/index.mjs.map +4 -4
  27. package/dist/lib/node-esm/index.mjs +17 -102
  28. package/dist/lib/node-esm/index.mjs.map +4 -4
  29. package/dist/lib/node-esm/meta.json +1 -1
  30. package/dist/lib/node-esm/operations/index.mjs +15 -0
  31. package/dist/lib/node-esm/operations/index.mjs.map +7 -0
  32. package/dist/lib/node-esm/translations.mjs +48 -0
  33. package/dist/lib/node-esm/translations.mjs.map +7 -0
  34. package/dist/lib/node-esm/types/index.mjs +10 -4
  35. package/dist/types/src/AutomationPlugin.d.ts +3 -1
  36. package/dist/types/src/AutomationPlugin.d.ts.map +1 -1
  37. package/dist/types/src/AutomationPlugin.node.d.ts +4 -0
  38. package/dist/types/src/AutomationPlugin.node.d.ts.map +1 -0
  39. package/dist/types/src/AutomationPlugin.test.d.ts +2 -0
  40. package/dist/types/src/AutomationPlugin.test.d.ts.map +1 -0
  41. package/dist/types/src/capabilities/app-graph-builder.d.ts +4 -2
  42. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  43. package/dist/types/src/capabilities/compute-runtime.d.ts +12 -3
  44. package/dist/types/src/capabilities/compute-runtime.d.ts.map +1 -1
  45. package/dist/types/src/capabilities/index.d.ts +6 -5
  46. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  47. package/dist/types/src/capabilities/node.d.ts +6 -0
  48. package/dist/types/src/capabilities/node.d.ts.map +1 -0
  49. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  50. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  51. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  52. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  53. package/dist/types/src/commands/index.d.ts +2 -0
  54. package/dist/types/src/commands/index.d.ts.map +1 -0
  55. package/dist/types/src/commands/trigger/create/index.d.ts +25 -0
  56. package/dist/types/src/commands/trigger/create/index.d.ts.map +1 -0
  57. package/dist/types/src/commands/trigger/create/queue.d.ts +13 -0
  58. package/dist/types/src/commands/trigger/create/queue.d.ts.map +1 -0
  59. package/dist/types/src/commands/trigger/create/subscription.d.ts +15 -0
  60. package/dist/types/src/commands/trigger/create/subscription.d.ts.map +1 -0
  61. package/dist/types/src/commands/trigger/create/timer.d.ts +13 -0
  62. package/dist/types/src/commands/trigger/create/timer.d.ts.map +1 -0
  63. package/dist/types/src/commands/trigger/index.d.ts +57 -0
  64. package/dist/types/src/commands/trigger/index.d.ts.map +1 -0
  65. package/dist/types/src/commands/trigger/list.d.ts +7 -0
  66. package/dist/types/src/commands/trigger/list.d.ts.map +1 -0
  67. package/dist/types/src/commands/trigger/options.d.ts +11 -0
  68. package/dist/types/src/commands/trigger/options.d.ts.map +1 -0
  69. package/dist/types/src/commands/trigger/remove.d.ts +8 -0
  70. package/dist/types/src/commands/trigger/remove.d.ts.map +1 -0
  71. package/dist/types/src/commands/trigger/update/index.d.ts +28 -0
  72. package/dist/types/src/commands/trigger/update/index.d.ts.map +1 -0
  73. package/dist/types/src/commands/trigger/update/queue.d.ts +12 -0
  74. package/dist/types/src/commands/trigger/update/queue.d.ts.map +1 -0
  75. package/dist/types/src/commands/trigger/update/subscription.d.ts +13 -0
  76. package/dist/types/src/commands/trigger/update/subscription.d.ts.map +1 -0
  77. package/dist/types/src/commands/trigger/update/timer.d.ts +11 -0
  78. package/dist/types/src/commands/trigger/update/timer.d.ts.map +1 -0
  79. package/dist/types/src/commands/trigger/util.d.ts +46 -0
  80. package/dist/types/src/commands/trigger/util.d.ts.map +1 -0
  81. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts +6 -7
  82. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts.map +1 -1
  83. package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts +32 -27
  84. package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts.map +1 -1
  85. package/dist/types/src/components/AutomationPanel/index.d.ts.map +1 -1
  86. package/dist/types/src/components/FunctionsPanel/FunctionsPanel.d.ts.map +1 -1
  87. package/dist/types/src/components/FunctionsPanel/index.d.ts.map +1 -1
  88. package/dist/types/src/components/FunctionsRegistry/FunctionsRegistry.d.ts +8 -0
  89. package/dist/types/src/components/FunctionsRegistry/FunctionsRegistry.d.ts.map +1 -0
  90. package/dist/types/src/components/FunctionsRegistry/index.d.ts +4 -0
  91. package/dist/types/src/components/FunctionsRegistry/index.d.ts.map +1 -0
  92. package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts +12 -9
  93. package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts.map +1 -1
  94. package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts +6 -3
  95. package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts.map +1 -1
  96. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +11 -8
  97. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
  98. package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts +106 -30
  99. package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts.map +1 -1
  100. package/dist/types/src/components/index.d.ts +3 -4
  101. package/dist/types/src/components/index.d.ts.map +1 -1
  102. package/dist/types/src/containers/AutomationSettings/AutomationSettings.d.ts +6 -0
  103. package/dist/types/src/containers/AutomationSettings/AutomationSettings.d.ts.map +1 -0
  104. package/dist/types/src/containers/AutomationSettings/index.d.ts +2 -0
  105. package/dist/types/src/containers/AutomationSettings/index.d.ts.map +1 -0
  106. package/dist/types/src/containers/FunctionsContainer/FunctionsContainer.d.ts +4 -0
  107. package/dist/types/src/containers/FunctionsContainer/FunctionsContainer.d.ts.map +1 -0
  108. package/dist/types/src/containers/FunctionsContainer/index.d.ts +2 -0
  109. package/dist/types/src/containers/FunctionsContainer/index.d.ts.map +1 -0
  110. package/dist/types/src/containers/TriggerSettings/TriggerSettings.d.ts.map +1 -0
  111. package/dist/types/src/containers/TriggerSettings/index.d.ts +3 -0
  112. package/dist/types/src/containers/TriggerSettings/index.d.ts.map +1 -0
  113. package/dist/types/src/containers/index.d.ts +5 -0
  114. package/dist/types/src/containers/index.d.ts.map +1 -0
  115. package/dist/types/src/hooks/index.d.ts +2 -0
  116. package/dist/types/src/hooks/index.d.ts.map +1 -1
  117. package/dist/types/src/hooks/useComputeRuntime.d.ts +8 -0
  118. package/dist/types/src/hooks/useComputeRuntime.d.ts.map +1 -0
  119. package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts +5 -4
  120. package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts.map +1 -1
  121. package/dist/types/src/hooks/useComputeRuntimeService.d.ts +17 -0
  122. package/dist/types/src/hooks/useComputeRuntimeService.d.ts.map +1 -0
  123. package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts +6 -5
  124. package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts.map +1 -1
  125. package/dist/types/src/index.d.ts +3 -4
  126. package/dist/types/src/index.d.ts.map +1 -1
  127. package/dist/types/src/meta.d.ts +2 -2
  128. package/dist/types/src/meta.d.ts.map +1 -1
  129. package/dist/types/src/operations/create-trigger-from-template.d.ts +5 -0
  130. package/dist/types/src/operations/create-trigger-from-template.d.ts.map +1 -0
  131. package/dist/types/src/operations/definitions.d.ts +19 -0
  132. package/dist/types/src/operations/definitions.d.ts.map +1 -0
  133. package/dist/types/src/operations/index.d.ts +4 -0
  134. package/dist/types/src/operations/index.d.ts.map +1 -0
  135. package/dist/types/src/testing/test-functions.d.ts +204 -3
  136. package/dist/types/src/testing/test-functions.d.ts.map +1 -1
  137. package/dist/types/src/translations.d.ts +32 -28
  138. package/dist/types/src/translations.d.ts.map +1 -1
  139. package/dist/types/src/types/capabilities.d.ts +24 -0
  140. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  141. package/dist/types/src/types/events.d.ts +5 -0
  142. package/dist/types/src/types/events.d.ts.map +1 -0
  143. package/dist/types/src/types/index.d.ts +2 -0
  144. package/dist/types/src/types/index.d.ts.map +1 -1
  145. package/dist/types/src/types/schema.d.ts +12 -4
  146. package/dist/types/src/types/schema.d.ts.map +1 -1
  147. package/dist/types/tsconfig.tsbuildinfo +1 -1
  148. package/package.json +99 -56
  149. package/src/AutomationPlugin.node.ts +29 -0
  150. package/src/AutomationPlugin.test.ts +27 -0
  151. package/src/AutomationPlugin.tsx +20 -35
  152. package/src/capabilities/app-graph-builder.ts +59 -79
  153. package/src/capabilities/compute-runtime.ts +272 -83
  154. package/src/capabilities/index.ts +9 -7
  155. package/src/capabilities/node.ts +13 -0
  156. package/src/capabilities/operation-handler.ts +16 -0
  157. package/src/capabilities/react-surface.tsx +49 -46
  158. package/src/commands/index.ts +5 -0
  159. package/src/commands/trigger/create/index.ts +14 -0
  160. package/src/commands/trigger/create/queue.ts +86 -0
  161. package/src/commands/trigger/create/subscription.ts +125 -0
  162. package/src/commands/trigger/create/timer.ts +90 -0
  163. package/src/commands/trigger/index.ts +16 -0
  164. package/src/commands/trigger/list.ts +70 -0
  165. package/src/commands/trigger/options.ts +59 -0
  166. package/src/commands/trigger/remove.ts +45 -0
  167. package/src/commands/trigger/update/index.ts +14 -0
  168. package/src/commands/trigger/update/queue.ts +194 -0
  169. package/src/commands/trigger/update/subscription.ts +278 -0
  170. package/src/commands/trigger/update/timer.ts +193 -0
  171. package/src/commands/trigger/util.ts +410 -0
  172. package/src/components/AutomationPanel/AutomationPanel.stories.tsx +7 -7
  173. package/src/components/AutomationPanel/AutomationPanel.tsx +249 -81
  174. package/src/components/FunctionsPanel/FunctionsPanel.tsx +50 -31
  175. package/src/components/FunctionsRegistry/FunctionsRegistry.tsx +114 -0
  176. package/src/components/FunctionsRegistry/index.ts +8 -0
  177. package/src/components/TriggerEditor/FunctionInputEditor.tsx +42 -35
  178. package/src/components/TriggerEditor/SpecSelector.tsx +28 -16
  179. package/src/components/TriggerEditor/TriggerEditor.stories.tsx +116 -29
  180. package/src/components/TriggerEditor/TriggerEditor.tsx +109 -42
  181. package/src/components/index.ts +1 -2
  182. package/src/containers/AutomationSettings/AutomationSettings.tsx +31 -0
  183. package/src/containers/AutomationSettings/index.ts +5 -0
  184. package/src/containers/FunctionsContainer/FunctionsContainer.tsx +33 -0
  185. package/src/containers/FunctionsContainer/index.ts +5 -0
  186. package/src/containers/TriggerSettings/TriggerSettings.tsx +48 -0
  187. package/src/containers/TriggerSettings/index.ts +6 -0
  188. package/src/containers/index.ts +9 -0
  189. package/src/hooks/index.ts +3 -0
  190. package/src/hooks/useComputeRuntime.ts +30 -0
  191. package/src/hooks/useComputeRuntimeCallback.ts +11 -9
  192. package/src/hooks/useComputeRuntimeService.ts +64 -0
  193. package/src/hooks/useTriggerRuntimeControls.ts +34 -16
  194. package/src/index.ts +5 -4
  195. package/src/meta.ts +9 -6
  196. package/src/operations/create-trigger-from-template.ts +71 -0
  197. package/src/operations/definitions.ts +28 -0
  198. package/src/operations/index.ts +9 -0
  199. package/src/testing/test-functions.ts +10 -7
  200. package/src/translations.ts +39 -33
  201. package/src/types/capabilities.ts +47 -0
  202. package/src/types/events.ts +11 -0
  203. package/src/types/index.ts +2 -0
  204. package/src/types/schema.ts +6 -6
  205. package/dist/lib/browser/AutomationPanel-4QA5G3ZY.mjs +0 -11
  206. package/dist/lib/browser/AutomationSettings-6XLG2PCK.mjs +0 -69
  207. package/dist/lib/browser/AutomationSettings-6XLG2PCK.mjs.map +0 -7
  208. package/dist/lib/browser/FunctionsContainer-BQ6VBDIG.mjs +0 -36
  209. package/dist/lib/browser/FunctionsContainer-BQ6VBDIG.mjs.map +0 -7
  210. package/dist/lib/browser/FunctionsPanel-QBTOFFNU.mjs +0 -10
  211. package/dist/lib/browser/app-graph-builder-6TP6ZNYG.mjs +0 -80
  212. package/dist/lib/browser/app-graph-builder-6TP6ZNYG.mjs.map +0 -7
  213. package/dist/lib/browser/chunk-BDJPBZGC.mjs +0 -14
  214. package/dist/lib/browser/chunk-BDJPBZGC.mjs.map +0 -7
  215. package/dist/lib/browser/chunk-EAQL33PI.mjs +0 -94
  216. package/dist/lib/browser/chunk-EAQL33PI.mjs.map +0 -7
  217. package/dist/lib/browser/chunk-F4K5EMWL.mjs +0 -38
  218. package/dist/lib/browser/chunk-F4K5EMWL.mjs.map +0 -7
  219. package/dist/lib/browser/chunk-IN2YC7NY.mjs +0 -230
  220. package/dist/lib/browser/chunk-IN2YC7NY.mjs.map +0 -7
  221. package/dist/lib/browser/chunk-LNMCGQC2.mjs +0 -143
  222. package/dist/lib/browser/chunk-LNMCGQC2.mjs.map +0 -7
  223. package/dist/lib/browser/chunk-M2WQCY2T.mjs +0 -53
  224. package/dist/lib/browser/chunk-M2WQCY2T.mjs.map +0 -7
  225. package/dist/lib/browser/chunk-U66KBK53.mjs +0 -14
  226. package/dist/lib/browser/chunk-U66KBK53.mjs.map +0 -7
  227. package/dist/lib/browser/chunk-WWURMV25.mjs +0 -13
  228. package/dist/lib/browser/chunk-WWURMV25.mjs.map +0 -7
  229. package/dist/lib/browser/chunk-ZTXBAXUT.mjs +0 -15
  230. package/dist/lib/browser/chunk-ZTXBAXUT.mjs.map +0 -7
  231. package/dist/lib/browser/compute-runtime-OFQPXD5I.mjs +0 -158
  232. package/dist/lib/browser/compute-runtime-OFQPXD5I.mjs.map +0 -7
  233. package/dist/lib/browser/intent-resolver-JXVWRAAH.mjs +0 -77
  234. package/dist/lib/browser/intent-resolver-JXVWRAAH.mjs.map +0 -7
  235. package/dist/lib/browser/react-surface-NG3MJCDH.mjs +0 -64
  236. package/dist/lib/browser/react-surface-NG3MJCDH.mjs.map +0 -7
  237. package/dist/lib/node-esm/AutomationPanel-WMVR3IST.mjs +0 -12
  238. package/dist/lib/node-esm/AutomationPanel-WMVR3IST.mjs.map +0 -7
  239. package/dist/lib/node-esm/AutomationSettings-4IW7RGMN.mjs +0 -70
  240. package/dist/lib/node-esm/AutomationSettings-4IW7RGMN.mjs.map +0 -7
  241. package/dist/lib/node-esm/FunctionsContainer-LHUUOLA5.mjs +0 -37
  242. package/dist/lib/node-esm/FunctionsContainer-LHUUOLA5.mjs.map +0 -7
  243. package/dist/lib/node-esm/FunctionsPanel-SHTQIMDV.mjs +0 -11
  244. package/dist/lib/node-esm/FunctionsPanel-SHTQIMDV.mjs.map +0 -7
  245. package/dist/lib/node-esm/app-graph-builder-3A2NIPR5.mjs +0 -81
  246. package/dist/lib/node-esm/app-graph-builder-3A2NIPR5.mjs.map +0 -7
  247. package/dist/lib/node-esm/chunk-2L2X4JMP.mjs +0 -16
  248. package/dist/lib/node-esm/chunk-2L2X4JMP.mjs.map +0 -7
  249. package/dist/lib/node-esm/chunk-5MQJPJR2.mjs +0 -15
  250. package/dist/lib/node-esm/chunk-5MQJPJR2.mjs.map +0 -7
  251. package/dist/lib/node-esm/chunk-B6O6UBS7.mjs +0 -16
  252. package/dist/lib/node-esm/chunk-B6O6UBS7.mjs.map +0 -7
  253. package/dist/lib/node-esm/chunk-CEWZ2CDM.mjs +0 -39
  254. package/dist/lib/node-esm/chunk-CEWZ2CDM.mjs.map +0 -7
  255. package/dist/lib/node-esm/chunk-DSEZPWSO.mjs +0 -54
  256. package/dist/lib/node-esm/chunk-DSEZPWSO.mjs.map +0 -7
  257. package/dist/lib/node-esm/chunk-MN5S5J4O.mjs +0 -144
  258. package/dist/lib/node-esm/chunk-MN5S5J4O.mjs.map +0 -7
  259. package/dist/lib/node-esm/chunk-N7NES6SZ.mjs +0 -231
  260. package/dist/lib/node-esm/chunk-N7NES6SZ.mjs.map +0 -7
  261. package/dist/lib/node-esm/chunk-PBULJE4B.mjs +0 -95
  262. package/dist/lib/node-esm/chunk-PBULJE4B.mjs.map +0 -7
  263. package/dist/lib/node-esm/chunk-UTZIMYCX.mjs +0 -16
  264. package/dist/lib/node-esm/chunk-UTZIMYCX.mjs.map +0 -7
  265. package/dist/lib/node-esm/compute-runtime-W5UJNSQY.mjs +0 -159
  266. package/dist/lib/node-esm/compute-runtime-W5UJNSQY.mjs.map +0 -7
  267. package/dist/lib/node-esm/intent-resolver-ROEBZXSS.mjs +0 -78
  268. package/dist/lib/node-esm/intent-resolver-ROEBZXSS.mjs.map +0 -7
  269. package/dist/lib/node-esm/react-surface-A4KTYX6Z.mjs +0 -65
  270. package/dist/lib/node-esm/react-surface-A4KTYX6Z.mjs.map +0 -7
  271. package/dist/types/src/capabilities/capabilities.d.ts +0 -18
  272. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  273. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  274. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  275. package/dist/types/src/components/AutomationSettings.d.ts +0 -5
  276. package/dist/types/src/components/AutomationSettings.d.ts.map +0 -1
  277. package/dist/types/src/components/FunctionsContainer.d.ts +0 -7
  278. package/dist/types/src/components/FunctionsContainer.d.ts.map +0 -1
  279. package/dist/types/src/components/TriggerSettings.d.ts.map +0 -1
  280. package/src/capabilities/capabilities.ts +0 -47
  281. package/src/capabilities/intent-resolver.ts +0 -72
  282. package/src/components/AutomationSettings.tsx +0 -30
  283. package/src/components/FunctionsContainer.tsx +0 -29
  284. package/src/components/TriggerSettings.tsx +0 -25
  285. /package/dist/lib/browser/{AutomationPanel-4QA5G3ZY.mjs.map → chunk-J5LGTIGS.mjs.map} +0 -0
  286. /package/dist/lib/{browser/FunctionsPanel-QBTOFFNU.mjs.map → node-esm/chunk-HSLMI22Q.mjs.map} +0 -0
  287. /package/dist/types/src/{components → containers/TriggerSettings}/TriggerSettings.d.ts +0 -0
@@ -2,52 +2,77 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
6
- import React, { useState } from 'react';
5
+ import * as Array from 'effect/Array';
6
+ import * as EFn from 'effect/Function';
7
+ import * as Match from 'effect/Match';
8
+ import * as Schema from 'effect/Schema';
9
+ import React, { useCallback, useMemo, useState } from 'react';
7
10
 
8
- import { Filter, Obj } from '@dxos/echo';
9
- import { FunctionTrigger, FunctionType, ScriptType } from '@dxos/functions';
11
+ import { useTypeOptions } from '@dxos/app-toolkit/ui';
12
+ import { Script, Trigger } from '@dxos/compute';
13
+ import { Operation } from '@dxos/compute';
14
+ import { Context } from '@dxos/context';
15
+ import { Filter, Obj, Query, Tag } from '@dxos/echo';
16
+ import { KEY_QUEUE_CURSOR } from '@dxos/functions-runtime';
17
+ import { FunctionsServiceClient } from '@dxos/functions-runtime/edge';
10
18
  import { type Client, useClient } from '@dxos/react-client';
11
- import { type Space, getSpace, useQuery } from '@dxos/react-client/echo';
12
- import { Clipboard, IconButton, Input, Separator, type ThemedClassName, useTranslation } from '@dxos/react-ui';
13
- import { ControlItem, controlItemClasses } from '@dxos/react-ui-form';
19
+ import { type Space, useObject, useQuery } from '@dxos/react-client/echo';
20
+ import { Clipboard, IconButton, type IconButtonProps, Input, Separator, useTranslation } from '@dxos/react-ui';
21
+ import { Settings } from '@dxos/react-ui-form';
14
22
  import { List } from '@dxos/react-ui-list';
15
- import { ghostHover, mx } from '@dxos/react-ui-theme';
23
+ import { Pipeline } from '@dxos/types';
24
+ import { ghostHover, mx } from '@dxos/ui-theme';
25
+ import { isNonNullable } from '@dxos/util';
26
+
27
+ import { meta } from '#meta';
16
28
 
17
- import { meta } from '../../meta';
18
29
  import { TriggerEditor, type TriggerEditorProps } from '../TriggerEditor';
19
30
 
20
- const grid = 'grid grid-cols-[40px_1fr_32px] min-bs-[2.5rem]';
31
+ const grid = 'grid grid-cols-[40px_1fr_32px_32px] min-h-[2.5rem]';
21
32
 
22
- export type AutomationPanelProps = ThemedClassName<{
33
+ export type AutomationPanelProps = {
23
34
  space: Space;
24
- object?: Obj.Any;
25
- initialTrigger?: FunctionTrigger;
35
+ object?: Obj.Unknown;
36
+ initialTrigger?: Trigger.Trigger;
26
37
  onDone?: () => void;
27
- }>;
38
+ };
28
39
 
29
40
  // TODO(burdon): Factor out common layout with ViewEditor.
30
- export const AutomationPanel = ({ classNames, space, object, initialTrigger, onDone }: AutomationPanelProps) => {
41
+ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: AutomationPanelProps) => {
31
42
  const { t } = useTranslation(meta.id);
32
43
  const client = useClient();
33
- const triggers = useQuery(space, Filter.type(FunctionTrigger));
34
- const functions = useQuery(space, Filter.type(FunctionType));
35
- const scripts = useQuery(space, Filter.type(ScriptType));
44
+ const functionsServiceClient = useMemo(() => FunctionsServiceClient.fromClient(client), [client]);
45
+ const functions = useQuery(space.db, Filter.type(Operation.PersistentOperation));
46
+ const triggers = useQuery(
47
+ space.db,
48
+ Query.select(Filter.type(Trigger.Trigger)).debugLabel('plugin-automation.AutomationPanel'),
49
+ );
50
+ const filteredTriggers = useMemo(() => {
51
+ return object ? triggers.filter(triggerMatch(object)) : triggers;
52
+ }, [object, triggers]);
53
+ const tags = useQuery(space.db, Filter.type(Tag.Tag));
54
+ const types = useTypeOptions({
55
+ space,
56
+ annotation: {
57
+ location: ['database', 'runtime'],
58
+ kind: ['user'],
59
+ },
60
+ });
36
61
 
37
- const [trigger, setTrigger] = useState<FunctionTrigger | undefined>(initialTrigger);
38
- const [selected, setSelected] = useState<FunctionTrigger>();
62
+ const [trigger, setTrigger] = useState<Trigger.Trigger | undefined>(initialTrigger);
63
+ const [selected, setSelected] = useState<Trigger.Trigger>();
39
64
 
40
- const handleSelect = (trigger: FunctionTrigger) => {
65
+ const handleSelect = (trigger: Trigger.Trigger) => {
41
66
  setTrigger(trigger);
42
67
  setSelected(trigger);
43
68
  };
44
69
 
45
70
  const handleAdd = () => {
46
- setTrigger(Obj.make(FunctionTrigger, {}));
71
+ setTrigger(Trigger.make({}));
47
72
  setSelected(undefined);
48
73
  };
49
74
 
50
- const handleDelete = (trigger: FunctionTrigger) => {
75
+ const handleDelete = (trigger: Trigger.Trigger) => {
51
76
  space.db.remove(trigger);
52
77
  setTrigger(undefined);
53
78
  setSelected(undefined);
@@ -55,9 +80,11 @@ export const AutomationPanel = ({ classNames, space, object, initialTrigger, onD
55
80
 
56
81
  const handleSave: TriggerEditorProps['onSave'] = (trigger) => {
57
82
  if (selected) {
58
- Object.assign(selected, trigger);
83
+ Obj.update(selected, (selected) => {
84
+ Object.assign(selected, trigger);
85
+ });
59
86
  } else {
60
- space.db.add(Obj.make(FunctionTrigger, trigger));
87
+ space.db.add(Trigger.make(trigger));
61
88
  }
62
89
 
63
90
  setTrigger(undefined);
@@ -70,90 +97,231 @@ export const AutomationPanel = ({ classNames, space, object, initialTrigger, onD
70
97
  onDone?.();
71
98
  };
72
99
 
100
+ const handleForceRunTrigger = async (trigger: Trigger.Trigger) => {
101
+ await functionsServiceClient.forceRunCronTrigger(Context.default(), space.id, trigger.id);
102
+ };
103
+
104
+ const handleResetCursor = async (trigger: Trigger.Trigger) => {
105
+ Obj.update(trigger, (trigger) => {
106
+ Obj.deleteKeys(trigger, KEY_QUEUE_CURSOR);
107
+ });
108
+ await space.db.flush({ indexes: true });
109
+ };
110
+
73
111
  if (trigger) {
74
112
  return (
75
- <ControlItem title={t('trigger editor title')}>
76
- <TriggerEditor space={space} trigger={trigger} onSave={handleSave} onCancel={handleCancel} />
77
- </ControlItem>
113
+ <Settings.Item title={t('trigger-editor.title')} description={t('trigger-editor.description')}>
114
+ <TriggerEditor
115
+ db={space.db}
116
+ trigger={trigger}
117
+ readonlySpec={Boolean(object)}
118
+ tags={tags}
119
+ types={types}
120
+ onSave={handleSave}
121
+ onCancel={handleCancel}
122
+ />
123
+ </Settings.Item>
78
124
  );
79
125
  }
80
126
 
81
127
  return (
82
- <div className={mx(controlItemClasses, classNames)}>
83
- {triggers.length > 0 && (
84
- <List.Root<FunctionTrigger> items={triggers} isItem={Schema.is(FunctionTrigger)} getId={(field) => field.id}>
85
- {({ items: triggers }) => (
86
- <div role='list' className='flex flex-col w-full'>
87
- {triggers?.map((trigger) => {
88
- const copyAction = getCopyAction(client, trigger);
89
- return (
90
- <List.Item<FunctionTrigger>
128
+ <Settings.Panel>
129
+ <Clipboard.Provider>
130
+ {filteredTriggers.length > 0 && (
131
+ <List.Root<Trigger.Trigger>
132
+ items={filteredTriggers}
133
+ isItem={Schema.is(Trigger.Trigger)}
134
+ getId={(field) => field.id}
135
+ >
136
+ {({ items: filteredTriggers }) => (
137
+ <div role='list' className='flex flex-col w-full'>
138
+ {filteredTriggers?.map((trigger) => (
139
+ <TriggerListItem
91
140
  key={trigger.id}
92
- item={trigger}
93
- classNames={mx(grid, ghostHover, 'items-center', 'px-2')}
94
- >
95
- <Input.Root>
96
- <Input.Switch
97
- checked={trigger.enabled}
98
- onCheckedChange={(checked) => (trigger.enabled = checked)}
99
- />
100
- </Input.Root>
101
-
102
- <div className={'flex'}>
103
- <List.ItemTitle
104
- classNames='px-1 cursor-pointer w-0 shrink truncate'
105
- onClick={() => handleSelect(trigger)}
106
- >
107
- {getFunctionName(scripts, functions, trigger) ?? '∅'}
108
- </List.ItemTitle>
109
-
110
- {/* TODO: a better way to expose copy action */}
111
- {copyAction && (
112
- <Clipboard.IconButton
113
- label={t(copyAction.translationKey)}
114
- value={copyAction.contentProvider()}
115
- />
116
- )}
117
- </div>
118
-
119
- <List.ItemDeleteButton onClick={() => handleDelete(trigger)} />
120
- </List.Item>
121
- );
122
- })}
123
- </div>
124
- )}
125
- </List.Root>
126
- )}
127
- {triggers.length > 0 && <Separator classNames='mlb-4' />}
128
- <IconButton icon='ph--plus--regular' label={t('new trigger label')} onClick={handleAdd} />
129
- </div>
141
+ trigger={trigger}
142
+ functions={functions}
143
+ onSelect={handleSelect}
144
+ onDelete={handleDelete}
145
+ onResetCursor={handleResetCursor}
146
+ onForceRun={handleForceRunTrigger}
147
+ />
148
+ ))}
149
+ </div>
150
+ )}
151
+ </List.Root>
152
+ )}
153
+
154
+ {filteredTriggers.length > 0 && <Separator classNames='my-4' />}
155
+ <IconButton icon='ph--plus--regular' label={t('new-trigger.label')} onClick={handleAdd} />
156
+ </Clipboard.Provider>
157
+ </Settings.Panel>
130
158
  );
131
159
  };
132
160
 
133
- const getCopyAction = (client: Client, trigger: FunctionTrigger | undefined) => {
161
+ const TriggerListItem = ({
162
+ trigger,
163
+ functions,
164
+ onSelect,
165
+ onDelete,
166
+ onResetCursor,
167
+ onForceRun,
168
+ }: {
169
+ trigger: Trigger.Trigger;
170
+ functions: Operation.PersistentOperation[];
171
+ onSelect?: (trigger: Trigger.Trigger) => void;
172
+ onDelete?: (trigger: Trigger.Trigger) => void;
173
+ onResetCursor?: (trigger: Trigger.Trigger) => void;
174
+ onForceRun?: (trigger: Trigger.Trigger) => void;
175
+ }) => {
176
+ const client = useClient();
177
+ const copyAction = getCopyAction(client, trigger);
178
+ const { t } = useTranslation(meta.id);
179
+ const cursor = Obj.getKeys(trigger, KEY_QUEUE_CURSOR).at(0)?.id;
180
+ const [snapshot, updateTrigger] = useObject(trigger);
181
+
182
+ const enabled = snapshot.enabled ?? false;
183
+ const onEnabledChange = (checked: boolean) => {
184
+ updateTrigger((trigger) => {
185
+ trigger.enabled = checked;
186
+ });
187
+ };
188
+
189
+ const handleSelect = useCallback(() => {
190
+ onSelect?.(trigger);
191
+ }, [onSelect, trigger]);
192
+
193
+ const handleDelete = useCallback(() => {
194
+ onDelete?.(trigger);
195
+ }, [onDelete, trigger]);
196
+
197
+ const handleResetCursor = useCallback(() => {
198
+ onResetCursor?.(trigger);
199
+ }, [onResetCursor, trigger]);
200
+
201
+ const handleForceRun = useCallback(() => {
202
+ onForceRun?.(trigger);
203
+ }, [onForceRun, trigger]);
204
+
205
+ const actionProps = useMemo<IconButtonProps | undefined>(() => {
206
+ if (trigger.spec?.kind === 'timer' && onForceRun) {
207
+ return {
208
+ disabled: !enabled || trigger.spec?.kind !== 'timer',
209
+ icon: 'ph--play--regular',
210
+ label: 'Force run',
211
+ onClick: handleForceRun,
212
+ };
213
+ }
214
+
215
+ if (trigger.spec?.kind === 'queue' && onResetCursor) {
216
+ return {
217
+ disabled: !cursor,
218
+ icon: 'ph--arrow-clockwise--regular',
219
+ label: 'Reset cursor',
220
+ onClick: handleResetCursor,
221
+ };
222
+ }
223
+ }, [enabled, trigger.spec?.kind, handleForceRun]);
224
+
225
+ return (
226
+ <List.Item<Obj.Snapshot<Trigger.Trigger>>
227
+ key={trigger.id}
228
+ item={snapshot}
229
+ classNames={mx(grid, ghostHover, 'items-center', 'px-2')}
230
+ >
231
+ <Input.Root>
232
+ <Input.Switch checked={enabled} onCheckedChange={onEnabledChange} />
233
+ </Input.Root>
234
+
235
+ <div className={'flex'}>
236
+ <List.ItemTitle classNames='px-1 cursor-pointer w-0 shrink truncate' onClick={handleSelect}>
237
+ {getFunctionName(functions, trigger) ?? '∅'}
238
+ {cursor && <div className='text-xs text-description truncate ml-4'>Position: {cursor}</div>}
239
+ </List.ItemTitle>
240
+
241
+ {copyAction && (
242
+ <Clipboard.IconButton label={t(copyAction.translationKey)} value={copyAction.contentProvider()} />
243
+ )}
244
+ </div>
245
+
246
+ {actionProps ? <List.ItemIconButton {...actionProps} autoHide={false} /> : <div />}
247
+
248
+ {onDelete && <List.ItemDeleteButton onClick={handleDelete} />}
249
+ </List.Item>
250
+ );
251
+ };
252
+
253
+ const getCopyAction = (client: Client, trigger: Trigger.Trigger | undefined) => {
134
254
  if (trigger?.spec?.kind === 'email') {
135
- return { translationKey: 'trigger copy email', contentProvider: () => `${getSpace(trigger)!.id}@dxos.network` };
255
+ return {
256
+ translationKey: 'trigger copy email' as const,
257
+ contentProvider: () => `${Obj.getDatabase(trigger)!.spaceId}@dxos.network`,
258
+ };
136
259
  }
137
260
 
138
261
  if (trigger?.spec?.kind === 'webhook') {
139
- return { translationKey: 'trigger copy url', contentProvider: () => getWebhookUrl(client, trigger) };
262
+ return {
263
+ translationKey: 'trigger copy url' as const,
264
+ contentProvider: () => getWebhookUrl(client, trigger!),
265
+ };
140
266
  }
141
267
 
142
268
  return undefined;
143
269
  };
144
270
 
145
- const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
146
- const spaceId = getSpace(trigger)!.id;
271
+ const getWebhookUrl = (client: Client, trigger: Trigger.Trigger) => {
272
+ const spaceId = Obj.getDatabase(trigger)!.spaceId;
147
273
  const edgeUrl = new URL(client.config.values.runtime!.services!.edge!.url!);
148
274
  const isSecure = edgeUrl.protocol.startsWith('https') || edgeUrl.protocol.startsWith('wss');
149
275
  edgeUrl.protocol = isSecure ? 'https' : 'http';
150
276
  return new URL(`/webhook/${spaceId}:${trigger.id}`, edgeUrl).toString();
151
277
  };
152
278
 
153
- const getFunctionName = (scripts: ScriptType[], functions: FunctionType[], trigger: FunctionTrigger) => {
279
+ const getFunctionName = (functions: Operation.PersistentOperation[], trigger: Trigger.Trigger) => {
154
280
  // TODO(wittjosiah): Truncation should be done in the UI.
155
281
  // Warning that the List component is currently a can of worms.
156
282
  const shortId = trigger.function && `${trigger.function.dxn.toString().slice(0, 16)}…`;
157
283
  const functionObject = functions.find((fn) => fn === trigger.function?.target);
158
284
  return functionObject?.name ?? shortId;
159
285
  };
286
+
287
+ const scriptMatch = (script: Script.Script) => (trigger: Trigger.Trigger) => {
288
+ const fn = trigger.function?.target;
289
+ if (!Obj.instanceOf(Operation.PersistentOperation, fn)) {
290
+ return false;
291
+ }
292
+
293
+ return fn.source?.target === script;
294
+ };
295
+
296
+ const projectMatch = (project: Pipeline.Pipeline) => {
297
+ const viewQueries = EFn.pipe(
298
+ project.columns,
299
+ Array.map((column) => column.view.target),
300
+ Array.filter(isNonNullable),
301
+ Array.map((view) => Obj.getSnapshot(view).query.ast),
302
+ Array.map((ast) => JSON.stringify(ast)),
303
+ );
304
+
305
+ return (trigger: Trigger.Trigger) => {
306
+ const spec = Obj.getSnapshot(trigger).spec;
307
+ if (spec?.kind !== 'subscription') {
308
+ return false;
309
+ }
310
+
311
+ // TODO(wittjosiah): Implement better way of comparing query ASTs.
312
+ return viewQueries.some((query) => JSON.stringify(spec.query) === query);
313
+ };
314
+ };
315
+
316
+ const triggerMatch = Match.type<Obj.Unknown>().pipe(
317
+ Match.withReturnType<(trigger: Trigger.Trigger) => boolean>(),
318
+ Match.when(
319
+ (obj) => Obj.instanceOf(Script.Script, obj),
320
+ (obj) => scriptMatch(obj),
321
+ ),
322
+ Match.when(
323
+ (obj) => Obj.instanceOf(Pipeline.Pipeline, obj),
324
+ (obj) => projectMatch(obj),
325
+ ),
326
+ Match.orElse((_obj) => () => true),
327
+ );
@@ -2,20 +2,20 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
  import React, { useCallback, useMemo } from 'react';
7
7
 
8
- import { LayoutAction, createIntent, useIntentDispatcher } from '@dxos/app-framework';
9
- import { FunctionType, ScriptType } from '@dxos/functions';
10
- import { Filter, type Space, fullyQualifiedId, useQuery } from '@dxos/react-client/echo';
11
- import { Button, useTranslation } from '@dxos/react-ui';
12
- import { controlItemClasses } from '@dxos/react-ui-form';
8
+ import { useOperationInvoker } from '@dxos/app-framework/ui';
9
+ import { LayoutOperation, getObjectPathFromObject } from '@dxos/app-toolkit';
10
+ import { Operation, Script } from '@dxos/compute';
11
+ import { SpaceOperation } from '@dxos/plugin-space/operations';
12
+ import { Filter, type Space, useQuery } from '@dxos/react-client/echo';
13
+ import { IconButton, useTranslation } from '@dxos/react-ui';
14
+ import { Settings } from '@dxos/react-ui-form';
13
15
  import { List } from '@dxos/react-ui-list';
14
- import { ghostHover, mx } from '@dxos/react-ui-theme';
16
+ import { ghostHover, mx } from '@dxos/ui-theme';
15
17
 
16
- import { meta } from '../../meta';
17
-
18
- const grid = 'grid grid-cols-[1fr_auto] min-bs-[2.5rem]';
18
+ import { meta } from '#meta';
19
19
 
20
20
  export type FunctionsPanelProps = {
21
21
  space: Space;
@@ -23,9 +23,9 @@ export type FunctionsPanelProps = {
23
23
 
24
24
  export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
25
25
  const { t } = useTranslation(meta.id);
26
- const functions = useQuery(space, Filter.type(FunctionType));
27
- const scripts = useQuery(space, Filter.type(ScriptType));
28
- const { dispatchPromise: dispatch } = useIntentDispatcher();
26
+ const functions = useQuery(space.db, Filter.type(Operation.PersistentOperation));
27
+ const scripts = useQuery(space.db, Filter.type(Script.Script));
28
+ const { invokePromise } = useOperationInvoker();
29
29
 
30
30
  const functionToScriptMap = useMemo(
31
31
  () =>
@@ -40,13 +40,13 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
40
40
  }
41
41
  return map;
42
42
  },
43
- {} as Record<string, ScriptType>,
43
+ {} as Record<string, Script.Script>,
44
44
  ),
45
45
  [functions, scripts],
46
46
  );
47
47
 
48
48
  const getScriptName = useCallback(
49
- (func: FunctionType) => {
49
+ (func: Operation.PersistentOperation) => {
50
50
  const script = functionToScriptMap[func.id];
51
51
  return script?.name;
52
52
  },
@@ -54,44 +54,63 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
54
54
  );
55
55
 
56
56
  const handleGoToScript = useCallback(
57
- (func: FunctionType) => {
57
+ (func: Operation.PersistentOperation) => {
58
58
  const script = functionToScriptMap[func.id];
59
59
  if (script) {
60
- void dispatch(createIntent(LayoutAction.Open, { part: 'main', subject: [fullyQualifiedId(script)] }));
60
+ void invokePromise(LayoutOperation.Open, { subject: [getObjectPathFromObject(script)] });
61
61
  }
62
62
  },
63
- [functionToScriptMap, dispatch],
63
+ [functionToScriptMap, invokePromise],
64
+ );
65
+
66
+ const handleDelete = useCallback(
67
+ (func: Operation.PersistentOperation) => invokePromise(SpaceOperation.RemoveObjects, { objects: [func] }),
68
+ [invokePromise],
64
69
  );
65
70
 
66
71
  return (
67
- <div role='none' className={mx(controlItemClasses)}>
72
+ <Settings.Panel>
68
73
  {functions.length > 0 && (
69
- <List.Root<FunctionType> items={functions} isItem={Schema.is(FunctionType)} getId={(func) => func.id}>
74
+ <List.Root<Operation.PersistentOperation>
75
+ items={functions}
76
+ isItem={Schema.is(Operation.PersistentOperation)}
77
+ getId={(func) => func.id}
78
+ >
70
79
  {({ items }) => (
71
80
  <div role='list' className='flex flex-col w-full'>
72
81
  {items?.map((func) => (
73
- <List.Item<FunctionType>
82
+ <List.Item<Operation.PersistentOperation>
74
83
  key={func.id}
75
84
  item={func}
76
- classNames={mx(grid, ghostHover, 'items-center', 'pli-2', 'min-bs-[3rem]')}
85
+ classNames={mx(
86
+ 'grid grid-cols-[1fr_min-content_auto] min-h-[2.5rem] min-h-[3rem] px-2 items-center',
87
+ ghostHover,
88
+ )}
77
89
  >
78
90
  <div className='flex flex-col truncate'>
79
91
  <List.ItemTitle classNames='truncate'>{func.name}</List.ItemTitle>
80
- {getScriptName(func) && (
81
- <div className='text-xs text-description truncate'>{getScriptName(func)}</div>
82
- )}
92
+ {getScriptName(func) && <p className='text-xs text-description truncate'>{getScriptName(func)}</p>}
83
93
  </div>
84
- {functionToScriptMap[func.id] && (
85
- <Button onClick={() => handleGoToScript(func)}>{t('go to function source button label')}</Button>
86
- )}
94
+ {(functionToScriptMap[func.id] && (
95
+ <IconButton
96
+ icon='ph--arrow-square-out--regular'
97
+ iconOnly
98
+ label={t('show-source-button.label')}
99
+ onClick={() => handleGoToScript(func)}
100
+ />
101
+ )) || <div />}
102
+ <IconButton
103
+ icon='ph--trash--regular'
104
+ iconOnly
105
+ label={t('delete-function-button.label')}
106
+ onClick={() => handleDelete(func)}
107
+ />
87
108
  </List.Item>
88
109
  ))}
89
110
  </div>
90
111
  )}
91
112
  </List.Root>
92
113
  )}
93
-
94
- {functions.length === 0 && <div className='text-center plb-4 text-gray-500'>{t('no functions found')}</div>}
95
- </div>
114
+ </Settings.Panel>
96
115
  );
97
116
  };
@@ -0,0 +1,114 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+ import { useState } from 'react';
7
+ import React, { useCallback } from 'react';
8
+
9
+ import * as OperationModule from '@dxos/compute';
10
+ import { Context } from '@dxos/context';
11
+ import { getDeployedFunctions } from '@dxos/functions-runtime/edge';
12
+ import { useClient } from '@dxos/react-client';
13
+ import { Filter, Query, type Space, useQuery } from '@dxos/react-client/echo';
14
+ import { IconButton, useAsyncEffect, useTranslation } from '@dxos/react-ui';
15
+ import { Settings } from '@dxos/react-ui-form';
16
+ import { List } from '@dxos/react-ui-list';
17
+ import { ghostHover, mx } from '@dxos/ui-theme';
18
+
19
+ import { meta } from '#meta';
20
+
21
+ const grid = 'grid grid-cols-[1fr_1fr_auto] min-h-[2.5rem]';
22
+
23
+ type FunctionsRegistryProps = {
24
+ space: Space;
25
+ };
26
+
27
+ export const FunctionsRegistry = ({ space }: FunctionsRegistryProps) => {
28
+ const client = useClient();
29
+ const [loading, setLoading] = useState(true);
30
+ const [functions, setFunctions] = useState<OperationModule.Operation.PersistentOperation[]>([]);
31
+ const { t } = useTranslation(meta.id);
32
+
33
+ const dbFunctions = useQuery(space.db, Filter.type(OperationModule.Operation.PersistentOperation));
34
+
35
+ const state = (func: OperationModule.Operation.PersistentOperation) => {
36
+ const dbFunction = dbFunctions.find((f) => f.key === func.key);
37
+ if (!dbFunction) {
38
+ return 'import';
39
+ }
40
+ if (dbFunction.version === func.version && dbFunction.updated === func.updated) {
41
+ return 'none';
42
+ }
43
+ return 'update';
44
+ };
45
+
46
+ useAsyncEffect(async () => {
47
+ setLoading(true);
48
+ const functions = await getDeployedFunctions(Context.default(), client, true);
49
+ setFunctions(functions);
50
+ setLoading(false);
51
+ }, []);
52
+
53
+ const hanleImportOrUpdate = useCallback(
54
+ async (func: OperationModule.Operation.PersistentOperation) => {
55
+ const functions = await space.db
56
+ .query(Query.type(OperationModule.Operation.PersistentOperation, { key: func.key }))
57
+ .run();
58
+ const [existingFunc] = functions;
59
+ if (!existingFunc) {
60
+ space.db.add(func);
61
+ return;
62
+ }
63
+ OperationModule.Operation.setFrom(existingFunc, func);
64
+ },
65
+ [space],
66
+ );
67
+
68
+ return (
69
+ <Settings.Panel>
70
+ {functions.length > 0 && (
71
+ <List.Root<OperationModule.Operation.PersistentOperation>
72
+ items={functions}
73
+ isItem={Schema.is(OperationModule.Operation.PersistentOperation)}
74
+ getId={(func) => func.id}
75
+ >
76
+ {({ items }) => (
77
+ <div role='list' className='flex flex-col w-full'>
78
+ {items?.map((func) => (
79
+ <List.Item<OperationModule.Operation.PersistentOperation>
80
+ key={func.id}
81
+ item={func}
82
+ classNames={mx(grid, ghostHover, 'items-center', 'px-2', 'min-h-[3rem]')}
83
+ >
84
+ <div className='flex flex-col truncate'>
85
+ <List.ItemTitle classNames='truncate'>{func.name}</List.ItemTitle>
86
+ <div className='text-xs text-description truncate'>{func.key}</div>
87
+ </div>
88
+ <div className='flex flex-col truncate'>
89
+ <div className='text-xs text-description truncate'>{func.version}</div>
90
+ <div className='text-xs text-description truncate'>
91
+ {func.updated ? `Uploaded ${new Date(func.updated).toLocaleString()}` : ''}
92
+ </div>
93
+ </div>
94
+
95
+ <IconButton
96
+ iconOnly
97
+ icon={state(func) === 'update' ? 'ph--arrows-clockwise--regular' : 'ph--download--regular'}
98
+ label={
99
+ state(func) === 'update' ? t('update-function-button.label') : t('import-function-button.label')
100
+ }
101
+ disabled={state(func) === 'none'}
102
+ onClick={() => hanleImportOrUpdate(func)}
103
+ />
104
+ </List.Item>
105
+ ))}
106
+ </div>
107
+ )}
108
+ </List.Root>
109
+ )}
110
+
111
+ {loading && <div className='text-center py-4 text-gray-500'>{t('loading-functions.message')}</div>}
112
+ </Settings.Panel>
113
+ );
114
+ };
@@ -0,0 +1,8 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { FunctionsRegistry } from './FunctionsRegistry';
6
+
7
+ export { FunctionsRegistry };
8
+ export default FunctionsRegistry;