@everysk/apps-sdk 0.2.0 → 0.3.0-beta-20261016180126

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 (349) hide show
  1. package/README.md +298 -12
  2. package/dist/EveryskApp.d.ts +34 -2
  3. package/dist/EveryskApp.d.ts.map +1 -1
  4. package/dist/EveryskApp.js +35 -2
  5. package/dist/EveryskApp.js.map +1 -1
  6. package/dist/agGrid/handleRowsScrollEnd.d.ts +53 -0
  7. package/dist/agGrid/handleRowsScrollEnd.d.ts.map +1 -0
  8. package/dist/agGrid/handleRowsScrollEnd.js +47 -0
  9. package/dist/agGrid/handleRowsScrollEnd.js.map +1 -0
  10. package/dist/agGrid/index.d.ts +2 -0
  11. package/dist/agGrid/index.d.ts.map +1 -1
  12. package/dist/agGrid/index.js +2 -1
  13. package/dist/api/datastore.d.ts +10 -8
  14. package/dist/api/datastore.d.ts.map +1 -1
  15. package/dist/api/datastore.js +24 -55
  16. package/dist/api/datastore.js.map +1 -1
  17. package/dist/api/entityQuery.d.ts +46 -41
  18. package/dist/api/entityQuery.d.ts.map +1 -1
  19. package/dist/api/entityQuery.js +55 -14
  20. package/dist/api/entityQuery.js.map +1 -1
  21. package/dist/api/file.d.ts +26 -9
  22. package/dist/api/file.d.ts.map +1 -1
  23. package/dist/api/file.js +45 -55
  24. package/dist/api/file.js.map +1 -1
  25. package/dist/api/portfolio.d.ts +11 -8
  26. package/dist/api/portfolio.d.ts.map +1 -1
  27. package/dist/api/portfolio.js +24 -55
  28. package/dist/api/portfolio.js.map +1 -1
  29. package/dist/api/profile.d.ts +3 -2
  30. package/dist/api/profile.d.ts.map +1 -1
  31. package/dist/api/profile.js +4 -9
  32. package/dist/api/profile.js.map +1 -1
  33. package/dist/api/request.d.ts +106 -0
  34. package/dist/api/request.d.ts.map +1 -0
  35. package/dist/api/request.js +132 -0
  36. package/dist/api/request.js.map +1 -0
  37. package/dist/api/websocketsConfig.d.ts +7 -2
  38. package/dist/api/websocketsConfig.d.ts.map +1 -1
  39. package/dist/api/websocketsConfig.js +2 -10
  40. package/dist/api/websocketsConfig.js.map +1 -1
  41. package/dist/api/workflow.d.ts +84 -89
  42. package/dist/api/workflow.d.ts.map +1 -1
  43. package/dist/api/workflow.js +121 -208
  44. package/dist/api/workflow.js.map +1 -1
  45. package/dist/api/workspace.d.ts +18 -8
  46. package/dist/api/workspace.d.ts.map +1 -1
  47. package/dist/api/workspace.js +21 -26
  48. package/dist/api/workspace.js.map +1 -1
  49. package/dist/components/ui/Footer/index.d.ts.map +1 -1
  50. package/dist/components/ui/Footer/index.js.map +1 -1
  51. package/dist/components/ui/GlobalLoading/index.d.ts.map +1 -1
  52. package/dist/components/ui/GlobalLoading/index.js.map +1 -1
  53. package/dist/components/ui/PageContainer/index.d.ts +1 -1
  54. package/dist/components/ui/PageContainer/index.d.ts.map +1 -1
  55. package/dist/components/ui/PageContainer/index.js.map +1 -1
  56. package/dist/components/ui/Toolbar/index.d.ts +40 -3
  57. package/dist/components/ui/Toolbar/index.d.ts.map +1 -1
  58. package/dist/components/ui/Toolbar/index.js +42 -15
  59. package/dist/components/ui/Toolbar/index.js.map +1 -1
  60. package/dist/contexts/appAlertContext/provider.js.map +1 -1
  61. package/dist/contexts/appAlertContext/types.d.ts.map +1 -1
  62. package/dist/contexts/appConfigContext/index.d.ts.map +1 -1
  63. package/dist/contexts/appConfigContext/index.js.map +1 -1
  64. package/dist/contexts/appConfigContext/provider.d.ts.map +1 -1
  65. package/dist/contexts/appConfigContext/provider.js.map +1 -1
  66. package/dist/contexts/webSocketContext/handler.d.ts +3 -2
  67. package/dist/contexts/webSocketContext/handler.d.ts.map +1 -1
  68. package/dist/contexts/webSocketContext/handler.js.map +1 -1
  69. package/dist/contexts/webSocketContext/hooks/useWebSocket/index.d.ts +1 -1
  70. package/dist/contexts/webSocketContext/hooks/useWebSocket/index.js +1 -1
  71. package/dist/contexts/webSocketContext/hooks/useWebSocket/index.js.map +1 -1
  72. package/dist/contexts/webSocketContext/provider.d.ts +6 -3
  73. package/dist/contexts/webSocketContext/provider.d.ts.map +1 -1
  74. package/dist/contexts/webSocketContext/provider.js +42 -7
  75. package/dist/contexts/webSocketContext/provider.js.map +1 -1
  76. package/dist/contexts/webSocketContext/types.d.ts +7 -5
  77. package/dist/contexts/webSocketContext/types.d.ts.map +1 -1
  78. package/dist/file/index.d.ts +2 -0
  79. package/dist/file/index.d.ts.map +1 -1
  80. package/dist/hooks/datastore/useDatastoreMutations.d.ts +4 -4
  81. package/dist/hooks/datastore/useDatastoreMutations.d.ts.map +1 -1
  82. package/dist/hooks/datastore/useDatastoreMutations.js +17 -55
  83. package/dist/hooks/datastore/useDatastoreMutations.js.map +1 -1
  84. package/dist/hooks/datastore/useFetchDatastore.d.ts +30 -41
  85. package/dist/hooks/datastore/useFetchDatastore.d.ts.map +1 -1
  86. package/dist/hooks/datastore/useFetchDatastore.js +25 -59
  87. package/dist/hooks/datastore/useFetchDatastore.js.map +1 -1
  88. package/dist/hooks/datastore/useFetchDatastores.d.ts +47 -52
  89. package/dist/hooks/datastore/useFetchDatastores.d.ts.map +1 -1
  90. package/dist/hooks/datastore/useFetchDatastores.js +55 -70
  91. package/dist/hooks/datastore/useFetchDatastores.js.map +1 -1
  92. package/dist/hooks/file/fileData.d.ts +17 -0
  93. package/dist/hooks/file/fileData.d.ts.map +1 -0
  94. package/dist/hooks/file/fileData.js +10 -0
  95. package/dist/hooks/file/fileData.js.map +1 -0
  96. package/dist/hooks/file/useFetchFile.d.ts +38 -58
  97. package/dist/hooks/file/useFetchFile.d.ts.map +1 -1
  98. package/dist/hooks/file/useFetchFile.js +26 -66
  99. package/dist/hooks/file/useFetchFile.js.map +1 -1
  100. package/dist/hooks/file/useFetchFiles.d.ts +18 -52
  101. package/dist/hooks/file/useFetchFiles.d.ts.map +1 -1
  102. package/dist/hooks/file/useFetchFiles.js +53 -69
  103. package/dist/hooks/file/useFetchFiles.js.map +1 -1
  104. package/dist/hooks/file/useFileMutations.d.ts +9 -8
  105. package/dist/hooks/file/useFileMutations.d.ts.map +1 -1
  106. package/dist/hooks/file/useFileMutations.js +23 -60
  107. package/dist/hooks/file/useFileMutations.js.map +1 -1
  108. package/dist/hooks/portfolio/useFetchPortfolio.d.ts +16 -50
  109. package/dist/hooks/portfolio/useFetchPortfolio.d.ts.map +1 -1
  110. package/dist/hooks/portfolio/useFetchPortfolio.js +15 -63
  111. package/dist/hooks/portfolio/useFetchPortfolio.js.map +1 -1
  112. package/dist/hooks/portfolio/useFetchPortfolios.d.ts +13 -52
  113. package/dist/hooks/portfolio/useFetchPortfolios.d.ts.map +1 -1
  114. package/dist/hooks/portfolio/useFetchPortfolios.js +29 -71
  115. package/dist/hooks/portfolio/useFetchPortfolios.js.map +1 -1
  116. package/dist/hooks/portfolio/usePortfolioMutations.d.ts +8 -8
  117. package/dist/hooks/portfolio/usePortfolioMutations.d.ts.map +1 -1
  118. package/dist/hooks/portfolio/usePortfolioMutations.js +20 -58
  119. package/dist/hooks/portfolio/usePortfolioMutations.js.map +1 -1
  120. package/dist/hooks/profile/useFetchProfile.d.ts +18 -44
  121. package/dist/hooks/profile/useFetchProfile.d.ts.map +1 -1
  122. package/dist/hooks/profile/useFetchProfile.js +16 -48
  123. package/dist/hooks/profile/useFetchProfile.js.map +1 -1
  124. package/dist/hooks/useAxios.js.map +1 -1
  125. package/dist/hooks/useEntityInfiniteQuery.d.ts +88 -0
  126. package/dist/hooks/useEntityInfiniteQuery.d.ts.map +1 -0
  127. package/dist/hooks/useEntityInfiniteQuery.js +111 -0
  128. package/dist/hooks/useEntityInfiniteQuery.js.map +1 -0
  129. package/dist/hooks/useEntityMutation.d.ts +47 -0
  130. package/dist/hooks/useEntityMutation.d.ts.map +1 -0
  131. package/dist/hooks/useEntityMutation.js +58 -0
  132. package/dist/hooks/useEntityMutation.js.map +1 -0
  133. package/dist/hooks/useEntityQuery.d.ts +68 -0
  134. package/dist/hooks/useEntityQuery.d.ts.map +1 -0
  135. package/dist/hooks/useEntityQuery.js +75 -0
  136. package/dist/hooks/useEntityQuery.js.map +1 -0
  137. package/dist/hooks/workflow/useFetchWorkerExecution.d.ts +17 -34
  138. package/dist/hooks/workflow/useFetchWorkerExecution.d.ts.map +1 -1
  139. package/dist/hooks/workflow/useFetchWorkerExecution.js +16 -46
  140. package/dist/hooks/workflow/useFetchWorkerExecution.js.map +1 -1
  141. package/dist/hooks/workflow/useFetchWorkerExecutions.d.ts +179 -41
  142. package/dist/hooks/workflow/useFetchWorkerExecutions.d.ts.map +1 -1
  143. package/dist/hooks/workflow/useFetchWorkerExecutions.js +29 -62
  144. package/dist/hooks/workflow/useFetchWorkerExecutions.js.map +1 -1
  145. package/dist/hooks/workflow/useFetchWorkflow.d.ts +16 -35
  146. package/dist/hooks/workflow/useFetchWorkflow.d.ts.map +1 -1
  147. package/dist/hooks/workflow/useFetchWorkflow.js +15 -48
  148. package/dist/hooks/workflow/useFetchWorkflow.js.map +1 -1
  149. package/dist/hooks/workflow/useFetchWorkflowExecution.d.ts +17 -34
  150. package/dist/hooks/workflow/useFetchWorkflowExecution.d.ts.map +1 -1
  151. package/dist/hooks/workflow/useFetchWorkflowExecution.js +16 -46
  152. package/dist/hooks/workflow/useFetchWorkflowExecution.js.map +1 -1
  153. package/dist/hooks/workflow/useFetchWorkflowExecutions.d.ts +22 -36
  154. package/dist/hooks/workflow/useFetchWorkflowExecutions.d.ts.map +1 -1
  155. package/dist/hooks/workflow/useFetchWorkflowExecutions.js +43 -56
  156. package/dist/hooks/workflow/useFetchWorkflowExecutions.js.map +1 -1
  157. package/dist/hooks/workflow/useFetchWorkflows.d.ts +20 -37
  158. package/dist/hooks/workflow/useFetchWorkflows.d.ts.map +1 -1
  159. package/dist/hooks/workflow/useFetchWorkflows.js +32 -47
  160. package/dist/hooks/workflow/useFetchWorkflows.js.map +1 -1
  161. package/dist/hooks/workflow/useRunWorkflow.d.ts +21 -16
  162. package/dist/hooks/workflow/useRunWorkflow.d.ts.map +1 -1
  163. package/dist/hooks/workflow/useRunWorkflow.js +66 -29
  164. package/dist/hooks/workflow/useRunWorkflow.js.map +1 -1
  165. package/dist/hooks/workflow/useTriggerWorkflow.d.ts +20 -0
  166. package/dist/hooks/workflow/useTriggerWorkflow.d.ts.map +1 -0
  167. package/dist/hooks/workflow/useTriggerWorkflow.js +32 -0
  168. package/dist/hooks/workflow/useTriggerWorkflow.js.map +1 -0
  169. package/dist/hooks/workspace/useFetchWorkspace.d.ts +19 -34
  170. package/dist/hooks/workspace/useFetchWorkspace.d.ts.map +1 -1
  171. package/dist/hooks/workspace/useFetchWorkspace.js +16 -46
  172. package/dist/hooks/workspace/useFetchWorkspace.js.map +1 -1
  173. package/dist/hooks/workspace/useFetchWorkspaces.d.ts +17 -37
  174. package/dist/hooks/workspace/useFetchWorkspaces.d.ts.map +1 -1
  175. package/dist/hooks/workspace/useFetchWorkspaces.js +25 -47
  176. package/dist/hooks/workspace/useFetchWorkspaces.js.map +1 -1
  177. package/dist/index.d.ts +13 -10
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +13 -11
  180. package/dist/lib/abort.d.ts +58 -0
  181. package/dist/lib/abort.d.ts.map +1 -0
  182. package/dist/lib/abort.js +112 -0
  183. package/dist/lib/abort.js.map +1 -0
  184. package/dist/lib/datastoreTable.d.ts +59 -54
  185. package/dist/lib/datastoreTable.d.ts.map +1 -1
  186. package/dist/lib/datastoreTable.js +58 -62
  187. package/dist/lib/datastoreTable.js.map +1 -1
  188. package/dist/lib/file.d.ts +34 -0
  189. package/dist/lib/file.d.ts.map +1 -0
  190. package/dist/lib/file.js +63 -0
  191. package/dist/lib/file.js.map +1 -0
  192. package/dist/lib/queryClient.d.ts.map +1 -1
  193. package/dist/lib/queryClient.js.map +1 -1
  194. package/dist/lib/workflowRun.d.ts +91 -0
  195. package/dist/lib/workflowRun.d.ts.map +1 -0
  196. package/dist/lib/workflowRun.js +224 -0
  197. package/dist/lib/workflowRun.js.map +1 -0
  198. package/dist/socket/index.d.ts +1 -3
  199. package/dist/socket/index.d.ts.map +1 -1
  200. package/dist/socket/index.js +3 -5
  201. package/dist/test/mocks/index.d.ts +1 -1
  202. package/dist/test/mocks/index.d.ts.map +1 -1
  203. package/dist/test/renderWithProviders.d.ts +2 -2
  204. package/dist/test/renderWithProviders.d.ts.map +1 -1
  205. package/dist/test/renderWithProviders.js.map +1 -1
  206. package/dist/test/setup.js.map +1 -1
  207. package/dist/theme/index.d.ts +1 -1
  208. package/dist/theme/index.d.ts.map +1 -1
  209. package/dist/theme/overrides/index.d.ts +4 -2
  210. package/dist/theme/overrides/index.d.ts.map +1 -1
  211. package/dist/theme/overrides/index.js +5 -3
  212. package/dist/theme/overrides/index.js.map +1 -1
  213. package/dist/theme/overrides/muiButton.d.ts +2 -2
  214. package/dist/theme/overrides/muiButton.js.map +1 -1
  215. package/dist/theme/overrides/muiCssBaseline.d.ts.map +1 -1
  216. package/dist/theme/overrides/muiCssBaseline.js.map +1 -1
  217. package/dist/theme/overrides/muiDatePicker.js.map +1 -1
  218. package/dist/theme/overrides/muiFormControl.js.map +1 -1
  219. package/dist/theme/overrides/muiIconButton.d.ts +2 -2
  220. package/dist/theme/overrides/muiIconButton.js.map +1 -1
  221. package/dist/theme/overrides/muiSelect.d.ts.map +1 -1
  222. package/dist/theme/overrides/muiSelect.js.map +1 -1
  223. package/dist/theme/overrides/muiSvgIcon.d.ts.map +1 -1
  224. package/dist/theme/overrides/muiSvgIcon.js.map +1 -1
  225. package/dist/theme/overrides/muiTextField.js.map +1 -1
  226. package/dist/theme/overrides/muiToggleButton.d.ts +2 -2
  227. package/dist/theme/overrides/muiToggleButton.js.map +1 -1
  228. package/dist/theme/overrides/muiToggleButtonGroup.d.ts +2 -2
  229. package/dist/theme/overrides/muiToggleButtonGroup.js.map +1 -1
  230. package/dist/theme/themes/DefaultDark.js +2 -2
  231. package/dist/theme/themes/DefaultDark.js.map +1 -1
  232. package/dist/theme/themes/DefaultLight.js +2 -2
  233. package/dist/theme/themes/DefaultLight.js.map +1 -1
  234. package/dist/theme/themes/EveryskDark.js +2 -2
  235. package/dist/theme/themes/EveryskDark.js.map +1 -1
  236. package/dist/theme/themes/EveryskLight.js +2 -2
  237. package/dist/theme/themes/EveryskLight.js.map +1 -1
  238. package/dist/theme/themes/MidnightEmber.js +2 -2
  239. package/dist/theme/themes/MidnightEmber.js.map +1 -1
  240. package/dist/types/anyRecord.d.ts.map +1 -1
  241. package/dist/types/datastore.d.ts +35 -5
  242. package/dist/types/datastore.d.ts.map +1 -1
  243. package/dist/types/datastoreDataFormat.d.ts +13 -0
  244. package/dist/types/datastoreDataFormat.d.ts.map +1 -0
  245. package/dist/types/entityQuery.d.ts +86 -16
  246. package/dist/types/entityQuery.d.ts.map +1 -1
  247. package/dist/types/file.d.ts +7 -3
  248. package/dist/types/file.d.ts.map +1 -1
  249. package/dist/types/portfolio.d.ts +1 -3
  250. package/dist/types/portfolio.d.ts.map +1 -1
  251. package/dist/types/profile.d.ts +0 -10
  252. package/dist/types/profile.d.ts.map +1 -1
  253. package/dist/types/workflow.d.ts +35 -6
  254. package/dist/types/workflow.d.ts.map +1 -1
  255. package/dist/types/workspace.d.ts +1 -2
  256. package/dist/types/workspace.d.ts.map +1 -1
  257. package/dist/ui/index.d.ts +1 -1
  258. package/dist/ui/index.d.ts.map +1 -1
  259. package/dist/workflow/index.d.ts +2 -0
  260. package/dist/workflow/index.d.ts.map +1 -1
  261. package/package.json +1 -1
  262. package/src/EveryskApp.tsx +53 -2
  263. package/src/agGrid/handleRowsScrollEnd.ts +71 -0
  264. package/src/agGrid/index.ts +5 -1
  265. package/src/api/datastore.ts +79 -107
  266. package/src/api/entityQuery.ts +86 -17
  267. package/src/api/file.ts +110 -103
  268. package/src/api/portfolio.ts +82 -125
  269. package/src/api/profile.ts +25 -18
  270. package/src/api/request.ts +168 -0
  271. package/src/api/websocketsConfig.ts +11 -19
  272. package/src/api/workflow.ts +232 -334
  273. package/src/api/workspace.ts +43 -43
  274. package/src/components/ui/Footer/index.tsx +8 -7
  275. package/src/components/ui/GlobalLoading/index.tsx +13 -3
  276. package/src/components/ui/PageContainer/index.tsx +9 -2
  277. package/src/components/ui/Toolbar/index.tsx +105 -30
  278. package/src/contexts/appAlertContext/provider.tsx +1 -1
  279. package/src/contexts/appAlertContext/types.ts +3 -3
  280. package/src/contexts/appConfigContext/index.tsx +1 -1
  281. package/src/contexts/appConfigContext/provider.tsx +1 -5
  282. package/src/contexts/webSocketContext/handler.ts +8 -2
  283. package/src/contexts/webSocketContext/hooks/useWebSocket/index.ts +1 -1
  284. package/src/contexts/webSocketContext/provider.tsx +64 -14
  285. package/src/contexts/webSocketContext/types.ts +7 -6
  286. package/src/file/index.ts +2 -0
  287. package/src/hooks/datastore/useDatastoreMutations.ts +19 -60
  288. package/src/hooks/datastore/useFetchDatastore.ts +45 -73
  289. package/src/hooks/datastore/useFetchDatastores.ts +106 -131
  290. package/src/hooks/file/fileData.ts +19 -0
  291. package/src/hooks/file/useFetchFile.ts +41 -76
  292. package/src/hooks/file/useFetchFiles.ts +92 -87
  293. package/src/hooks/file/useFileMutations.ts +23 -73
  294. package/src/hooks/portfolio/useFetchPortfolio.ts +26 -78
  295. package/src/hooks/portfolio/useFetchPortfolios.ts +50 -93
  296. package/src/hooks/portfolio/usePortfolioMutations.ts +22 -72
  297. package/src/hooks/profile/useFetchProfile.ts +24 -65
  298. package/src/hooks/useAxios.ts +5 -5
  299. package/src/hooks/useEntityInfiniteQuery.ts +193 -0
  300. package/src/hooks/useEntityMutation.ts +70 -0
  301. package/src/hooks/useEntityQuery.ts +117 -0
  302. package/src/hooks/workflow/useFetchWorkerExecution.ts +27 -56
  303. package/src/hooks/workflow/useFetchWorkerExecutions.ts +54 -79
  304. package/src/hooks/workflow/useFetchWorkflow.ts +24 -58
  305. package/src/hooks/workflow/useFetchWorkflowExecution.ts +27 -57
  306. package/src/hooks/workflow/useFetchWorkflowExecutions.ts +64 -73
  307. package/src/hooks/workflow/useFetchWorkflows.ts +48 -64
  308. package/src/hooks/workflow/useRunWorkflow.ts +82 -36
  309. package/src/hooks/workflow/useTriggerWorkflow.ts +36 -0
  310. package/src/hooks/workspace/useFetchWorkspace.ts +25 -58
  311. package/src/hooks/workspace/useFetchWorkspaces.ts +38 -69
  312. package/src/index.ts +34 -19
  313. package/src/lib/abort.ts +131 -0
  314. package/src/lib/datastoreTable.ts +127 -74
  315. package/src/lib/file.ts +67 -0
  316. package/src/lib/queryClient.ts +11 -13
  317. package/src/lib/workflowRun.ts +329 -0
  318. package/src/socket/index.ts +1 -3
  319. package/src/test/mocks/index.ts +1 -5
  320. package/src/test/renderWithProviders.tsx +8 -3
  321. package/src/test/setup.ts +16 -16
  322. package/src/theme/index.ts +1 -1
  323. package/src/theme/overrides/index.ts +5 -3
  324. package/src/theme/overrides/muiButton.ts +3 -3
  325. package/src/theme/overrides/muiCssBaseline.ts +11 -12
  326. package/src/theme/overrides/muiDatePicker.ts +2 -2
  327. package/src/theme/overrides/muiFormControl.ts +2 -2
  328. package/src/theme/overrides/muiIconButton.ts +3 -3
  329. package/src/theme/overrides/muiSelect.ts +2 -2
  330. package/src/theme/overrides/muiSvgIcon.ts +2 -2
  331. package/src/theme/overrides/muiTextField.ts +1 -1
  332. package/src/theme/overrides/muiToggleButton.ts +4 -4
  333. package/src/theme/overrides/muiToggleButtonGroup.ts +4 -4
  334. package/src/theme/themes/DefaultDark.ts +2 -2
  335. package/src/theme/themes/DefaultLight.ts +2 -2
  336. package/src/theme/themes/EveryskDark.ts +2 -2
  337. package/src/theme/themes/EveryskLight.ts +2 -2
  338. package/src/theme/themes/MidnightEmber.ts +15 -15
  339. package/src/types/anyRecord.ts +2 -2
  340. package/src/types/datastore.ts +56 -22
  341. package/src/types/datastoreDataFormat.ts +12 -0
  342. package/src/types/entityQuery.ts +122 -38
  343. package/src/types/file.ts +8 -6
  344. package/src/types/portfolio.ts +70 -74
  345. package/src/types/profile.ts +0 -11
  346. package/src/types/workflow.ts +38 -9
  347. package/src/types/workspace.ts +2 -4
  348. package/src/ui/index.ts +1 -1
  349. package/src/workflow/index.ts +5 -0
package/README.md CHANGED
@@ -63,7 +63,7 @@ to nested JSX in the right slot.
63
63
  | `theme` | A MUI theme. Omitted, the app mounts the Everysk dark theme |
64
64
  | `queryClient` | Defaults to a fresh `createQueryClient()`, created once per mount |
65
65
  | `appId`, `appEnvironmentVar` | Override `window.APP_ID` / `window.APP_CONFIG`, for tests and hosts that resolve configuration themselves |
66
- | `providers` | Optional providers, mounted inside the core tree |
66
+ | `providers` | Optional providers, mounted inside the core tree. One configured by props goes in through `withProps(Provider, props)` |
67
67
 
68
68
  ## What it exports
69
69
 
@@ -83,10 +83,57 @@ a list rendered.
83
83
  | File | `useFetchFile` | `useFetchFiles` | `useFileMutations` |
84
84
  | Workspace | `useFetchWorkspace` | `useFetchWorkspaces` | — |
85
85
  | Profile | `useFetchProfile` | — | — |
86
- | Workflow | `useFetchWorkflow` | `useFetchWorkflows` | `useRunWorkflow` |
86
+ | Workflow | `useFetchWorkflow` | `useFetchWorkflows` | `useTriggerWorkflow`, `useRunWorkflow` |
87
87
 
88
88
  Workflow carries four more for the run side: `useFetchWorkflowExecution(s)` and
89
- `useFetchWorkerExecution(s)`.
89
+ `useFetchWorkerExecution(s)`. `useFetchWorkflowExecutions` pages by cursor and takes the
90
+ endpoint's own filters, `workerId` and `withResume`, not a `filters` list.
91
+ `useFetchWorkerExecutions` is not paginated: the API returns every worker of one execution at
92
+ once, grouped by worker, so it returns `{ data, …, queryKey }` like a single-entity hook. Narrow
93
+ each group on `is_parallel_execution`: a worker that ran in parallel carries `executions`, any
94
+ other one `execution`.
95
+
96
+ `useFetchWorkflows` and `useFetchWorkspaces` take a text `search` rather than filters: their
97
+ endpoints search by name prefix (and, for workflows, by `#tag` or id). `useFetchWorkflows`
98
+ requires a `workspace`, like every other hook — without one the API would read `main`.
99
+ `useFetchWorkspaces` takes none: it lists the account's workspaces.
100
+
101
+ A file's content comes as base64 in `data`, a third larger than the file, so `useFetchFile` and
102
+ `useFetchFiles` leave it out unless `withData: true` asks for it (the list through the projection
103
+ `-data`, added to a `projection` of the app's own that only excludes fields; one that includes
104
+ fields is sent as given, and the content is still removed). To
105
+ show or download a file, use its `url` (`/file/<hash>`) in an `<iframe>`, an `<img>` or an
106
+ `<a href download>`: the platform serves it inline with the user's permissions, and the
107
+ `@everysk/apps-devkit` dev server and `vite preview` serve it too. Do not build a `blob:` URL
108
+ from the content: the app page's CSP blocks `blob:` frames, images and media. To upload,
109
+ `fileToBase64(file)` turns a `File` from an `<input type="file">` into the raw base64
110
+ `createFile` and `updateFile` take, and which they insist on: a data URL is refused, since the
111
+ API would store its prefix as part of the file. `base64ToBlob(data, contentType)` decodes one, to
112
+ work on the bytes in code (a CSV, a zip); given a file read without its content, it warns and
113
+ gives an empty blob. An app uploads
114
+ in one JSON request: the app's `/api` reads no multipart body.
115
+
116
+ ```tsx
117
+ const { data: file } = useFetchFile({ id, workspace });
118
+
119
+ return file ? <iframe src={file.url} title={file.name} /> : null;
120
+ ```
121
+
122
+ `useTriggerWorkflow` starts a run and resolves with the started `WorkflowExecution`; it needs no
123
+ websocket. `useRunWorkflow` starts one and waits for it to end **over the websocket**,
124
+ resolving with `{ execution, workerExecution, result }`, so it needs `WebSocketProvider` (from
125
+ `@everysk/apps-sdk/socket`) in `EveryskApp`'s `providers`, and throws on render without it.
126
+
127
+ The backend announces each worker and the run's end on the app's channel; the end itself is
128
+ then read from the API, and the Ender's output with it. Because those events are not replayed,
129
+ it also reads the execution after a minute without any event of the run
130
+ (`waitOptions.silenceMs`) and whenever the socket reconnects, and gives up after five minutes
131
+ (`waitOptions.timeoutMs`). It fails when the socket stays unavailable for 30 seconds
132
+ (`waitOptions.unavailableAfterMs`), since the end cannot be followed without it, and does not
133
+ start the run before the socket is connected. Leaving the
134
+ screen, or aborting `waitOptions.signal`, stops it without an error alert; the mutation then fails
135
+ with a `WorkflowRunAbortedError`, so `error instanceof WorkflowRunAbortedError` tells a stop from
136
+ a failure.
90
137
 
91
138
  ```tsx
92
139
  const { query, queryKey } = useFetchPortfolios({
@@ -103,6 +150,197 @@ directly, so `const { data, isLoading, queryKey } = useFetchPortfolio({ id, work
103
150
  Mutations return `{ create, update, remove }`, each a React Query mutation that shows an alert on
104
151
  success and failure through `useAppAlert`.
105
152
 
153
+ #### Using a list hook without wrapping it
154
+
155
+ The six list hooks (`useFetchDatastores`, `useFetchFiles`, `useFetchPortfolios`,
156
+ `useFetchWorkflows`, `useFetchWorkflowExecutions`, `useFetchWorkspaces`) share one set of props, meant to cover what apps used to write a hook of
157
+ their own for:
158
+
159
+ | Prop | Default | |
160
+ |---|---|---|
161
+ | `select` | — | Shapes the merged list: `query.data` is what it returns. Applied after the pages are merged |
162
+ | `fetchAllPages` | `false` | Reads to the last page before reporting success |
163
+ | `pageSize` | `10` | Items per request |
164
+ | `queryOptions` | — | Passed through to `useInfiniteQuery`, except `select`, which is the prop above |
165
+
166
+ ```tsx
167
+ // Module scope, so the reference is stable: TanStack reruns a select whose reference changes.
168
+ const toRows = (datastores: ParsedDatastore[]) =>
169
+ datastores.filter(isTableDatastore).flatMap((datastore) => datastore.data);
170
+
171
+ const { query, queryKey } = useFetchDatastores({
172
+ filters: [{ field: "workspace", value: workspace }],
173
+ select: toRows, // query.data: AnyRecord[]
174
+ });
175
+ ```
176
+
177
+ - **`select` shapes what one caller sees; the cache keeps the pages.** Write to it with the
178
+ returned `queryKey` and the hook's cache type (`DatastoresCache`, `FilesCache`, …), and every
179
+ `select` reruns on the new data:
180
+
181
+ ```ts
182
+ queryClient.setQueryData<DatastoresCache>(queryKey, (cache) => cache && {
183
+ ...cache,
184
+ pages: cache.pages.map((page) => ({
185
+ ...page,
186
+ datastores: page.datastores.map((datastore) =>
187
+ isTableDatastore(datastore)
188
+ ? { ...datastore, data: datastore.data.map((row) => (row.UID === uid ? { ...row, ...patch } : row)) }
189
+ : datastore,
190
+ ),
191
+ })),
192
+ });
193
+ ```
194
+
195
+ - **`fetchAllPages` is for a view that consolidates the whole set**, where a partial read is a
196
+ wrong answer rather than a shorter one. Without it the hook reads one page and stops, which a
197
+ screen that never asks for more shows as silently missing data. It fails rather than stop short
198
+ after 100 pages, so raise `pageSize` with it — the limit counts requests, not items — and it
199
+ stops requesting the moment the screen that asked unmounts, so a user leaving mid-read does not
200
+ keep the backend walking the cursor.
201
+ - **Leaving a screen aborts its request.** Every list hook hands the query's `AbortSignal` to its
202
+ `api/` function, which passes it to axios: the HTTP request in flight is cancelled, not only the
203
+ query state. The `api/` list functions take it as an optional last argument.
204
+ - **The key holds everything that changes the response** — filters (field, operator and value),
205
+ `order`, `projection`, `pageSize` and `fetchAllPages` — after the base keys, so a metadata read
206
+ (`projection: "-data"`) and a full read of the same workspace never share an entry, and prefix
207
+ invalidation (`["workflow_executions", workflowId]`) still reaches every page size and filter.
208
+ - **`projection` takes one field or a list.** `["name", "date"]` returns only those; `["-data"]`
209
+ everything but `data`. Don't mix the two, and name only fields the entity has: the API answers
210
+ either with a 500. That is also why `"name,date"` fails — it reads as one field of that name. Filters take
211
+ `IN` and `NOT_IN` with a list as the value.
212
+ - **A missing workspace fails loudly.** Without a `workspace` filter, or with an empty one, the
213
+ hooks that take filters fail at once with an error naming the hook, log it with
214
+ `console.error` (once, in every environment), and send no request. The app config is in
215
+ `window.APP_CONFIG` before the app renders, so an empty workspace is a wrong or misspelled
216
+ config key — never a state to wait out, and not one to hide behind an empty screen.
217
+ - **A datastore is tabular or JSON, and a list can mix them.** The API does not say which, so the
218
+ shape of `data` decides: an array of rows is a table; an object or an array of objects is JSON.
219
+ They come back told apart by `format`:
220
+
221
+ | `format` | `data` | Also |
222
+ |---|---|---|
223
+ | `"table"` | row objects, one per matrix row — or, with `dataFormat: "matrix"`, the matrix unconverted | `fields`: the header row, in the datastore's order |
224
+ | `"json"` | the object or array of objects, untouched | — |
225
+
226
+ Narrow before reading rows — `isTableDatastore(datastore)` or `datastore.format === "table"`.
227
+ A table keeps its column order in `fields`, so saving back is `toDatastoreMatrix(datastore)`,
228
+ which returns a new matrix the caller owns, however the datastore was read — rebuilt from the
229
+ rows, patches included, or copied after a `dataFormat: "matrix"` read. `query.data` itself is
230
+ the query cache: do not edit it in place. A read that projects `data` away has nothing to tell
231
+ the kinds apart and comes back as an empty table, so do not save its result.
232
+ - **`dataFormat: "matrix"` skips the conversion** in `useFetchDatastores` and `useFetchDatastore`:
233
+ a tabular datastore's `data` stays the matrix, header row included, typed as such in
234
+ `query.data` and in `select`. Use it for a grid that takes arrays, an export, or a datastore too
235
+ large to convert one object per row. The default, `"object"`, is what lets an app patch a row
236
+ as `{ ...row, ...patch }`. The option is part of the cache key, so the two reads never share an
237
+ entry.
238
+ - **Paging on grid scroll** is `handleRowsScrollEnd`, in [`/ag-grid`](#paging-an-ag-grid-on-scroll),
239
+ kept out of the hooks so that it works with any of them.
240
+
241
+ #### A list hook for an endpoint the SDK does not cover
242
+
243
+ Compose `useEntityInfiniteQuery`, the hook the seven are built on, rather than copying one of
244
+ them. You say how to request a page and where its items are; it brings the rest — pages merged,
245
+ `select`, `fetchAllPages` (up to `FETCH_ALL_PAGES_LIMIT` pages), and a `query` that
246
+ `handleRowsScrollEnd` pages. Two ways to hold a request back: `configError` for what must always
247
+ be there (it fails loudly), `enabled` for what legitimately is not there yet, like a selection
248
+ (it stays idle).
249
+
250
+ ```ts
251
+ export const useFetchReports = <TData = Report[]>({ workspace, select, fetchAllPages = false }: Props<TData>) => {
252
+ const { api } = useAxios();
253
+ const filters = [{ field: "workspace", value: workspace }];
254
+ // Everything that changes the response goes in the key, fetchAllPages included.
255
+ const queryKey = buildQueryKey(["reports"], filters, { fetchAllPages });
256
+
257
+ const query = useEntityInfiniteQuery<ReportListResponse, "reports", TData>({
258
+ queryKey,
259
+ itemsKey: "reports", // ReportListResponse = { next_page_token: string | null; reports: Report[] }
260
+ fetchPage: (pageToken) => listReports(api, workspace, pageToken),
261
+ select,
262
+ fetchAllPages,
263
+ configError: hasWorkspaceFilter(filters) ? undefined : missingWorkspaceError("useFetchReports"),
264
+ });
265
+
266
+ return { query, queryKey };
267
+ };
268
+ ```
269
+
270
+ The key is the one thing it cannot check for you: two reads under the same key share one cache
271
+ entry, whatever each asked for. `buildQueryKey` builds it from the same parts as the request.
272
+
273
+ `fetchPage` also receives an `AbortSignal`, aborted when no screen reads the query any more.
274
+ Pass it on (`api.get(url, { signal })`) and a request in flight is cancelled too; ignore it and
275
+ the hook still stops `fetchAllPages` between pages.
276
+
277
+ #### Single-entity hooks
278
+
279
+ The eight (`useFetchDatastore`, `useFetchFile`, `useFetchPortfolio`, `useFetchWorkflow`,
280
+ `useFetchWorkflowExecution`, `useFetchWorkerExecution`, `useFetchWorkspace`, `useFetchProfile`),
281
+ and `useFetchWorkerExecutions`, which has the same shape,
282
+ share one set of doors with the lists:
283
+
284
+ | Prop | |
285
+ |---|---|
286
+ | `select` | Shapes the entity: `data` is what it returns, and is typed so |
287
+ | `queryOptions` | Passed through to `useQuery`, **merged over** the hook's defaults, except `select` |
288
+
289
+ - **Options are merged, not swapped.** Passing `queryOptions: { enabled }` keeps the defaults
290
+ (`refetchOnMount: "always"`, `staleTime: 0`, `gcTime: 0`; for the profile, fetched once per
291
+ session). The hooks used to replace them with whatever was passed, which fell back to
292
+ TanStack's own and cached for five minutes.
293
+ - **An empty id waits; an empty workspace fails.** An id usually comes from a selection the user
294
+ has not made yet, so while it is empty the hook stays idle — the guard apps wrote as
295
+ `queryOptions: { enabled: Boolean(id && workspace) }`, no longer needed. The workspace comes from
296
+ the app config, in place before the app renders, so an empty one is a misconfigured app: the
297
+ hook fails at once, without a request, with an error naming it, logged once with
298
+ `console.error` and shown as the alert — even when the id is empty too. `useFetchWorkspace` is
299
+ the exception that follows from the API: a workspace is identified by its name, so its `name`
300
+ is the id, and an empty one waits.
301
+ - **A failure shows an error alert**, through `useAppAlert`, as it always has.
302
+
303
+ For an endpoint the SDK does not cover, compose `useEntityQuery`, the hook the eight are built
304
+ on:
305
+
306
+ ```ts
307
+ export const useFetchReport = <TData = Report>({ id, workspace, select, queryOptions }: Props<TData>) => {
308
+ const { api } = useAxios();
309
+ const queryKey = ["report", id, workspace];
310
+
311
+ const query = useEntityQuery<Report, TData>({
312
+ queryKey,
313
+ fetchEntity: (signal) => getReport(api, id, workspace, signal),
314
+ select,
315
+ enabled: Boolean(id), // a selection not made yet: wait
316
+ configError: workspace ? undefined : missingWorkspaceError("useFetchReport"), // misconfigured: fail
317
+ queryOptions,
318
+ });
319
+
320
+ return { ...query, queryKey };
321
+ };
322
+ ```
323
+
324
+ `defaultQueryOptions` replaces the SDK's defaults for a hook whose data does not change, as the
325
+ profile's does. `fetchEntity` receives the query's `AbortSignal`: the SDK's hooks hand it to
326
+ their `api/` get function, which passes it to axios, so leaving a screen aborts the request.
327
+
328
+ #### Mutation hooks
329
+
330
+ `useDatastoreMutations`, `useFileMutations` and `usePortfolioMutations` are built on
331
+ `useEntityMutation`. For a write the SDK does not cover, compose it too: it invalidates
332
+ `queryKey` after a success (awaited, so code after `mutateAsync` sees fresh data) and shows the
333
+ success alert, or the failure's own message as an error alert, falling back to `errorMessage`.
334
+
335
+ ```ts
336
+ const share = useEntityMutation({
337
+ mutationFn: ({ id, workspace }: ShareReportProps) => shareReport(api, id, workspace),
338
+ queryKey, // optional: invalidated after a success
339
+ successMessage: "Report shared successfully.",
340
+ errorMessage: "Unable to share the report. Please try again.",
341
+ });
342
+ ```
343
+
106
344
  ### Components
107
345
 
108
346
  Five, all MUI underneath.
@@ -110,7 +348,7 @@ Five, all MUI underneath.
110
348
  | Component | Props | |
111
349
  |---|---|---|
112
350
  | `PageContainer` | `header?`, `footer?`, `contentClassName?` | The page frame: chrome around a scrolling content area |
113
- | `Toolbar` | `className?` | Top bar. Renders `EveryskIcon` and its children |
351
+ | `Toolbar` | `title?`, `subheader?`, `action?`, `startAction?`, `logo?`, `hideLogo?`, `position?`, `className?` | Top bar: `startAction`, logo (`EveryskIcon` unless replaced or hidden), title, children, then `action` at the far end. `position` defaults to `relative` |
114
352
  | `Footer` | `className?` | Bottom bar |
115
353
  | `GlobalLoading` | `isLoading`, `backdrop?`, `message?` | Full-page spinner. `backdrop` defaults to `true` |
116
354
  | `EveryskIcon` | every `SvgIconProps` except `color` | The brand mark |
@@ -119,8 +357,15 @@ Five, all MUI underneath.
119
357
  but a utility loses to MUI's own styles in the cascade, so reach for `sx` when one is dropped.
120
358
  `GlobalLoading` takes no `className`, and `EveryskIcon` gets one through `SvgIconProps`.
121
359
 
360
+ A shell that renders the `Toolbar` for its pages holds a `ToolbarContent` — `title`, `subheader`,
361
+ `action` and `children`, the part a page supplies — and adds `startAction`, the logo and
362
+ `position` itself.
363
+
122
364
  ```tsx
123
- <PageContainer header={<Toolbar>Trade Allocation</Toolbar>} footer={<Footer />}>
365
+ <PageContainer
366
+ header={<Toolbar title="Trade Allocation" action={<DateFilter />} />}
367
+ footer={<Footer />}
368
+ >
124
369
  <GlobalLoading isLoading={query.isLoading} message="Loading portfolios…" />
125
370
  {/* the page */}
126
371
  </PageContainer>
@@ -139,14 +384,19 @@ but a utility loses to MUI's own styles in the cascade, so reach for `sx` when o
139
384
  `EveryskDarkTheme`, `EveryskLightTheme`, `DefaultDarkTheme`, `DefaultLightTheme` and
140
385
  `MidnightEmberTheme`, plus `getTheme(name)` to resolve one by the name the CLI writes, and
141
386
  `getBrandTheme(mode)` / `getDefaultTheme(mode)` for the pair. `EveryskApp` mounts the brand dark
142
- theme unless given a `theme` prop.
387
+ theme unless given a `theme` prop. `componentOverrides` is the MUI component overrides every one
388
+ of them applies, for an app that builds its own theme.
143
389
 
144
390
  ### Query client and helpers
145
391
 
146
392
  `createQueryClient(config?)` builds the QueryClient `EveryskApp` mounts — a factory rather than a
147
393
  shared instance, because two apps or two tests sharing one client share its cache.
148
- `getQueryFn`, `toFilterTuple`, `buildQueryObject` and `buildQueryKey` are the pieces the entity
149
- hooks are built from, exported for a query the slices do not cover.
394
+ `buildQueryObject`, `buildQueryKey`, `hasWorkspaceFilter`, `missingWorkspaceError`,
395
+ `useEntityQuery`, `useEntityInfiniteQuery` and `useEntityMutation` are the pieces the entity hooks are built from,
396
+ exported for a query the slices do not cover. `parseDatastore` is the
397
+ conversion the datastore hooks apply to each datastore, for an app that seeds their cache, and
398
+ `isTableDatastore` narrows its result to a tabular one. `toDatastoreMatrix` gives a tabular
399
+ datastore's matrix back, and `matrixToObjects` / `objectsToMatrix` convert a matrix either way. They were `tableToObjects` / `objectsToTable` in 0.2.0.
150
400
 
151
401
  `useAxios`'s interceptor is worth knowing about if you write one of those: on `GET` and `DELETE`
152
402
  it parses the request's query object into the shape the platform API expects. It is attached in
@@ -176,13 +426,25 @@ nothing but `createQueryClient`. The subpath is what makes the peer genuinely op
176
426
 
177
427
  ## The AG Grid licence is a parameter
178
428
 
179
- ```ts
180
- // src/main.tsx
181
- import { setAgGridLicense } from "@everysk/apps-sdk/ag-grid";
429
+ ```tsx
430
+ // src/App.tsx
431
+ import { EveryskApp, withProps } from "@everysk/apps-sdk";
432
+ import { AgGridLicenseProvider } from "@everysk/apps-sdk/ag-grid";
182
433
 
183
- setAgGridLicense(import.meta.env.VITE_AG_GRID_LICENSE_KEY);
434
+ const agGridLicense = withProps(AgGridLicenseProvider, {
435
+ licenseKey: import.meta.env.VITE_AG_GRID_LICENSE_KEY,
436
+ });
437
+
438
+ export function App() {
439
+ return <EveryskApp providers={[agGridLicense]}>…</EveryskApp>;
440
+ }
184
441
  ```
185
442
 
443
+ `providers` mounts a bare component with no props, so a bare `AgGridLicenseProvider` there
444
+ registers nothing; `withProps` is what carries the key, checked against the provider's props. It describes
445
+ the provider rather than wrapping it, so calling it inside a render does not remount the app.
446
+ `setAgGridLicense(key)`, called once in `main.tsx`, does the same without a provider.
447
+
186
448
  This package never reads your environment. Vite replaces `import.meta.env.VITE_*` with a literal
187
449
  at the build of whoever compiles the file — for a published package that is this package's own
188
450
  CI, where the variable does not exist, so the value would freeze as `undefined` and your
@@ -193,6 +455,30 @@ is the AG Grid watermark.
193
455
  any theme — including one the app creates — gets a matching grid without a second table of
194
456
  colours.
195
457
 
458
+ ## Paging an AG Grid on scroll
459
+
460
+ ```tsx
461
+ import { handleRowsScrollEnd } from "@everysk/apps-sdk/ag-grid";
462
+
463
+ const { query } = useFetchDatastores({ filters, select: toRows });
464
+
465
+ <AgGridReact
466
+ rowData={query.data}
467
+ onBodyScrollEnd={(event) => handleRowsScrollEnd(event.api, query)}
468
+ onModelUpdated={(event) => handleRowsScrollEnd(event.api, query)}
469
+ />
470
+ ```
471
+
472
+ It requests the next page when the grid is scrolled within `scrollEndThreshold` pixels of its
473
+ last row, or shows no rows — the second event is what keeps a grid whose rows a filter hid
474
+ entirely loading. The names are MUI X DataGrid's (`onRowsScrollEnd`, `scrollEndThreshold`, default
475
+ `80`), and so is the unit: pixels, which hold when rows differ in height.
476
+
477
+ A function rather than part of a hook: it takes any infinite query's `hasNextPage`,
478
+ `isFetchingNextPage` and `fetchNextPage`, so it pages any list hook or a `useInfiniteQuery` of the
479
+ app's own. Grid events arrive in bursts, so it asks with `cancelRefetch: false` and a repeat
480
+ while the first request is in flight does nothing.
481
+
196
482
  ## Testing an app against it
197
483
 
198
484
  ```ts
@@ -1,9 +1,39 @@
1
- import { type ComponentType, type PropsWithChildren } from "react";
1
+ import { type ComponentType, type PropsWithChildren, type ReactNode } from "react";
2
2
  import { type QueryClient } from "@tanstack/react-query";
3
3
  import type { AnyRecord } from "./types/anyRecord.d.ts";
4
4
  import type { Theme } from "@mui/material/styles";
5
5
  /** A provider is any component that renders its children. */
6
6
  export type ProviderComponent = ComponentType<PropsWithChildren>;
7
+ /** A provider with the props it is mounted with, as `withProps` describes it. */
8
+ export type ConfiguredProvider = {
9
+ readonly component: ComponentType<PropsWithChildren<AnyRecord>>;
10
+ readonly props: AnyRecord;
11
+ };
12
+ /** What `providers` takes: a provider, or one with its props. */
13
+ export type ProviderEntry = ProviderComponent | ConfiguredProvider;
14
+ /**
15
+ * withProps
16
+ *
17
+ * A provider for `EveryskApp`'s `providers`, mounted with these props. `providers` mounts a bare
18
+ * component as `<Provider>{children}</Provider>`, so a provider configured by a prop — AG Grid's
19
+ * license key — needs its props said here:
20
+ *
21
+ * ```tsx
22
+ * <EveryskApp
23
+ * providers={[
24
+ * withProps(AgGridLicenseProvider, { licenseKey: import.meta.env.VITE_AG_GRID_LICENSE_KEY }),
25
+ * WebSocketProvider,
26
+ * ]}
27
+ * >
28
+ * ```
29
+ *
30
+ * It describes the provider rather than returning a wrapper component: a wrapper made in `App`'s
31
+ * render is a new component type each time, and React would remount the whole app under it on
32
+ * every render. The props are checked against the component's own.
33
+ */
34
+ export declare const withProps: <P extends {
35
+ children?: ReactNode;
36
+ }>(component: ComponentType<P>, props: NoInfer<Omit<P, "children">>) => ConfiguredProvider;
7
37
  export type EveryskAppProps = PropsWithChildren<{
8
38
  /**
9
39
  * The client every `useQuery` in the tree talks to. Defaults to a fresh
@@ -28,8 +58,10 @@ export type EveryskAppProps = PropsWithChildren<{
28
58
  * `modules.json` kept an explicit `providerOrder` only because the app owned the tree and
29
59
  * each module had to be hand-wired into the right slot; owning the tree here is what
30
60
  * retires that contract.
61
+ *
62
+ * A provider configured by props goes in through `withProps`.
31
63
  */
32
- providers?: ProviderComponent[];
64
+ providers?: ProviderEntry[];
33
65
  }>;
34
66
  /**
35
67
  * EveryskApp
@@ -1 +1 @@
1
- {"version":3,"file":"EveryskApp.d.ts","sourceRoot":"","sources":["../src/EveryskApp.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAkB,MAAM,OAAO,CAAC;AAE5F,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAM9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAElD,6DAA6D;AAC7D,MAAM,MAAM,iBAAiB,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAAC;AAEjE,MAAM,MAAM,eAAe,GAAG,iBAAiB,CAAC;IAC5C;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC9B;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACnC,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,UAAU,GAAI,wEAOxB,eAAe,gCAoBjB,CAAC"}
1
+ {"version":3,"file":"EveryskApp.d.ts","sourceRoot":"","sources":["../src/EveryskApp.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5F,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAM9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAElD,6DAA6D;AAC7D,MAAM,MAAM,iBAAiB,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAAC;AAEjE,iFAAiF;AACjF,MAAM,MAAM,kBAAkB,GAAG;IAC7B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;IAChE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;CAC7B,CAAC;AAEF,iEAAiE;AACjE,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,EACxD,WAAW,aAAa,CAAC,CAAC,CAAC,EAC3B,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,KACpC,kBAGD,CAAC;AAcH,MAAM,MAAM,eAAe,GAAG,iBAAiB,CAAC;IAC5C;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;CAC/B,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,UAAU,GAAI,wEAOxB,eAAe,gCAoBjB,CAAC"}
@@ -7,6 +7,39 @@ import { QueryClientProvider } from "@tanstack/react-query";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  //#region src/EveryskApp.tsx
9
9
  /**
10
+ * withProps
11
+ *
12
+ * A provider for `EveryskApp`'s `providers`, mounted with these props. `providers` mounts a bare
13
+ * component as `<Provider>{children}</Provider>`, so a provider configured by a prop — AG Grid's
14
+ * license key — needs its props said here:
15
+ *
16
+ * ```tsx
17
+ * <EveryskApp
18
+ * providers={[
19
+ * withProps(AgGridLicenseProvider, { licenseKey: import.meta.env.VITE_AG_GRID_LICENSE_KEY }),
20
+ * WebSocketProvider,
21
+ * ]}
22
+ * >
23
+ * ```
24
+ *
25
+ * It describes the provider rather than returning a wrapper component: a wrapper made in `App`'s
26
+ * render is a new component type each time, and React would remount the whole app under it on
27
+ * every render. The props are checked against the component's own.
28
+ */
29
+ var withProps = (component, props) => ({
30
+ component,
31
+ props
32
+ });
33
+ var isConfigured = (entry) => typeof entry === "object" && entry !== null && "component" in entry && "props" in entry;
34
+ var mount = (entry, children) => {
35
+ if (!isConfigured(entry)) return /* @__PURE__ */ jsx(entry, { children });
36
+ const { component: Provider, props } = entry;
37
+ return /* @__PURE__ */ jsx(Provider, {
38
+ ...props,
39
+ children
40
+ });
41
+ };
42
+ /**
10
43
  * EveryskApp
11
44
  *
12
45
  * The provider tree every Everysk app mounts, composed in one fixed order:
@@ -47,7 +80,7 @@ import { jsx } from "react/jsx-runtime";
47
80
  var EveryskApp = ({ children, queryClient, theme, appId, appEnvironmentVar, providers }) => {
48
81
  const fallbackQueryClient = useMemo(() => createQueryClient(), []);
49
82
  const client = queryClient ?? fallbackQueryClient;
50
- const wrapped = (providers ?? []).reduceRight((inner, Provider) => /* @__PURE__ */ jsx(Provider, { children: inner }), children);
83
+ const wrapped = (providers ?? []).reduceRight((inner, entry) => mount(entry, inner), children);
51
84
  return /* @__PURE__ */ jsx(ThemeProvider, {
52
85
  theme,
53
86
  children: /* @__PURE__ */ jsx(AppConfigProvider, {
@@ -61,6 +94,6 @@ var EveryskApp = ({ children, queryClient, theme, appId, appEnvironmentVar, prov
61
94
  });
62
95
  };
63
96
  //#endregion
64
- export { EveryskApp };
97
+ export { EveryskApp, withProps };
65
98
 
66
99
  //# sourceMappingURL=EveryskApp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EveryskApp.js","names":[],"sources":["../src/EveryskApp.tsx"],"sourcesContent":["import { useMemo, type ComponentType, type PropsWithChildren, type ReactNode } from \"react\";\n\nimport { QueryClientProvider, type QueryClient } from \"@tanstack/react-query\";\n\nimport { ThemeProvider } from \"@/theme/ThemeProvider\";\nimport { AppConfigProvider } from \"@/contexts/appConfigContext/provider\";\nimport { AppAlertProvider } from \"@/contexts/appAlertContext/provider\";\nimport { createQueryClient } from \"@/lib/queryClient\";\nimport type { AnyRecord } from \"@/types/anyRecord\";\nimport type { Theme } from \"@mui/material/styles\";\n\n/** A provider is any component that renders its children. */\nexport type ProviderComponent = ComponentType<PropsWithChildren>;\n\nexport type EveryskAppProps = PropsWithChildren<{\n /**\n * The client every `useQuery` in the tree talks to. Defaults to a fresh\n * `createQueryClient()` created once per mount. Pass one to seed the cache, to share it\n * with code outside the tree, or to assert on it from a test.\n */\n queryClient?: QueryClient;\n /** Overrides `window.APP_ID`. */\n appId?: string | null;\n /**\n * The MUI theme. Omitted, the app gets the Everysk dark theme — see `ThemeProvider`.\n */\n theme?: Theme;\n /** Overrides `window.APP_CONFIG` wholesale. */\n appEnvironmentVar?: AnyRecord;\n /**\n * Optional providers, applied innermost — inside every core provider and directly around\n * `children`, in array order, so the first entry is outermost of the group.\n *\n * They go innermost rather than into named slots because that is the only position from\n * which a provider can read every core context (theme, app config, query client, alerts).\n * `modules.json` kept an explicit `providerOrder` only because the app owned the tree and\n * each module had to be hand-wired into the right slot; owning the tree here is what\n * retires that contract.\n */\n providers?: ProviderComponent[];\n}>;\n\n/**\n * EveryskApp\n *\n * The provider tree every Everysk app mounts, composed in one fixed order:\n *\n * ```\n * ThemeProvider\n * AppConfigProvider\n * QueryClientProvider\n * AppAlertProvider\n * …providers\n * children\n * ```\n *\n * The order is not arbitrary. `AppAlertProvider` renders MUI's `Snackbar`, so it must sit\n * inside `ThemeProvider`; and `QueryClientProvider` sits inside `AppConfigProvider` because\n * entity hooks build their Axios instance from `useAppConfig()`.\n *\n * `BroadcastChannelProvider` used to sit between theme and app configuration. It is gone: the\n * optional socket layer supersedes it for cross-tab and server-push messaging (as\n * `modules.json` already stated), and the one production path that still used it — app\n * configuration arriving over the channel — was replaced by `window.APP_CONFIG`.\n *\n * `src/test/renderWithProviders` renders through this same component, so a test and the real\n * app can never see different trees.\n *\n * @example\n * ```tsx\n * // src/App.tsx — the entire file\n * export function App() {\n * return (\n * <EveryskApp>\n * <Routes />\n * </EveryskApp>\n * );\n * }\n * ```\n */\nexport const EveryskApp = ({\n children,\n queryClient,\n theme,\n appId,\n appEnvironmentVar,\n providers,\n}: EveryskAppProps) => {\n // Only used when the caller passes no client. Created once per mount so a re-render\n // never swaps the cache out from under the tree.\n const fallbackQueryClient = useMemo(() => createQueryClient(), []);\n const client = queryClient ?? fallbackQueryClient;\n\n const wrapped = (providers ?? []).reduceRight<ReactNode>(\n (inner, Provider) => <Provider>{inner}</Provider>,\n children,\n );\n\n return (\n <ThemeProvider theme={theme}>\n <AppConfigProvider appId={appId} appEnvironmentVar={appEnvironmentVar}>\n <QueryClientProvider client={client}>\n <AppAlertProvider>{wrapped}</AppAlertProvider>\n </QueryClientProvider>\n </AppConfigProvider>\n </ThemeProvider>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgFA,IAAa,cAAc,EACvB,UACA,aACA,OACA,OACA,mBACA,gBACmB;CAGnB,MAAM,sBAAsB,cAAc,kBAAkB,GAAG,CAAC,CAAC;CACjE,MAAM,SAAS,eAAe;CAE9B,MAAM,WAAW,aAAa,CAAC,EAAA,CAAG,aAC7B,OAAO,aAAa,oBAAC,UAAD,EAAA,UAAW,MAAgB,CAAA,GAChD,QACJ;CAEA,OACI,oBAAC,eAAD;EAAsB;EAClB,UAAA,oBAAC,mBAAD;GAA0B;GAA0B;GAChD,UAAA,oBAAC,qBAAD;IAA6B;IACzB,UAAA,oBAAC,kBAAD,EAAA,UAAmB,QAA0B,CAAA;GAC5B,CAAA;EACN,CAAA;CACR,CAAA;AAEvB"}
1
+ {"version":3,"file":"EveryskApp.js","names":[],"sources":["../src/EveryskApp.tsx"],"sourcesContent":["import { useMemo, type ComponentType, type PropsWithChildren, type ReactNode } from \"react\";\n\nimport { QueryClientProvider, type QueryClient } from \"@tanstack/react-query\";\n\nimport { ThemeProvider } from \"@/theme/ThemeProvider\";\nimport { AppConfigProvider } from \"@/contexts/appConfigContext/provider\";\nimport { AppAlertProvider } from \"@/contexts/appAlertContext/provider\";\nimport { createQueryClient } from \"@/lib/queryClient\";\nimport type { AnyRecord } from \"@/types/anyRecord\";\nimport type { Theme } from \"@mui/material/styles\";\n\n/** A provider is any component that renders its children. */\nexport type ProviderComponent = ComponentType<PropsWithChildren>;\n\n/** A provider with the props it is mounted with, as `withProps` describes it. */\nexport type ConfiguredProvider = {\n readonly component: ComponentType<PropsWithChildren<AnyRecord>>;\n readonly props: AnyRecord;\n};\n\n/** What `providers` takes: a provider, or one with its props. */\nexport type ProviderEntry = ProviderComponent | ConfiguredProvider;\n\n/**\n * withProps\n *\n * A provider for `EveryskApp`'s `providers`, mounted with these props. `providers` mounts a bare\n * component as `<Provider>{children}</Provider>`, so a provider configured by a prop — AG Grid's\n * license key — needs its props said here:\n *\n * ```tsx\n * <EveryskApp\n * providers={[\n * withProps(AgGridLicenseProvider, { licenseKey: import.meta.env.VITE_AG_GRID_LICENSE_KEY }),\n * WebSocketProvider,\n * ]}\n * >\n * ```\n *\n * It describes the provider rather than returning a wrapper component: a wrapper made in `App`'s\n * render is a new component type each time, and React would remount the whole app under it on\n * every render. The props are checked against the component's own.\n */\nexport const withProps = <P extends { children?: ReactNode }>(\n component: ComponentType<P>,\n props: NoInfer<Omit<P, \"children\">>,\n): ConfiguredProvider => ({\n component: component as ComponentType<PropsWithChildren<AnyRecord>>,\n props: props as AnyRecord,\n});\n\nconst isConfigured = (entry: ProviderEntry): entry is ConfiguredProvider =>\n typeof entry === \"object\" && entry !== null && \"component\" in entry && \"props\" in entry;\n\nconst mount = (entry: ProviderEntry, children: ReactNode): ReactNode => {\n if (!isConfigured(entry)) {\n const Provider = entry;\n return <Provider>{children}</Provider>;\n }\n const { component: Provider, props } = entry;\n return <Provider {...props}>{children}</Provider>;\n};\n\nexport type EveryskAppProps = PropsWithChildren<{\n /**\n * The client every `useQuery` in the tree talks to. Defaults to a fresh\n * `createQueryClient()` created once per mount. Pass one to seed the cache, to share it\n * with code outside the tree, or to assert on it from a test.\n */\n queryClient?: QueryClient;\n /** Overrides `window.APP_ID`. */\n appId?: string | null;\n /**\n * The MUI theme. Omitted, the app gets the Everysk dark theme — see `ThemeProvider`.\n */\n theme?: Theme;\n /** Overrides `window.APP_CONFIG` wholesale. */\n appEnvironmentVar?: AnyRecord;\n /**\n * Optional providers, applied innermost — inside every core provider and directly around\n * `children`, in array order, so the first entry is outermost of the group.\n *\n * They go innermost rather than into named slots because that is the only position from\n * which a provider can read every core context (theme, app config, query client, alerts).\n * `modules.json` kept an explicit `providerOrder` only because the app owned the tree and\n * each module had to be hand-wired into the right slot; owning the tree here is what\n * retires that contract.\n *\n * A provider configured by props goes in through `withProps`.\n */\n providers?: ProviderEntry[];\n}>;\n\n/**\n * EveryskApp\n *\n * The provider tree every Everysk app mounts, composed in one fixed order:\n *\n * ```\n * ThemeProvider\n * AppConfigProvider\n * QueryClientProvider\n * AppAlertProvider\n * …providers\n * children\n * ```\n *\n * The order is not arbitrary. `AppAlertProvider` renders MUI's `Snackbar`, so it must sit\n * inside `ThemeProvider`; and `QueryClientProvider` sits inside `AppConfigProvider` because\n * entity hooks build their Axios instance from `useAppConfig()`.\n *\n * `BroadcastChannelProvider` used to sit between theme and app configuration. It is gone: the\n * optional socket layer supersedes it for cross-tab and server-push messaging (as\n * `modules.json` already stated), and the one production path that still used it — app\n * configuration arriving over the channel — was replaced by `window.APP_CONFIG`.\n *\n * `src/test/renderWithProviders` renders through this same component, so a test and the real\n * app can never see different trees.\n *\n * @example\n * ```tsx\n * // src/App.tsx — the entire file\n * export function App() {\n * return (\n * <EveryskApp>\n * <Routes />\n * </EveryskApp>\n * );\n * }\n * ```\n */\nexport const EveryskApp = ({\n children,\n queryClient,\n theme,\n appId,\n appEnvironmentVar,\n providers,\n}: EveryskAppProps) => {\n // Only used when the caller passes no client. Created once per mount so a re-render\n // never swaps the cache out from under the tree.\n const fallbackQueryClient = useMemo(() => createQueryClient(), []);\n const client = queryClient ?? fallbackQueryClient;\n\n const wrapped = (providers ?? []).reduceRight<ReactNode>(\n (inner, entry) => mount(entry, inner),\n children,\n );\n\n return (\n <ThemeProvider theme={theme}>\n <AppConfigProvider appId={appId} appEnvironmentVar={appEnvironmentVar}>\n <QueryClientProvider client={client}>\n <AppAlertProvider>{wrapped}</AppAlertProvider>\n </QueryClientProvider>\n </AppConfigProvider>\n </ThemeProvider>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,IAAa,aACT,WACA,WACsB;CACX;CACJ;AACX;AAEA,IAAM,gBAAgB,UAClB,OAAO,UAAU,YAAY,UAAU,QAAQ,eAAe,SAAS,WAAW;AAEtF,IAAM,SAAS,OAAsB,aAAmC;CACpE,IAAI,CAAC,aAAa,KAAK,GAEnB,OAAO,oBAAC,OAAD,EAAW,SAAmB,CAAA;CAEzC,MAAM,EAAE,WAAW,UAAU,UAAU;CACvC,OAAO,oBAAC,UAAD;EAAU,GAAI;EAAQ;CAAmB,CAAA;AACpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,IAAa,cAAc,EACvB,UACA,aACA,OACA,OACA,mBACA,gBACmB;CAGnB,MAAM,sBAAsB,cAAc,kBAAkB,GAAG,CAAC,CAAC;CACjE,MAAM,SAAS,eAAe;CAE9B,MAAM,WAAW,aAAa,CAAC,EAAA,CAAG,aAC7B,OAAO,UAAU,MAAM,OAAO,KAAK,GACpC,QACJ;CAEA,OACI,oBAAC,eAAD;EAAsB;EAClB,UAAA,oBAAC,mBAAD;GAA0B;GAA0B;GAChD,UAAA,oBAAC,qBAAD;IAA6B;IACzB,UAAA,oBAAC,kBAAD,EAAA,UAAmB,QAA0B,CAAA;GAC5B,CAAA;EACN,CAAA;CACR,CAAA;AAEvB"}
@@ -0,0 +1,53 @@
1
+ import type { GridApi } from "ag-grid-community";
2
+ /**
3
+ * The part of an infinite query this needs. Structural, so it takes any list hook's `query`,
4
+ * or a `useInfiniteQuery` an app writes itself.
5
+ */
6
+ export type InfiniteQueryPaging = {
7
+ hasNextPage: boolean;
8
+ isFetchingNextPage: boolean;
9
+ fetchNextPage: (options?: {
10
+ cancelRefetch?: boolean;
11
+ }) => Promise<unknown>;
12
+ };
13
+ export type HandleRowsScrollEndOptions = {
14
+ /**
15
+ * Distance in pixels from the end of the rows at which the next page is requested. Named and
16
+ * measured as MUI X DataGrid's `scrollEndThreshold`, whose default it shares. Pixels rather
17
+ * than rows, so it holds with rows of different heights.
18
+ */
19
+ scrollEndThreshold?: number;
20
+ };
21
+ /**
22
+ * handleRowsScrollEnd
23
+ *
24
+ * Requests the next page when an AG Grid is scrolled near its last row, or shows none — the
25
+ * grid paging on demand. The handler for what MUI X DataGrid calls `onRowsScrollEnd`; wire it
26
+ * to AG Grid's `onBodyScrollEnd` and `onModelUpdated`, the second so that a grid whose rows a
27
+ * client-side filter hid entirely keeps loading.
28
+ *
29
+ * A function, not part of any hook: the data hook does not know a grid exists, and the grid
30
+ * does not know where its rows come from. It lives in this subpath because it needs `GridApi`,
31
+ * and AG Grid is an optional peer.
32
+ *
33
+ * Events fire in bursts, before `isFetchingNextPage` has flipped, so the request is made with
34
+ * `cancelRefetch: false`: TanStack then ignores a repeat while the first one is in flight,
35
+ * instead of cancelling it and asking for the same page again.
36
+ *
37
+ * @param {GridApi} gridApi - The grid, from the event (`event.api`).
38
+ * @param {InfiniteQueryPaging} query - The infinite query feeding the grid.
39
+ * @param {HandleRowsScrollEndOptions} [options] - `scrollEndThreshold`, in pixels (default 80).
40
+ *
41
+ * Example:
42
+ * ```tsx
43
+ * const { query } = useFetchDatastores({ filters, select: toRows });
44
+ *
45
+ * <AgGridReact
46
+ * rowData={query.data}
47
+ * onBodyScrollEnd={(event) => handleRowsScrollEnd(event.api, query)}
48
+ * onModelUpdated={(event) => handleRowsScrollEnd(event.api, query)}
49
+ * />
50
+ * ```
51
+ */
52
+ export declare const handleRowsScrollEnd: (gridApi: GridApi, query: InfiniteQueryPaging, { scrollEndThreshold }?: HandleRowsScrollEndOptions) => void;
53
+ //# sourceMappingURL=handleRowsScrollEnd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handleRowsScrollEnd.d.ts","sourceRoot":"","sources":["../../src/agGrid/handleRowsScrollEnd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9E,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACrC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,mBAAmB,GAC5B,SAAS,OAAO,EAChB,OAAO,mBAAmB,EAC1B,yBAA6B,0BAA+B,KAC7D,IAcF,CAAC"}
@@ -0,0 +1,47 @@
1
+ //#region src/agGrid/handleRowsScrollEnd.ts
2
+ /**
3
+ * handleRowsScrollEnd
4
+ *
5
+ * Requests the next page when an AG Grid is scrolled near its last row, or shows none — the
6
+ * grid paging on demand. The handler for what MUI X DataGrid calls `onRowsScrollEnd`; wire it
7
+ * to AG Grid's `onBodyScrollEnd` and `onModelUpdated`, the second so that a grid whose rows a
8
+ * client-side filter hid entirely keeps loading.
9
+ *
10
+ * A function, not part of any hook: the data hook does not know a grid exists, and the grid
11
+ * does not know where its rows come from. It lives in this subpath because it needs `GridApi`,
12
+ * and AG Grid is an optional peer.
13
+ *
14
+ * Events fire in bursts, before `isFetchingNextPage` has flipped, so the request is made with
15
+ * `cancelRefetch: false`: TanStack then ignores a repeat while the first one is in flight,
16
+ * instead of cancelling it and asking for the same page again.
17
+ *
18
+ * @param {GridApi} gridApi - The grid, from the event (`event.api`).
19
+ * @param {InfiniteQueryPaging} query - The infinite query feeding the grid.
20
+ * @param {HandleRowsScrollEndOptions} [options] - `scrollEndThreshold`, in pixels (default 80).
21
+ *
22
+ * Example:
23
+ * ```tsx
24
+ * const { query } = useFetchDatastores({ filters, select: toRows });
25
+ *
26
+ * <AgGridReact
27
+ * rowData={query.data}
28
+ * onBodyScrollEnd={(event) => handleRowsScrollEnd(event.api, query)}
29
+ * onModelUpdated={(event) => handleRowsScrollEnd(event.api, query)}
30
+ * />
31
+ * ```
32
+ */
33
+ var handleRowsScrollEnd = (gridApi, query, { scrollEndThreshold = 80 } = {}) => {
34
+ if (!query.hasNextPage || query.isFetchingNextPage) return;
35
+ const rowCount = gridApi.getDisplayedRowCount();
36
+ const lastRow = rowCount > 0 ? gridApi.getDisplayedRowAtIndex(rowCount - 1) : void 0;
37
+ if (lastRow) {
38
+ const rowsEnd = (lastRow.rowTop ?? 0) + (lastRow.rowHeight ?? 0);
39
+ const { bottom } = gridApi.getVerticalPixelRange();
40
+ if (rowsEnd - bottom > scrollEndThreshold) return;
41
+ }
42
+ query.fetchNextPage({ cancelRefetch: false });
43
+ };
44
+ //#endregion
45
+ export { handleRowsScrollEnd };
46
+
47
+ //# sourceMappingURL=handleRowsScrollEnd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handleRowsScrollEnd.js","names":[],"sources":["../../src/agGrid/handleRowsScrollEnd.ts"],"sourcesContent":["import type { GridApi } from \"ag-grid-community\";\n\n/**\n * The part of an infinite query this needs. Structural, so it takes any list hook's `query`,\n * or a `useInfiniteQuery` an app writes itself.\n */\nexport type InfiniteQueryPaging = {\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n fetchNextPage: (options?: { cancelRefetch?: boolean }) => Promise<unknown>;\n};\n\nexport type HandleRowsScrollEndOptions = {\n /**\n * Distance in pixels from the end of the rows at which the next page is requested. Named and\n * measured as MUI X DataGrid's `scrollEndThreshold`, whose default it shares. Pixels rather\n * than rows, so it holds with rows of different heights.\n */\n scrollEndThreshold?: number;\n};\n\n/**\n * handleRowsScrollEnd\n *\n * Requests the next page when an AG Grid is scrolled near its last row, or shows none — the\n * grid paging on demand. The handler for what MUI X DataGrid calls `onRowsScrollEnd`; wire it\n * to AG Grid's `onBodyScrollEnd` and `onModelUpdated`, the second so that a grid whose rows a\n * client-side filter hid entirely keeps loading.\n *\n * A function, not part of any hook: the data hook does not know a grid exists, and the grid\n * does not know where its rows come from. It lives in this subpath because it needs `GridApi`,\n * and AG Grid is an optional peer.\n *\n * Events fire in bursts, before `isFetchingNextPage` has flipped, so the request is made with\n * `cancelRefetch: false`: TanStack then ignores a repeat while the first one is in flight,\n * instead of cancelling it and asking for the same page again.\n *\n * @param {GridApi} gridApi - The grid, from the event (`event.api`).\n * @param {InfiniteQueryPaging} query - The infinite query feeding the grid.\n * @param {HandleRowsScrollEndOptions} [options] - `scrollEndThreshold`, in pixels (default 80).\n *\n * Example:\n * ```tsx\n * const { query } = useFetchDatastores({ filters, select: toRows });\n *\n * <AgGridReact\n * rowData={query.data}\n * onBodyScrollEnd={(event) => handleRowsScrollEnd(event.api, query)}\n * onModelUpdated={(event) => handleRowsScrollEnd(event.api, query)}\n * />\n * ```\n */\nexport const handleRowsScrollEnd = (\n gridApi: GridApi,\n query: InfiniteQueryPaging,\n { scrollEndThreshold = 80 }: HandleRowsScrollEndOptions = {},\n): void => {\n if (!query.hasNextPage || query.isFetchingNextPage) return;\n\n const rowCount = gridApi.getDisplayedRowCount();\n const lastRow = rowCount > 0 ? gridApi.getDisplayedRowAtIndex(rowCount - 1) : undefined;\n\n if (lastRow) {\n const rowsEnd = (lastRow.rowTop ?? 0) + (lastRow.rowHeight ?? 0);\n const { bottom } = gridApi.getVerticalPixelRange();\n\n if (rowsEnd - bottom > scrollEndThreshold) return;\n }\n\n void query.fetchNextPage({ cancelRefetch: false });\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,IAAa,uBACT,SACA,OACA,EAAE,qBAAqB,OAAmC,CAAC,MACpD;CACP,IAAI,CAAC,MAAM,eAAe,MAAM,oBAAoB;CAEpD,MAAM,WAAW,QAAQ,qBAAqB;CAC9C,MAAM,UAAU,WAAW,IAAI,QAAQ,uBAAuB,WAAW,CAAC,IAAI,KAAA;CAE9E,IAAI,SAAS;EACT,MAAM,WAAW,QAAQ,UAAU,MAAM,QAAQ,aAAa;EAC9D,MAAM,EAAE,WAAW,QAAQ,sBAAsB;EAEjD,IAAI,UAAU,SAAS,oBAAoB;CAC/C;CAEA,MAAW,cAAc,EAAE,eAAe,MAAM,CAAC;AACrD"}
@@ -1,4 +1,6 @@
1
1
  export { setAgGridLicense, AgGridLicenseProvider } from "../contexts/agGridLicenseContext/provider.js";
2
2
  export type { AgGridLicenseProviderProps } from "../contexts/agGridLicenseContext/provider.js";
3
3
  export { getAgGridTheme, useAgGridTheme, agGridThemeParams } from "./agGridTheme";
4
+ export { handleRowsScrollEnd } from "./handleRowsScrollEnd";
5
+ export type { HandleRowsScrollEndOptions, InfiniteQueryPaging } from "./handleRowsScrollEnd";
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agGrid/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACnG,YAAY,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AAE3F,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agGrid/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACnG,YAAY,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AAE3F,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElF,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC"}