@tulip-systems/core 0.5.1 → 0.5.3

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 (381) hide show
  1. package/dist/components/common/icons.d.mts +52 -52
  2. package/dist/components/common/icons.d.mts.map +1 -1
  3. package/dist/components/common/status.d.mts +3 -3
  4. package/dist/components/common/status.d.mts.map +1 -1
  5. package/dist/components/editor/components/content.client.d.mts +2 -2
  6. package/dist/components/editor/components/editor.client.d.mts +4 -4
  7. package/dist/components/editor/components/editor.client.mjs +2 -2
  8. package/dist/components/editor/components/editor.client.mjs.map +1 -1
  9. package/dist/components/editor/components/menu.client.d.mts +3 -3
  10. package/dist/components/editor/components/menu.client.mjs +3 -0
  11. package/dist/components/editor/components/menu.client.mjs.map +1 -1
  12. package/dist/components/editor/extensions/file-handler/extension.d.mts +21 -0
  13. package/dist/components/editor/extensions/file-handler/extension.d.mts.map +1 -0
  14. package/dist/components/editor/extensions/file-handler/extension.mjs +40 -0
  15. package/dist/components/editor/extensions/file-handler/extension.mjs.map +1 -0
  16. package/dist/components/editor/extensions/file-handler/strategy.d.mts +29 -0
  17. package/dist/components/editor/extensions/file-handler/strategy.d.mts.map +1 -0
  18. package/dist/components/editor/extensions/file-handler/strategy.mjs +111 -0
  19. package/dist/components/editor/extensions/file-handler/strategy.mjs.map +1 -0
  20. package/dist/components/editor/extensions/file-handler/utils.mjs +50 -0
  21. package/dist/components/editor/extensions/file-handler/utils.mjs.map +1 -0
  22. package/dist/components/editor/extensions/image/extension.d.mts +8 -0
  23. package/dist/components/editor/extensions/image/extension.d.mts.map +1 -0
  24. package/dist/components/editor/extensions/image/extension.mjs +52 -0
  25. package/dist/components/editor/extensions/image/extension.mjs.map +1 -0
  26. package/dist/components/editor/extensions/skeleton/extension.mjs +41 -0
  27. package/dist/components/editor/extensions/skeleton/extension.mjs.map +1 -0
  28. package/dist/components/editor/extensions/skeleton/renderer.mjs +25 -0
  29. package/dist/components/editor/extensions/skeleton/renderer.mjs.map +1 -0
  30. package/dist/components/editor/lib/constants.d.mts +29 -26
  31. package/dist/components/editor/lib/constants.d.mts.map +1 -1
  32. package/dist/components/editor/lib/constants.mjs +14 -2
  33. package/dist/components/editor/lib/constants.mjs.map +1 -1
  34. package/dist/components/editor/lib/extensions.d.mts +5 -1
  35. package/dist/components/editor/lib/extensions.d.mts.map +1 -1
  36. package/dist/components/editor/lib/extensions.mjs.map +1 -1
  37. package/dist/components/editor/lib/variants.mjs +10 -2
  38. package/dist/components/editor/lib/variants.mjs.map +1 -1
  39. package/dist/components/header/back-button.client.d.mts +2 -2
  40. package/dist/components/header/bottom-bar.client.d.mts +3 -3
  41. package/dist/components/header/breadcrumbs.client.d.mts +7 -7
  42. package/dist/components/header/header.client.d.mts +2 -2
  43. package/dist/components/header/mobile-nav-switcher.client.d.mts +2 -2
  44. package/dist/components/header/top-bar.client.d.mts +4 -4
  45. package/dist/components/layouts/admin-content.client.d.mts +2 -2
  46. package/dist/components/layouts/admin-layout.d.mts +2 -2
  47. package/dist/components/layouts/admin-loading.d.mts +2 -2
  48. package/dist/components/layouts/admin-loading.d.mts.map +1 -1
  49. package/dist/components/layouts/empty-page.d.mts +4 -4
  50. package/dist/components/layouts/empty-page.d.mts.map +1 -1
  51. package/dist/components/layouts/list-layout.d.mts +2 -2
  52. package/dist/components/layouts/list-layout.d.mts.map +1 -1
  53. package/dist/components/layouts/not-found-page.d.mts +3 -3
  54. package/dist/components/layouts/not-found-page.d.mts.map +1 -1
  55. package/dist/components/layouts/providers.client.d.mts +2 -2
  56. package/dist/components/layouts/root-layout.server.d.mts +2 -2
  57. package/dist/components/layouts/root-layout.server.d.mts.map +1 -1
  58. package/dist/components/layouts/root-loading.d.mts +2 -2
  59. package/dist/components/layouts/root-loading.d.mts.map +1 -1
  60. package/dist/components/layouts/tab-layout.d.mts +2 -2
  61. package/dist/components/layouts/tab-layout.d.mts.map +1 -1
  62. package/dist/components/lists/data-list.d.mts +5 -5
  63. package/dist/components/lists/data-list.d.mts.map +1 -1
  64. package/dist/components/lists/data-stack.d.mts +8 -8
  65. package/dist/components/navigation/admin-sidebar-paths.client.d.mts +10 -10
  66. package/dist/components/ui/accordion.d.mts +5 -5
  67. package/dist/components/ui/alert-dialog.d.mts +12 -12
  68. package/dist/components/ui/alert.d.mts +6 -6
  69. package/dist/components/ui/aspect-ratio.d.mts +2 -2
  70. package/dist/components/ui/avatar.client.d.mts +4 -4
  71. package/dist/components/ui/badge.d.mts +4 -4
  72. package/dist/components/ui/breadcrumb.d.mts +8 -8
  73. package/dist/components/ui/breadcrumb.d.mts.map +1 -1
  74. package/dist/components/ui/button.d.mts +5 -5
  75. package/dist/components/ui/button.d.mts.map +1 -1
  76. package/dist/components/ui/calendar.d.mts +3 -3
  77. package/dist/components/ui/calendar.d.mts.map +1 -1
  78. package/dist/components/ui/card.d.mts +7 -7
  79. package/dist/components/ui/card.d.mts.map +1 -1
  80. package/dist/components/ui/carousel.d.mts +6 -6
  81. package/dist/components/ui/carousel.d.mts.map +1 -1
  82. package/dist/components/ui/chart.client.d.mts +5 -5
  83. package/dist/components/ui/checkbox.d.mts +2 -2
  84. package/dist/components/ui/checkbox.d.mts.map +1 -1
  85. package/dist/components/ui/collapsible.client.d.mts +4 -4
  86. package/dist/components/ui/color-picker.client.d.mts +2 -2
  87. package/dist/components/ui/combobox-dropdown.client.d.mts +2 -2
  88. package/dist/components/ui/combobox.client.d.mts +2 -2
  89. package/dist/components/ui/command.d.mts +10 -10
  90. package/dist/components/ui/command.d.mts.map +1 -1
  91. package/dist/components/ui/context-menu.d.mts +16 -16
  92. package/dist/components/ui/date-picker.client.d.mts +2 -2
  93. package/dist/components/ui/dialog.client.d.mts +11 -11
  94. package/dist/components/ui/drawer.client.d.mts +11 -11
  95. package/dist/components/ui/dropdown-menu.d.mts +16 -16
  96. package/dist/components/ui/form.client.d.mts +7 -7
  97. package/dist/components/ui/hover-card.client.d.mts +4 -4
  98. package/dist/components/ui/input-recipient.d.mts +2 -2
  99. package/dist/components/ui/input.d.mts +2 -2
  100. package/dist/components/ui/label.d.mts +2 -2
  101. package/dist/components/ui/navigation-menu.d.mts +11 -11
  102. package/dist/components/ui/pagination.d.mts +8 -8
  103. package/dist/components/ui/popover.d.mts +5 -5
  104. package/dist/components/ui/popover.d.mts.map +1 -1
  105. package/dist/components/ui/progress.client.d.mts +2 -2
  106. package/dist/components/ui/radio-group.d.mts +3 -3
  107. package/dist/components/ui/resizable.client.d.mts +4 -4
  108. package/dist/components/ui/scroll-area.d.mts +3 -3
  109. package/dist/components/ui/select.client.d.mts +11 -11
  110. package/dist/components/ui/separator.d.mts +2 -2
  111. package/dist/components/ui/sheet.client.d.mts +9 -9
  112. package/dist/components/ui/sidebar.client.d.mts +24 -24
  113. package/dist/components/ui/skeleton.d.mts +2 -2
  114. package/dist/components/ui/slider.d.mts +2 -2
  115. package/dist/components/ui/sonner.client.d.mts +2 -2
  116. package/dist/components/ui/switch.d.mts +2 -2
  117. package/dist/components/ui/tabs.d.mts +5 -5
  118. package/dist/components/ui/textarea.d.mts +2 -2
  119. package/dist/components/ui/time-input.client.d.mts +2 -2
  120. package/dist/components/ui/toggle-group.client.d.mts +3 -3
  121. package/dist/components/ui/toggle.d.mts +2 -2
  122. package/dist/components/ui/tooltip.client.d.mts +5 -5
  123. package/dist/components.d.mts +2 -1
  124. package/dist/components.mjs +3 -2
  125. package/dist/data-tables.d.mts +2 -2
  126. package/dist/data-tables.mjs +2 -2
  127. package/dist/inline-edit/client.d.mts +2 -3
  128. package/dist/inline-edit/client.mjs +1 -2
  129. package/dist/lib/client.d.mts +2 -2
  130. package/dist/lib/client.mjs +2 -2
  131. package/dist/lib/hooks/use-action.d.mts +2 -2
  132. package/dist/lib/hooks/use-indicator.d.mts +11 -3
  133. package/dist/lib/hooks/use-indicator.d.mts.map +1 -1
  134. package/dist/lib/hooks/use-indicator.mjs +19 -8
  135. package/dist/lib/hooks/use-indicator.mjs.map +1 -1
  136. package/dist/modules/auth/components/allowed.client.d.mts +2 -2
  137. package/dist/modules/auth/components/allowed.client.d.mts.map +1 -1
  138. package/dist/modules/auth/components/auth-layout.server.d.mts +2 -2
  139. package/dist/modules/auth/components/auth-layout.server.d.mts.map +1 -1
  140. package/dist/modules/auth/components/auth-loading.d.mts +2 -2
  141. package/dist/modules/auth/components/create-first-user-page.client.d.mts +2 -2
  142. package/dist/modules/auth/components/create-first-user-page.client.d.mts.map +1 -1
  143. package/dist/modules/auth/components/forget-password-page.client.d.mts +2 -2
  144. package/dist/modules/auth/components/forget-password-page.client.d.mts.map +1 -1
  145. package/dist/modules/auth/components/guard-first-user.server.d.mts +2 -2
  146. package/dist/modules/auth/components/guard-first-user.server.d.mts.map +1 -1
  147. package/dist/modules/auth/components/guard.server.d.mts +2 -2
  148. package/dist/modules/auth/components/guard.server.d.mts.map +1 -1
  149. package/dist/modules/auth/components/login-page.client.d.mts +2 -2
  150. package/dist/modules/auth/components/login-page.client.d.mts.map +1 -1
  151. package/dist/modules/auth/components/reset-password-page.client.d.mts +2 -2
  152. package/dist/modules/auth/components/reset-password-page.client.d.mts.map +1 -1
  153. package/dist/modules/auth/components/update-password-command.d.mts +2 -2
  154. package/dist/modules/auth/components/update-password-command.d.mts.map +1 -1
  155. package/dist/modules/auth/db/schema.d.mts +73 -73
  156. package/dist/modules/auth/db/schema.d.mts.map +1 -1
  157. package/dist/modules/auth/handler/client.client.d.mts +8 -8
  158. package/dist/modules/auth/hooks/use-permission.d.mts +2 -2
  159. package/dist/modules/auth/hooks/use-session.d.mts +4 -4
  160. package/dist/modules/auth/lib/validators.d.mts +2 -2
  161. package/dist/modules/auth/lib/validators.d.mts.map +1 -1
  162. package/dist/modules/commands/components/alert-dialog-command.client.d.mts +10 -10
  163. package/dist/modules/commands/components/alert-dialog-command.client.d.mts.map +1 -1
  164. package/dist/modules/commands/components/click-command.client.d.mts +2 -2
  165. package/dist/modules/commands/components/click-command.client.d.mts.map +1 -1
  166. package/dist/modules/commands/components/command-trigger.client.d.mts +6 -6
  167. package/dist/modules/commands/components/command-trigger.client.d.mts.map +1 -1
  168. package/dist/modules/commands/components/dialog-command.client.d.mts +8 -8
  169. package/dist/modules/commands/components/dialog-command.client.d.mts.map +1 -1
  170. package/dist/modules/commands/components/dropdown-command.client.d.mts +5 -5
  171. package/dist/modules/commands/components/dropdown-command.client.d.mts.map +1 -1
  172. package/dist/modules/commands/components/empty-command.client.d.mts +2 -2
  173. package/dist/modules/commands/components/empty-command.client.d.mts.map +1 -1
  174. package/dist/modules/commands/components/form-dialog-command.client.d.mts +11 -11
  175. package/dist/modules/commands/components/form-dialog-command.client.d.mts.map +1 -1
  176. package/dist/modules/commands/hooks/use-command-mutation.client.d.mts +2 -2
  177. package/dist/modules/commands/menus/context-menu.client.d.mts +2 -2
  178. package/dist/modules/commands/menus/context-menu.client.d.mts.map +1 -1
  179. package/dist/modules/commands/menus/dropdown-menu.client.d.mts +3 -3
  180. package/dist/modules/commands/menus/dropdown-menu.client.d.mts.map +1 -1
  181. package/dist/modules/commands/menus/inline-menu.client.d.mts +3 -3
  182. package/dist/modules/commands/menus/inline-menu.client.d.mts.map +1 -1
  183. package/dist/modules/commands/menus/responsive-menu.client.d.mts +3 -3
  184. package/dist/modules/commands/menus/responsive-menu.client.d.mts.map +1 -1
  185. package/dist/modules/commands/utils/archive-command.client.d.mts +3 -3
  186. package/dist/modules/commands/utils/archive-command.client.d.mts.map +1 -1
  187. package/dist/modules/commands/utils/delete-command.client.d.mts +3 -3
  188. package/dist/modules/commands/utils/delete-command.client.d.mts.map +1 -1
  189. package/dist/modules/config/db/helpers.d.mts +5 -5
  190. package/dist/modules/config/db/helpers.d.mts.map +1 -1
  191. package/dist/modules/data-tables/components/cell/common.client.d.mts +5 -5
  192. package/dist/modules/data-tables/components/cell/common.client.d.mts.map +1 -1
  193. package/dist/modules/data-tables/components/column-header.d.mts +2 -2
  194. package/dist/modules/data-tables/components/filters/combobox.client.d.mts +2 -2
  195. package/dist/modules/data-tables/components/filters/combobox.client.d.mts.map +1 -1
  196. package/dist/modules/data-tables/components/filters/slider.client.d.mts +2 -2
  197. package/dist/modules/data-tables/components/filters/slider.client.d.mts.map +1 -1
  198. package/dist/modules/data-tables/components/header.d.mts +4 -4
  199. package/dist/modules/data-tables/components/layout.d.mts +2 -2
  200. package/dist/modules/data-tables/components/search-input.client.d.mts +2 -2
  201. package/dist/modules/data-tables/components/search-input.client.d.mts.map +1 -1
  202. package/dist/modules/data-tables/components/skeleton.d.mts +2 -2
  203. package/dist/modules/data-tables/components/table.d.mts +7 -7
  204. package/dist/modules/data-tables/components/toolbar.d.mts +3 -3
  205. package/dist/modules/data-tables/hooks/use-context.client.d.mts +2 -2
  206. package/dist/modules/data-tables/hooks/use-context.client.d.mts.map +1 -1
  207. package/dist/modules/data-tables/lib/filters/parsers.d.mts +6 -1
  208. package/dist/modules/data-tables/lib/filters/parsers.d.mts.map +1 -1
  209. package/dist/modules/data-tables/lib/filters/parsers.mjs +6 -1
  210. package/dist/modules/data-tables/lib/filters/parsers.mjs.map +1 -1
  211. package/dist/modules/data-tables/tables/data-table/components/table.d.mts +2 -2
  212. package/dist/modules/data-tables/tables/data-table/components/table.d.mts.map +1 -1
  213. package/dist/modules/data-tables/tables/inline-table/components/cells/common.d.mts +2 -2
  214. package/dist/modules/data-tables/tables/inline-table/components/cells/common.d.mts.map +1 -1
  215. package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.d.mts +2 -2
  216. package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.d.mts.map +1 -1
  217. package/dist/modules/data-tables/tables/inline-table/components/inputs/advanced-select.client.d.mts +2 -2
  218. package/dist/modules/data-tables/tables/inline-table/components/inputs/advanced-select.client.d.mts.map +1 -1
  219. package/dist/modules/data-tables/tables/inline-table/components/inputs/combobox.client.d.mts +2 -2
  220. package/dist/modules/data-tables/tables/inline-table/components/inputs/combobox.client.d.mts.map +1 -1
  221. package/dist/modules/data-tables/tables/inline-table/components/inputs/input.client.d.mts +3 -3
  222. package/dist/modules/data-tables/tables/inline-table/components/inputs/input.client.d.mts.map +1 -1
  223. package/dist/modules/data-tables/tables/inline-table/components/inputs/read-only.d.mts +2 -2
  224. package/dist/modules/data-tables/tables/inline-table/components/inputs/read-only.d.mts.map +1 -1
  225. package/dist/modules/data-tables/tables/inline-table/components/inputs/select.client.d.mts +2 -2
  226. package/dist/modules/data-tables/tables/inline-table/components/inputs/select.client.d.mts.map +1 -1
  227. package/dist/modules/data-tables/tables/inline-table/components/table.d.mts +2 -2
  228. package/dist/modules/data-tables/tables/inline-table/components/table.d.mts.map +1 -1
  229. package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts +2 -2
  230. package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts.map +1 -1
  231. package/dist/modules/inline-edit/components/combobox-dropdown.client.d.mts +6 -4
  232. package/dist/modules/inline-edit/components/combobox-dropdown.client.d.mts.map +1 -1
  233. package/dist/modules/inline-edit/components/combobox-dropdown.client.mjs +11 -6
  234. package/dist/modules/inline-edit/components/combobox-dropdown.client.mjs.map +1 -1
  235. package/dist/modules/inline-edit/components/combobox.client.d.mts +6 -4
  236. package/dist/modules/inline-edit/components/combobox.client.d.mts.map +1 -1
  237. package/dist/modules/inline-edit/components/combobox.client.mjs +7 -5
  238. package/dist/modules/inline-edit/components/combobox.client.mjs.map +1 -1
  239. package/dist/modules/inline-edit/components/date-input.client.d.mts +5 -3
  240. package/dist/modules/inline-edit/components/date-input.client.d.mts.map +1 -1
  241. package/dist/modules/inline-edit/components/date-input.client.mjs +26 -13
  242. package/dist/modules/inline-edit/components/date-input.client.mjs.map +1 -1
  243. package/dist/modules/inline-edit/components/date-picker.client.d.mts +6 -4
  244. package/dist/modules/inline-edit/components/date-picker.client.d.mts.map +1 -1
  245. package/dist/modules/inline-edit/components/date-picker.client.mjs +12 -7
  246. package/dist/modules/inline-edit/components/date-picker.client.mjs.map +1 -1
  247. package/dist/modules/inline-edit/components/editor.client.d.mts +6 -4
  248. package/dist/modules/inline-edit/components/editor.client.d.mts.map +1 -1
  249. package/dist/modules/inline-edit/components/editor.client.mjs +11 -6
  250. package/dist/modules/inline-edit/components/editor.client.mjs.map +1 -1
  251. package/dist/modules/inline-edit/components/input-recipient.client.d.mts +6 -4
  252. package/dist/modules/inline-edit/components/input-recipient.client.d.mts.map +1 -1
  253. package/dist/modules/inline-edit/components/input-recipient.client.mjs +11 -6
  254. package/dist/modules/inline-edit/components/input-recipient.client.mjs.map +1 -1
  255. package/dist/modules/inline-edit/components/input-toggle.client.d.mts +6 -4
  256. package/dist/modules/inline-edit/components/input-toggle.client.d.mts.map +1 -1
  257. package/dist/modules/inline-edit/components/input-toggle.client.mjs +9 -7
  258. package/dist/modules/inline-edit/components/input-toggle.client.mjs.map +1 -1
  259. package/dist/modules/inline-edit/components/input.client.d.mts +13 -10
  260. package/dist/modules/inline-edit/components/input.client.d.mts.map +1 -1
  261. package/dist/modules/inline-edit/components/input.client.mjs +35 -19
  262. package/dist/modules/inline-edit/components/input.client.mjs.map +1 -1
  263. package/dist/modules/inline-edit/components/select.client.d.mts +10 -9
  264. package/dist/modules/inline-edit/components/select.client.d.mts.map +1 -1
  265. package/dist/modules/inline-edit/components/select.client.mjs +14 -7
  266. package/dist/modules/inline-edit/components/select.client.mjs.map +1 -1
  267. package/dist/modules/inline-edit/components/switch.client.d.mts +5 -3
  268. package/dist/modules/inline-edit/components/switch.client.d.mts.map +1 -1
  269. package/dist/modules/inline-edit/components/switch.client.mjs +8 -6
  270. package/dist/modules/inline-edit/components/switch.client.mjs.map +1 -1
  271. package/dist/modules/inline-edit/components/toggle.client.d.mts +6 -4
  272. package/dist/modules/inline-edit/components/toggle.client.d.mts.map +1 -1
  273. package/dist/modules/inline-edit/components/toggle.client.mjs +9 -7
  274. package/dist/modules/inline-edit/components/toggle.client.mjs.map +1 -1
  275. package/dist/modules/inline-edit/hooks/use-inline.client.d.mts +35 -3
  276. package/dist/modules/inline-edit/hooks/use-inline.client.d.mts.map +1 -1
  277. package/dist/modules/inline-edit/hooks/use-inline.client.mjs +71 -9
  278. package/dist/modules/inline-edit/hooks/use-inline.client.mjs.map +1 -1
  279. package/dist/modules/inline-edit/lib/variants.d.mts +1 -0
  280. package/dist/modules/inline-edit/lib/variants.d.mts.map +1 -1
  281. package/dist/modules/inline-edit/lib/variants.mjs +12 -4
  282. package/dist/modules/inline-edit/lib/variants.mjs.map +1 -1
  283. package/dist/modules/router/handler/init.server.d.mts +6 -6
  284. package/dist/modules/router/handler/init.server.d.mts.map +1 -1
  285. package/dist/modules/router/lib/query-client.server.d.mts +2 -2
  286. package/dist/modules/storage/components/dropzone-context.client.d.mts +2 -2
  287. package/dist/modules/storage/components/dropzone-context.client.d.mts.map +1 -1
  288. package/dist/modules/storage/components/dropzone.client.d.mts +5 -5
  289. package/dist/modules/storage/components/dropzone.client.d.mts.map +1 -1
  290. package/dist/modules/storage/components/dropzone.client.mjs +1 -1
  291. package/dist/modules/storage/components/image-grid.client.d.mts +3 -3
  292. package/dist/modules/storage/components/image-grid.client.d.mts.map +1 -1
  293. package/dist/modules/storage/components/image-grid.client.mjs +3 -3
  294. package/dist/modules/storage/components/image-grid.client.mjs.map +1 -1
  295. package/dist/modules/storage/components/upload-zone-context.client.d.mts +3 -11
  296. package/dist/modules/storage/components/upload-zone-context.client.d.mts.map +1 -1
  297. package/dist/modules/storage/components/upload-zone-context.client.mjs.map +1 -1
  298. package/dist/modules/storage/components/upload-zone.client.d.mts +10 -31
  299. package/dist/modules/storage/components/upload-zone.client.d.mts.map +1 -1
  300. package/dist/modules/storage/components/upload-zone.client.mjs +19 -91
  301. package/dist/modules/storage/components/upload-zone.client.mjs.map +1 -1
  302. package/dist/modules/storage/config/filters.d.mts +2 -0
  303. package/dist/modules/storage/config/filters.d.mts.map +1 -1
  304. package/dist/modules/storage/config/filters.mjs +3 -1
  305. package/dist/modules/storage/config/filters.mjs.map +1 -1
  306. package/dist/modules/storage/lib/create-upload.client.d.mts +56 -0
  307. package/dist/modules/storage/lib/create-upload.client.d.mts.map +1 -0
  308. package/dist/modules/storage/lib/create-upload.client.mjs +98 -0
  309. package/dist/modules/storage/lib/create-upload.client.mjs.map +1 -0
  310. package/dist/modules/storage/lib/helpers.d.mts +2 -2
  311. package/dist/modules/storage/lib/helpers.d.mts.map +1 -1
  312. package/dist/modules/storage/lib/helpers.mjs +1 -0
  313. package/dist/modules/storage/lib/helpers.mjs.map +1 -1
  314. package/dist/modules/storage/lib/router.server.d.mts +4181 -2283
  315. package/dist/modules/storage/lib/router.server.d.mts.map +1 -1
  316. package/dist/modules/storage/lib/router.server.mjs +5 -2
  317. package/dist/modules/storage/lib/router.server.mjs.map +1 -1
  318. package/dist/modules/storage/lib/schema.d.mts +156 -88
  319. package/dist/modules/storage/lib/schema.d.mts.map +1 -1
  320. package/dist/modules/storage/lib/schema.mjs +3 -1
  321. package/dist/modules/storage/lib/schema.mjs.map +1 -1
  322. package/dist/modules/storage/lib/service.server.d.mts +54 -38
  323. package/dist/modules/storage/lib/service.server.d.mts.map +1 -1
  324. package/dist/modules/storage/lib/service.server.mjs +11 -5
  325. package/dist/modules/storage/lib/service.server.mjs.map +1 -1
  326. package/dist/modules/storage/lib/validators.d.mts +232 -71
  327. package/dist/modules/storage/lib/validators.d.mts.map +1 -1
  328. package/dist/modules/storage/lib/validators.mjs +16 -25
  329. package/dist/modules/storage/lib/validators.mjs.map +1 -1
  330. package/dist/storage/client.d.mts +2 -1
  331. package/dist/storage/client.mjs +2 -8
  332. package/dist/storage.d.mts +3 -3
  333. package/dist/storage.mjs +3 -3
  334. package/package.json +2 -1
  335. package/src/components/editor/components/editor.client.tsx +7 -2
  336. package/src/components/editor/components/menu.client.tsx +26 -0
  337. package/src/components/editor/extensions/file-handler/extension.ts +69 -0
  338. package/src/components/editor/extensions/file-handler/strategy.ts +167 -0
  339. package/src/components/editor/extensions/file-handler/utils.ts +51 -0
  340. package/src/components/editor/extensions/image/extension.ts +57 -0
  341. package/src/components/editor/extensions/skeleton/extension.ts +35 -0
  342. package/src/components/editor/extensions/skeleton/renderer.tsx +14 -0
  343. package/src/components/editor/lib/constants.ts +16 -2
  344. package/src/components/editor/lib/extensions.ts +4 -1
  345. package/src/components/editor/lib/utils.ts +32 -0
  346. package/src/components/editor/lib/variants.ts +72 -1
  347. package/src/components/entry.ts +2 -1
  348. package/src/lib/hooks/use-indicator.tsx +27 -18
  349. package/src/modules/data-tables/lib/filters/parsers.ts +14 -0
  350. package/src/modules/inline-edit/components/combobox-dropdown.client.tsx +21 -12
  351. package/src/modules/inline-edit/components/combobox.client.tsx +13 -6
  352. package/src/modules/inline-edit/components/date-input.client.tsx +29 -13
  353. package/src/modules/inline-edit/components/date-picker.client.tsx +13 -9
  354. package/src/modules/inline-edit/components/editor.client.tsx +14 -14
  355. package/src/modules/inline-edit/components/input-recipient.client.tsx +15 -8
  356. package/src/modules/inline-edit/components/input-toggle.client.tsx +16 -9
  357. package/src/modules/inline-edit/components/input.client.tsx +38 -24
  358. package/src/modules/inline-edit/components/select.client.tsx +16 -9
  359. package/src/modules/inline-edit/components/switch.client.tsx +10 -6
  360. package/src/modules/inline-edit/components/toggle.client.tsx +13 -9
  361. package/src/modules/inline-edit/entry.client.ts +0 -1
  362. package/src/modules/inline-edit/hooks/use-inline.client copy.tsx +97 -0
  363. package/src/modules/inline-edit/hooks/use-inline.client.tsx +139 -20
  364. package/src/modules/inline-edit/lib/variants.ts +7 -0
  365. package/src/modules/storage/components/image-grid.client.tsx +2 -2
  366. package/src/modules/storage/components/upload-zone-context.client.tsx +3 -4
  367. package/src/modules/storage/components/upload-zone.client.tsx +17 -113
  368. package/src/modules/storage/config/filters.ts +2 -0
  369. package/src/modules/storage/entry.client.ts +2 -1
  370. package/src/modules/storage/lib/create-upload.client.ts +134 -0
  371. package/src/modules/storage/lib/helpers.ts +3 -2
  372. package/src/modules/storage/lib/router.server.ts +1 -0
  373. package/src/modules/storage/lib/schema.ts +2 -0
  374. package/src/modules/storage/lib/service.server.ts +18 -4
  375. package/src/modules/storage/lib/validators.ts +21 -31
  376. package/dist/modules/inline-edit/components/deprecated-editor.client.d.mts +0 -21
  377. package/dist/modules/inline-edit/components/deprecated-editor.client.d.mts.map +0 -1
  378. package/dist/modules/inline-edit/components/deprecated-editor.client.mjs +0 -37
  379. package/dist/modules/inline-edit/components/deprecated-editor.client.mjs.map +0 -1
  380. package/dist/storage/client.mjs.map +0 -1
  381. package/src/modules/inline-edit/components/deprecated-editor.client.tsx +0 -48
@@ -2,7 +2,6 @@
2
2
 
3
3
  import { cn } from "../../../lib/utils/cn.mjs";
4
4
  import { Input } from "../../../components/ui/input.mjs";
5
- import { generateDefaultUUID } from "../../config/db/helpers.mjs";
6
5
  import { useAction } from "../../../lib/hooks/use-action.mjs";
7
6
  import { toast } from "../../../components/ui/sonner.client.mjs";
8
7
  import { UploadZoneContext } from "./upload-zone-context.client.mjs";
@@ -11,7 +10,7 @@ import { useCallback, useState } from "react";
11
10
  import { useQueryClient } from "@tanstack/react-query";
12
11
 
13
12
  //#region src/modules/storage/components/upload-zone.client.tsx
14
- function UploadZone({ namespace, parentId, queryKey, presignAction, confirmAction, deleteAction, onPresignStarted, onPresignCompleted, onConfirmStarted, onConfirmCompleted, onUploadCompleted, onUploadFailed, children, className, onDragOver, onDragLeave, ...props }) {
13
+ function UploadZone({ variables, queryKey, uploadClient, uploadHooks, onUploadCompleted, onUploadFailed, children, className, onDragOver, onDragLeave, ...props }) {
15
14
  const queryClient = useQueryClient();
16
15
  const [isDragging, setIsDragging] = useState(false);
17
16
  /**
@@ -31,9 +30,9 @@ function UploadZone({ namespace, parentId, queryKey, presignAction, confirmActio
31
30
  * Delete mutation
32
31
  */
33
32
  const deleteMutation = useAction({
34
- mutationFn: deleteAction,
35
- onMutate: async (variables) => {
36
- await updateData((prev) => prev.filter((node) => !variables.ids.includes(node.id)));
33
+ mutationFn: async (params) => uploadClient.deleteFiles(params.ids),
34
+ onMutate: async (variables$1) => {
35
+ await updateData((prev) => prev.filter((node) => !variables$1.ids.includes(node.id)));
37
36
  },
38
37
  onSuccess: async () => {
39
38
  await invalidateQuery();
@@ -44,77 +43,28 @@ function UploadZone({ namespace, parentId, queryKey, presignAction, confirmActio
44
43
  }
45
44
  });
46
45
  /**
47
- * Presign upload mutation
46
+ * Upload mutation
48
47
  */
49
- const presignMutation = useAction({
50
- mutationFn: async (params) => {
51
- await onPresignStarted?.(params);
52
- const data = await presignAction(params);
53
- await onPresignCompleted?.(data);
54
- return data;
55
- },
56
- onMutate: async (variables) => {
48
+ const uploadMutation = useAction({
49
+ mutationFn: async (params) => uploadClient.upload(params, uploadHooks),
50
+ onMutate: async (variables$1) => {
57
51
  const newNode = {
58
- ...variables,
52
+ ...variables$1,
59
53
  type: "file",
60
54
  isPending: true
61
55
  };
62
56
  await updateData((prev) => [...prev ?? [], newNode]);
63
57
  },
64
- onError: async (error, variables) => {
65
- await onUploadFailed?.(error);
66
- console.error("Presign error: ", error);
67
- toast.error("Bestand uploaden mislukt", { description: error instanceof Error ? error.message : void 0 });
68
- console.info("Deleting failed upload");
69
- await deleteMutation.mutateAsync({ ids: [variables.id] });
70
- }
71
- });
72
- /**
73
- * Upload mutation
74
- */
75
- const uploadMutation = useAction({
76
- mutationFn: async (input) => {
77
- /**
78
- * Upload the file
79
- */
80
- const uploadResponse = await fetch(input.presignedUrl, {
81
- method: "PUT",
82
- headers: { "Content-Type": input.file.type },
83
- body: input.file
84
- });
85
- if (!uploadResponse.ok) {
86
- const message = await uploadResponse.text();
87
- throw new Error(`Upload failed: ${message}`);
88
- }
89
- },
90
- onError: async (error, variables) => {
91
- await onUploadFailed?.(error);
92
- console.error("Upload error: ", error);
93
- toast.error("Bestand uploaden mislukt", { description: error instanceof Error ? error.message : void 0 });
94
- console.info("Deleting failed upload");
95
- await deleteMutation.mutateAsync({ ids: [variables.id] });
96
- }
97
- });
98
- /**
99
- * Confirm upload mutation
100
- */
101
- const confirmMutation = useAction({
102
- mutationFn: async (params) => {
103
- await onConfirmStarted?.(params.id);
104
- const data = await confirmAction(params);
105
- await onConfirmCompleted?.(data);
106
- return data;
107
- },
108
58
  onSuccess: async (data) => {
109
59
  await onUploadCompleted?.(data);
110
60
  toast.success(`Succesvol geupload: ${data.name}`);
111
61
  },
112
- onError: async (error, variables) => {
62
+ onError: async (error, variables$1) => {
113
63
  await onUploadFailed?.(error);
114
- console.error("Confirm error: ", error);
64
+ console.error("Upload error: ", error);
115
65
  toast.error("Bestand uploaden mislukt", { description: error instanceof Error ? error.message : void 0 });
116
66
  console.info("Deleting failed upload");
117
- await deleteMutation.mutateAsync({ ids: [variables.id] });
67
+ await deleteMutation.mutateAsync({ ids: [variables$1.id] });
118
68
  },
119
69
  onSettled: () => {
120
70
  queryClient.invalidateQueries({ queryKey });
@@ -124,35 +74,15 @@ function UploadZone({ namespace, parentId, queryKey, presignAction, confirmActio
124
74
  * Upload file handler
125
75
  */
126
76
  const onUpload = useCallback(async (file) => {
127
- /**
128
- * Send the presign request
129
- */
130
- const { id, presignedUrl } = await presignMutation.mutateAsync({
131
- id: generateDefaultUUID(),
132
- name: file.name,
133
- size: file.size,
134
- contentType: file.type,
135
- namespace,
136
- parentId
137
- });
138
- /**
139
- * Uploading the file
140
- */
141
- await uploadMutation.mutateAsync({
142
- id,
143
- presignedUrl,
77
+ const req = uploadClient.prepareUpload({
78
+ ...variables,
144
79
  file
145
80
  });
146
- /**
147
- * Confirming the upload
148
- */
149
- return await confirmMutation.mutateAsync({ id });
81
+ return await uploadMutation.mutateAsync(req);
150
82
  }, [
151
- presignMutation,
152
- uploadMutation,
153
- confirmMutation,
154
- namespace,
155
- parentId
83
+ uploadClient,
84
+ variables,
85
+ uploadMutation
156
86
  ]);
157
87
  return /* @__PURE__ */ jsxs("div", {
158
88
  ...props,
@@ -179,9 +109,7 @@ function UploadZone({ namespace, parentId, queryKey, presignAction, confirmActio
179
109
  })
180
110
  }), /* @__PURE__ */ jsx(UploadZoneContext, {
181
111
  value: {
182
- presignMutation: (params) => presignMutation.mutateAsync(params),
183
- confirmMutation: (params) => confirmMutation.mutateAsync(params),
184
- deleteMutation: (params) => deleteMutation.mutateAsync(params),
112
+ uploadClient,
185
113
  onUpload,
186
114
  updateData,
187
115
  invalidateQuery
@@ -1 +1 @@
1
- {"version":3,"file":"upload-zone.client.mjs","names":[],"sources":["../../../../src/modules/storage/components/upload-zone.client.tsx"],"sourcesContent":["\"use client\";\n\nimport { Input } from \"@/components/ui/input\";\nimport { toast } from \"@/components/ui/sonner.client\";\nimport { cn } from \"@/lib/entry\";\nimport { useAction } from \"@/lib/hooks/use-action\";\nimport { generateDefaultUUID } from \"@/modules/config/entry\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { ComponentProps, useCallback, useState } from \"react\";\nimport { FileNode, Node, PresignFileSchema } from \"../lib/validators\";\nimport { UploadZoneContext } from \"./upload-zone-context.client\";\n\nexport type UploadZoneProps = ComponentProps<\"div\"> & {\n namespace: string;\n parentId: string | null;\n\n queryKey: readonly unknown[];\n\n presignAction: (params: PresignFileSchema) => Promise<{ id: string; presignedUrl: string }>;\n onPresignStarted?: (file: PresignFileSchema) => Promise<void> | void;\n onPresignCompleted?: (data: { id: string; presignedUrl: string }) => Promise<void> | void;\n\n confirmAction: (params: { id: string }) => Promise<Node>;\n onConfirmStarted?: (id: string) => Promise<void> | void;\n onConfirmCompleted?: (node: Node) => Promise<void> | void;\n\n deleteAction: (params: { ids: string[] }) => Promise<unknown>;\n\n onUploadCompleted?: (node: Node) => Promise<void> | void;\n onUploadFailed?: (error: unknown) => Promise<void> | void;\n};\n\nexport function UploadZone({\n namespace,\n parentId,\n queryKey,\n presignAction,\n confirmAction,\n deleteAction,\n onPresignStarted,\n onPresignCompleted,\n onConfirmStarted,\n onConfirmCompleted,\n onUploadCompleted,\n onUploadFailed,\n children,\n className,\n onDragOver,\n onDragLeave,\n ...props\n}: UploadZoneProps) {\n const queryClient = useQueryClient();\n const [isDragging, setIsDragging] = useState(false);\n\n /**\n * Optimisticly update the data\n */\n const updateData = useCallback(\n async (newValue: FileNode[] | ((oldValue: FileNode[]) => FileNode[])) => {\n await queryClient.cancelQueries({ queryKey });\n await queryClient.setQueryData(queryKey, newValue);\n },\n [queryClient, queryKey],\n );\n\n /**\n * Invalidate the query\n */\n const invalidateQuery = useCallback(async () => {\n await queryClient.invalidateQueries({ queryKey });\n }, [queryClient, queryKey]);\n\n /**\n * Delete mutation\n */\n const deleteMutation = useAction({\n mutationFn: deleteAction,\n onMutate: async (variables) => {\n await updateData((prev) => prev.filter((node) => !variables.ids.includes(node.id)));\n },\n onSuccess: async () => {\n await invalidateQuery();\n },\n onError: async (error) => {\n await onUploadFailed?.(error);\n console.error(\"Delete failed upload error: \", error);\n },\n });\n\n /**\n * Presign upload mutation\n */\n const presignMutation = useAction({\n mutationFn: async (params: PresignFileSchema) => {\n await onPresignStarted?.(params);\n const data = await presignAction(params);\n await onPresignCompleted?.(data);\n return data;\n },\n onMutate: async (variables) => {\n // Generate a new node\n const newNode = {\n ...variables,\n type: \"file\",\n isPending: true,\n } as FileNode;\n\n await updateData((prev) => [...(prev ?? []), newNode]);\n },\n onError: async (error, variables) => {\n await onUploadFailed?.(error);\n\n console.error(\"Presign error: \", error);\n toast.error(\"Bestand uploaden mislukt\", {\n description: error instanceof Error ? error.message : undefined,\n });\n\n console.info(\"Deleting failed upload\");\n await deleteMutation.mutateAsync({ ids: [variables.id] });\n },\n });\n\n /**\n * Upload mutation\n */\n const uploadMutation = useAction({\n mutationFn: async (input: { id: string; presignedUrl: string; file: File }) => {\n /**\n * Upload the file\n */\n const uploadResponse = await fetch(input.presignedUrl, {\n method: \"PUT\",\n headers: { \"Content-Type\": input.file.type },\n body: input.file,\n });\n\n if (!uploadResponse.ok) {\n const message = await uploadResponse.text();\n throw new Error(`Upload failed: ${message}`);\n }\n },\n onError: async (error, variables) => {\n await onUploadFailed?.(error);\n\n console.error(\"Upload error: \", error);\n toast.error(\"Bestand uploaden mislukt\", {\n description: error instanceof Error ? error.message : undefined,\n });\n\n console.info(\"Deleting failed upload\");\n await deleteMutation.mutateAsync({ ids: [variables.id] });\n },\n });\n\n /**\n * Confirm upload mutation\n */\n const confirmMutation = useAction({\n mutationFn: async (params: { id: string }) => {\n await onConfirmStarted?.(params.id);\n const data = await confirmAction(params);\n await onConfirmCompleted?.(data);\n return data;\n },\n onSuccess: async (data) => {\n await onUploadCompleted?.(data);\n toast.success(`Succesvol geupload: ${data.name}`);\n },\n onError: async (error, variables) => {\n await onUploadFailed?.(error);\n\n console.error(\"Confirm error: \", error);\n toast.error(\"Bestand uploaden mislukt\", {\n description: error instanceof Error ? error.message : undefined,\n });\n\n console.info(\"Deleting failed upload\");\n await deleteMutation.mutateAsync({ ids: [variables.id] });\n },\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey });\n },\n });\n\n /**\n * Upload file handler\n */\n const onUpload = useCallback(\n async (file: File) => {\n /**\n * Send the presign request\n */\n const { id, presignedUrl } = await presignMutation.mutateAsync({\n id: generateDefaultUUID(),\n name: file.name,\n size: file.size,\n contentType: file.type,\n namespace,\n parentId,\n });\n\n /**\n * Uploading the file\n */\n await uploadMutation.mutateAsync({ id, presignedUrl, file });\n\n /**\n * Confirming the upload\n */\n return await confirmMutation.mutateAsync({ id });\n },\n [presignMutation, uploadMutation, confirmMutation, namespace, parentId],\n );\n\n return (\n <div\n {...props}\n className={cn(\"relative z-0 flex-1 space-y-6\", className)}\n onDragOver={(...args) => {\n setIsDragging(true);\n return onDragOver?.(...args);\n }}\n onDragLeave={(...args) => {\n setIsDragging(false);\n return onDragLeave?.(...args);\n }}\n >\n {isDragging && (\n <div className=\"border-primary bg-primary/20 absolute -inset-5 z-10 rounded-md border opacity-70 backdrop-blur-3xl\">\n <Input\n multiple\n type=\"file\"\n className=\"h-full w-full opacity-0\"\n onChange={async (e) => {\n setIsDragging(false);\n\n if (!e.target.files) return;\n\n await Promise.allSettled(Array.from(e.target.files).map(onUpload));\n }}\n />\n </div>\n )}\n <UploadZoneContext\n value={{\n presignMutation: (params) => presignMutation.mutateAsync(params),\n confirmMutation: (params) => confirmMutation.mutateAsync(params),\n deleteMutation: (params) => deleteMutation.mutateAsync(params),\n onUpload,\n updateData,\n invalidateQuery,\n }}\n >\n {children}\n </UploadZoneContext>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAgCA,SAAgB,WAAW,EACzB,WACA,UACA,UACA,eACA,eACA,cACA,kBACA,oBACA,kBACA,oBACA,mBACA,gBACA,UACA,WACA,YACA,aACA,GAAG,SACe;CAClB,MAAM,cAAc,gBAAgB;CACpC,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;;;;CAKnD,MAAM,aAAa,YACjB,OAAO,aAAkE;AACvE,QAAM,YAAY,cAAc,EAAE,UAAU,CAAC;AAC7C,QAAM,YAAY,aAAa,UAAU,SAAS;IAEpD,CAAC,aAAa,SAAS,CACxB;;;;CAKD,MAAM,kBAAkB,YAAY,YAAY;AAC9C,QAAM,YAAY,kBAAkB,EAAE,UAAU,CAAC;IAChD,CAAC,aAAa,SAAS,CAAC;;;;CAK3B,MAAM,iBAAiB,UAAU;EAC/B,YAAY;EACZ,UAAU,OAAO,cAAc;AAC7B,SAAM,YAAY,SAAS,KAAK,QAAQ,SAAS,CAAC,UAAU,IAAI,SAAS,KAAK,GAAG,CAAC,CAAC;;EAErF,WAAW,YAAY;AACrB,SAAM,iBAAiB;;EAEzB,SAAS,OAAO,UAAU;AACxB,SAAM,iBAAiB,MAAM;AAC7B,WAAQ,MAAM,gCAAgC,MAAM;;EAEvD,CAAC;;;;CAKF,MAAM,kBAAkB,UAAU;EAChC,YAAY,OAAO,WAA8B;AAC/C,SAAM,mBAAmB,OAAO;GAChC,MAAM,OAAO,MAAM,cAAc,OAAO;AACxC,SAAM,qBAAqB,KAAK;AAChC,UAAO;;EAET,UAAU,OAAO,cAAc;GAE7B,MAAM,UAAU;IACd,GAAG;IACH,MAAM;IACN,WAAW;IACZ;AAED,SAAM,YAAY,SAAS,CAAC,GAAI,QAAQ,EAAE,EAAG,QAAQ,CAAC;;EAExD,SAAS,OAAO,OAAO,cAAc;AACnC,SAAM,iBAAiB,MAAM;AAE7B,WAAQ,MAAM,mBAAmB,MAAM;AACvC,SAAM,MAAM,4BAA4B,EACtC,aAAa,iBAAiB,QAAQ,MAAM,UAAU,QACvD,CAAC;AAEF,WAAQ,KAAK,yBAAyB;AACtC,SAAM,eAAe,YAAY,EAAE,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;;EAE5D,CAAC;;;;CAKF,MAAM,iBAAiB,UAAU;EAC/B,YAAY,OAAO,UAA4D;;;;GAI7E,MAAM,iBAAiB,MAAM,MAAM,MAAM,cAAc;IACrD,QAAQ;IACR,SAAS,EAAE,gBAAgB,MAAM,KAAK,MAAM;IAC5C,MAAM,MAAM;IACb,CAAC;AAEF,OAAI,CAAC,eAAe,IAAI;IACtB,MAAM,UAAU,MAAM,eAAe,MAAM;AAC3C,UAAM,IAAI,MAAM,kBAAkB,UAAU;;;EAGhD,SAAS,OAAO,OAAO,cAAc;AACnC,SAAM,iBAAiB,MAAM;AAE7B,WAAQ,MAAM,kBAAkB,MAAM;AACtC,SAAM,MAAM,4BAA4B,EACtC,aAAa,iBAAiB,QAAQ,MAAM,UAAU,QACvD,CAAC;AAEF,WAAQ,KAAK,yBAAyB;AACtC,SAAM,eAAe,YAAY,EAAE,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;;EAE5D,CAAC;;;;CAKF,MAAM,kBAAkB,UAAU;EAChC,YAAY,OAAO,WAA2B;AAC5C,SAAM,mBAAmB,OAAO,GAAG;GACnC,MAAM,OAAO,MAAM,cAAc,OAAO;AACxC,SAAM,qBAAqB,KAAK;AAChC,UAAO;;EAET,WAAW,OAAO,SAAS;AACzB,SAAM,oBAAoB,KAAK;AAC/B,SAAM,QAAQ,uBAAuB,KAAK,OAAO;;EAEnD,SAAS,OAAO,OAAO,cAAc;AACnC,SAAM,iBAAiB,MAAM;AAE7B,WAAQ,MAAM,mBAAmB,MAAM;AACvC,SAAM,MAAM,4BAA4B,EACtC,aAAa,iBAAiB,QAAQ,MAAM,UAAU,QACvD,CAAC;AAEF,WAAQ,KAAK,yBAAyB;AACtC,SAAM,eAAe,YAAY,EAAE,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;;EAE3D,iBAAiB;AACf,eAAY,kBAAkB,EAAE,UAAU,CAAC;;EAE9C,CAAC;;;;CAKF,MAAM,WAAW,YACf,OAAO,SAAe;;;;EAIpB,MAAM,EAAE,IAAI,iBAAiB,MAAM,gBAAgB,YAAY;GAC7D,IAAI,qBAAqB;GACzB,MAAM,KAAK;GACX,MAAM,KAAK;GACX,aAAa,KAAK;GAClB;GACA;GACD,CAAC;;;;AAKF,QAAM,eAAe,YAAY;GAAE;GAAI;GAAc;GAAM,CAAC;;;;AAK5D,SAAO,MAAM,gBAAgB,YAAY,EAAE,IAAI,CAAC;IAElD;EAAC;EAAiB;EAAgB;EAAiB;EAAW;EAAS,CACxE;AAED,QACE,qBAAC;EACC,GAAI;EACJ,WAAW,GAAG,iCAAiC,UAAU;EACzD,aAAa,GAAG,SAAS;AACvB,iBAAc,KAAK;AACnB,UAAO,aAAa,GAAG,KAAK;;EAE9B,cAAc,GAAG,SAAS;AACxB,iBAAc,MAAM;AACpB,UAAO,cAAc,GAAG,KAAK;;aAG9B,cACC,oBAAC;GAAI,WAAU;aACb,oBAAC;IACC;IACA,MAAK;IACL,WAAU;IACV,UAAU,OAAO,MAAM;AACrB,mBAAc,MAAM;AAEpB,SAAI,CAAC,EAAE,OAAO,MAAO;AAErB,WAAM,QAAQ,WAAW,MAAM,KAAK,EAAE,OAAO,MAAM,CAAC,IAAI,SAAS,CAAC;;KAEpE;IACE,EAER,oBAAC;GACC,OAAO;IACL,kBAAkB,WAAW,gBAAgB,YAAY,OAAO;IAChE,kBAAkB,WAAW,gBAAgB,YAAY,OAAO;IAChE,iBAAiB,WAAW,eAAe,YAAY,OAAO;IAC9D;IACA;IACA;IACD;GAEA;IACiB;GAChB"}
1
+ {"version":3,"file":"upload-zone.client.mjs","names":["variables"],"sources":["../../../../src/modules/storage/components/upload-zone.client.tsx"],"sourcesContent":["\"use client\";\n\nimport { Input } from \"@/components/ui/input\";\nimport { toast } from \"@/components/ui/sonner.client\";\nimport { cn } from \"@/lib/entry\";\nimport { useAction } from \"@/lib/hooks/use-action\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { ComponentProps, useCallback, useState } from \"react\";\nimport { UploadClient, UploadFileRequest, UploadHooks } from \"../lib/create-upload.client\";\nimport { FileNode, Node, UploadFileSchema } from \"../lib/validators\";\nimport { UploadZoneContext } from \"./upload-zone-context.client\";\n\nexport type UploadZoneProps = ComponentProps<\"div\"> & {\n queryKey: readonly unknown[];\n\n variables: Pick<UploadFileSchema, \"namespace\" | \"parentId\" | \"mode\" | \"hidden\" | \"readonly\">;\n uploadClient: UploadClient;\n uploadHooks?: UploadHooks;\n\n onUploadCompleted?: (node: Node) => Promise<void> | void;\n onUploadFailed?: (error: unknown) => Promise<void> | void;\n};\n\nexport function UploadZone({\n variables,\n queryKey,\n uploadClient,\n uploadHooks,\n onUploadCompleted,\n onUploadFailed,\n children,\n className,\n onDragOver,\n onDragLeave,\n ...props\n}: UploadZoneProps) {\n const queryClient = useQueryClient();\n const [isDragging, setIsDragging] = useState(false);\n\n /**\n * Optimisticly update the data\n */\n const updateData = useCallback(\n async (newValue: FileNode[] | ((oldValue: FileNode[]) => FileNode[])) => {\n await queryClient.cancelQueries({ queryKey });\n await queryClient.setQueryData(queryKey, newValue);\n },\n [queryClient, queryKey],\n );\n\n /**\n * Invalidate the query\n */\n const invalidateQuery = useCallback(async () => {\n await queryClient.invalidateQueries({ queryKey });\n }, [queryClient, queryKey]);\n\n /**\n * Delete mutation\n */\n const deleteMutation = useAction({\n mutationFn: async (params: { ids: string[] }) => uploadClient.deleteFiles(params.ids),\n onMutate: async (variables) => {\n await updateData((prev) => prev.filter((node) => !variables.ids.includes(node.id)));\n },\n onSuccess: async () => {\n await invalidateQuery();\n },\n onError: async (error) => {\n await onUploadFailed?.(error);\n console.error(\"Delete failed upload error: \", error);\n },\n });\n\n /**\n * Upload mutation\n */\n const uploadMutation = useAction({\n mutationFn: async (params: UploadFileRequest) => uploadClient.upload(params, uploadHooks),\n onMutate: async (variables) => {\n // Generate a new node\n const newNode = {\n ...variables,\n type: \"file\",\n isPending: true,\n } as FileNode;\n\n await updateData((prev) => [...(prev ?? []), newNode]);\n },\n onSuccess: async (data) => {\n await onUploadCompleted?.(data);\n toast.success(`Succesvol geupload: ${data.name}`);\n },\n onError: async (error, variables) => {\n await onUploadFailed?.(error);\n\n console.error(\"Upload error: \", error);\n toast.error(\"Bestand uploaden mislukt\", {\n description: error instanceof Error ? error.message : undefined,\n });\n\n console.info(\"Deleting failed upload\");\n await deleteMutation.mutateAsync({ ids: [variables.id] });\n },\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey });\n },\n });\n\n /**\n * Upload file handler\n */\n const onUpload = useCallback(\n async (file: File) => {\n const req = uploadClient.prepareUpload({ ...variables, file });\n return await uploadMutation.mutateAsync(req);\n },\n [uploadClient, variables, uploadMutation],\n );\n\n return (\n <div\n {...props}\n className={cn(\"relative z-0 flex-1 space-y-6\", className)}\n onDragOver={(...args) => {\n setIsDragging(true);\n return onDragOver?.(...args);\n }}\n onDragLeave={(...args) => {\n setIsDragging(false);\n return onDragLeave?.(...args);\n }}\n >\n {isDragging && (\n <div className=\"border-primary bg-primary/20 absolute -inset-5 z-10 rounded-md border opacity-70 backdrop-blur-3xl\">\n <Input\n multiple\n type=\"file\"\n className=\"h-full w-full opacity-0\"\n onChange={async (e) => {\n setIsDragging(false);\n\n if (!e.target.files) return;\n\n await Promise.allSettled(Array.from(e.target.files).map(onUpload));\n }}\n />\n </div>\n )}\n <UploadZoneContext\n value={{\n uploadClient,\n onUpload,\n updateData,\n invalidateQuery,\n }}\n >\n {children}\n </UploadZoneContext>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAuBA,SAAgB,WAAW,EACzB,WACA,UACA,cACA,aACA,mBACA,gBACA,UACA,WACA,YACA,aACA,GAAG,SACe;CAClB,MAAM,cAAc,gBAAgB;CACpC,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;;;;CAKnD,MAAM,aAAa,YACjB,OAAO,aAAkE;AACvE,QAAM,YAAY,cAAc,EAAE,UAAU,CAAC;AAC7C,QAAM,YAAY,aAAa,UAAU,SAAS;IAEpD,CAAC,aAAa,SAAS,CACxB;;;;CAKD,MAAM,kBAAkB,YAAY,YAAY;AAC9C,QAAM,YAAY,kBAAkB,EAAE,UAAU,CAAC;IAChD,CAAC,aAAa,SAAS,CAAC;;;;CAK3B,MAAM,iBAAiB,UAAU;EAC/B,YAAY,OAAO,WAA8B,aAAa,YAAY,OAAO,IAAI;EACrF,UAAU,OAAO,gBAAc;AAC7B,SAAM,YAAY,SAAS,KAAK,QAAQ,SAAS,CAACA,YAAU,IAAI,SAAS,KAAK,GAAG,CAAC,CAAC;;EAErF,WAAW,YAAY;AACrB,SAAM,iBAAiB;;EAEzB,SAAS,OAAO,UAAU;AACxB,SAAM,iBAAiB,MAAM;AAC7B,WAAQ,MAAM,gCAAgC,MAAM;;EAEvD,CAAC;;;;CAKF,MAAM,iBAAiB,UAAU;EAC/B,YAAY,OAAO,WAA8B,aAAa,OAAO,QAAQ,YAAY;EACzF,UAAU,OAAO,gBAAc;GAE7B,MAAM,UAAU;IACd,GAAGA;IACH,MAAM;IACN,WAAW;IACZ;AAED,SAAM,YAAY,SAAS,CAAC,GAAI,QAAQ,EAAE,EAAG,QAAQ,CAAC;;EAExD,WAAW,OAAO,SAAS;AACzB,SAAM,oBAAoB,KAAK;AAC/B,SAAM,QAAQ,uBAAuB,KAAK,OAAO;;EAEnD,SAAS,OAAO,OAAO,gBAAc;AACnC,SAAM,iBAAiB,MAAM;AAE7B,WAAQ,MAAM,kBAAkB,MAAM;AACtC,SAAM,MAAM,4BAA4B,EACtC,aAAa,iBAAiB,QAAQ,MAAM,UAAU,QACvD,CAAC;AAEF,WAAQ,KAAK,yBAAyB;AACtC,SAAM,eAAe,YAAY,EAAE,KAAK,CAACA,YAAU,GAAG,EAAE,CAAC;;EAE3D,iBAAiB;AACf,eAAY,kBAAkB,EAAE,UAAU,CAAC;;EAE9C,CAAC;;;;CAKF,MAAM,WAAW,YACf,OAAO,SAAe;EACpB,MAAM,MAAM,aAAa,cAAc;GAAE,GAAG;GAAW;GAAM,CAAC;AAC9D,SAAO,MAAM,eAAe,YAAY,IAAI;IAE9C;EAAC;EAAc;EAAW;EAAe,CAC1C;AAED,QACE,qBAAC;EACC,GAAI;EACJ,WAAW,GAAG,iCAAiC,UAAU;EACzD,aAAa,GAAG,SAAS;AACvB,iBAAc,KAAK;AACnB,UAAO,aAAa,GAAG,KAAK;;EAE9B,cAAc,GAAG,SAAS;AACxB,iBAAc,MAAM;AACpB,UAAO,cAAc,GAAG,KAAK;;aAG9B,cACC,oBAAC;GAAI,WAAU;aACb,oBAAC;IACC;IACA,MAAK;IACL,WAAU;IACV,UAAU,OAAO,MAAM;AACrB,mBAAc,MAAM;AAEpB,SAAI,CAAC,EAAE,OAAO,MAAO;AAErB,WAAM,QAAQ,WAAW,MAAM,KAAK,EAAE,OAAO,MAAM,CAAC,IAAI,SAAS,CAAC;;KAEpE;IACE,EAER,oBAAC;GACC,OAAO;IACL;IACA;IACA;IACA;IACD;GAEA;IACiB;GAChB"}
@@ -9,6 +9,8 @@ declare const nodesTableFilters: {
9
9
  }>>>;
10
10
  nodeIds: ParseFilter<z.ZodArray<z.ZodString>>;
11
11
  isDeleted: ParseFilter<z.ZodBoolean>;
12
+ isOrphaned: ParseFilter<z.ZodBoolean>;
13
+ hidden: ParseFilter<z.ZodBoolean>;
12
14
  };
13
15
  //#endregion
14
16
  export { nodesTableFilters };
@@ -1 +1 @@
1
- {"version":3,"file":"filters.d.mts","names":[],"sources":["../../../../src/modules/storage/config/filters.ts"],"sourcesContent":[],"mappings":";;;;cAKa;;;IAAA,MAAA,EAAA,QAIX"}
1
+ {"version":3,"file":"filters.d.mts","names":[],"sources":["../../../../src/modules/storage/config/filters.ts"],"sourcesContent":[],"mappings":";;;;cAKa;;;IAAA,MAAA,EAAA,QAMX"}
@@ -7,7 +7,9 @@ import z from "zod";
7
7
  const nodesTableFilters = createTableFilters({
8
8
  types: parseFilterArray(z.array(z.enum(nodeTypeEnum.enumValues))),
9
9
  nodeIds: parseFilterArray(z.array(z.string())),
10
- isDeleted: parseFilterBoolean(z.boolean())
10
+ isDeleted: parseFilterBoolean(z.boolean()),
11
+ isOrphaned: parseFilterBoolean(z.boolean()),
12
+ hidden: parseFilterBoolean(z.boolean())
11
13
  });
12
14
 
13
15
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"filters.mjs","names":[],"sources":["../../../../src/modules/storage/config/filters.ts"],"sourcesContent":["import { createTableFilters } from \"@/modules/data-tables/lib/filters/config\";\nimport { parseFilterArray, parseFilterBoolean } from \"@/modules/data-tables/lib/filters/parsers\";\nimport z from \"zod\";\nimport { nodeTypeEnum } from \"../lib/schema\";\n\nexport const nodesTableFilters = createTableFilters({\n types: parseFilterArray(z.array(z.enum(nodeTypeEnum.enumValues))),\n nodeIds: parseFilterArray(z.array(z.string())),\n isDeleted: parseFilterBoolean(z.boolean()),\n});\n"],"mappings":";;;;;;AAKA,MAAa,oBAAoB,mBAAmB;CAClD,OAAO,iBAAiB,EAAE,MAAM,EAAE,KAAK,aAAa,WAAW,CAAC,CAAC;CACjE,SAAS,iBAAiB,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;CAC9C,WAAW,mBAAmB,EAAE,SAAS,CAAC;CAC3C,CAAC"}
1
+ {"version":3,"file":"filters.mjs","names":[],"sources":["../../../../src/modules/storage/config/filters.ts"],"sourcesContent":["import { createTableFilters } from \"@/modules/data-tables/lib/filters/config\";\nimport { parseFilterArray, parseFilterBoolean } from \"@/modules/data-tables/lib/filters/parsers\";\nimport z from \"zod\";\nimport { nodeTypeEnum } from \"../lib/schema\";\n\nexport const nodesTableFilters = createTableFilters({\n types: parseFilterArray(z.array(z.enum(nodeTypeEnum.enumValues))),\n nodeIds: parseFilterArray(z.array(z.string())),\n isDeleted: parseFilterBoolean(z.boolean()),\n isOrphaned: parseFilterBoolean(z.boolean()),\n hidden: parseFilterBoolean(z.boolean()),\n});\n"],"mappings":";;;;;;AAKA,MAAa,oBAAoB,mBAAmB;CAClD,OAAO,iBAAiB,EAAE,MAAM,EAAE,KAAK,aAAa,WAAW,CAAC,CAAC;CACjE,SAAS,iBAAiB,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;CAC9C,WAAW,mBAAmB,EAAE,SAAS,CAAC;CAC1C,YAAY,mBAAmB,EAAE,SAAS,CAAC;CAC3C,QAAQ,mBAAmB,EAAE,SAAS,CAAC;CACxC,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { PresignFileSchema, SelectNodeSchema, UpdateNodeSchema } from "./validators.mjs";
2
+ import { BulkActionSchema } from "../../router/lib/validators.mjs";
3
+ import "../../../router.mjs";
4
+
5
+ //#region src/modules/storage/lib/create-upload.client.d.ts
6
+ /**
7
+ * Upload request
8
+ */
9
+ type PrepareUploadInput = Omit<UploadFileRequest, "id" | "name" | "size" | "contentType">;
10
+ type UploadFileRequest = PresignFileSchema & {
11
+ file: File;
12
+ };
13
+ type UploadHooks = {
14
+ beforePresign?: (input: UploadFileRequest) => Promise<void> | void;
15
+ afterPresign?: (presignResult: {
16
+ id: string;
17
+ presignedUrl: string;
18
+ }) => Promise<void> | void;
19
+ beforeConfirm?: (presignResult: {
20
+ id: string;
21
+ presignedUrl: string;
22
+ }) => Promise<void> | void;
23
+ afterConfirm?: (node: SelectNodeSchema) => Promise<void> | void;
24
+ };
25
+ /**
26
+ * Upload client
27
+ */
28
+ type UploadClient = {
29
+ prepareUpload: (input: PrepareUploadInput) => UploadFileRequest;
30
+ upload: (input: UploadFileRequest, hooks?: UploadHooks) => Promise<SelectNodeSchema>;
31
+ deleteFiles: (ids: string[]) => Promise<void>;
32
+ updateNode: (id: string, data: UpdateNodeSchema) => Promise<SelectNodeSchema>;
33
+ };
34
+ /**
35
+ * Create upload client
36
+ */
37
+ type CreateUploadClientProps = {
38
+ endpoints: {
39
+ presign: (input: PresignFileSchema) => Promise<{
40
+ id: string;
41
+ presignedUrl: string;
42
+ }>;
43
+ confirm: (input: {
44
+ id: string;
45
+ }) => Promise<SelectNodeSchema>;
46
+ deleteNodes: (input: BulkActionSchema) => Promise<void>;
47
+ updateNode: (input: {
48
+ id: string;
49
+ data: UpdateNodeSchema;
50
+ }) => Promise<SelectNodeSchema>;
51
+ };
52
+ };
53
+ declare function createUploadClient(props: CreateUploadClientProps): UploadClient;
54
+ //#endregion
55
+ export { PrepareUploadInput, UploadClient, UploadFileRequest, UploadHooks, createUploadClient };
56
+ //# sourceMappingURL=create-upload.client.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-upload.client.d.mts","names":[],"sources":["../../../../src/modules/storage/lib/create-upload.client.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAOY,KAAA,kBAAA,GAAqB,IAAH,CAAQ,iBAAA,EAAL,IAAA,GAAI,MAAA,GAAA,MAAA,GAAA,aAAA,CAAA;AACzB,KAAA,iBAAA,GAAoB,iBAAA,GAAA;EACpB,IAAA,EADgD,IAChD;CACc;AAAsB,KADpC,WAAA,GACoC;EAC0B,aAAA,CAAA,EAAA,CAAA,KAAA,EADhD,iBACgD,EAAA,GAD1B,OAC0B,CAAA,IAAA,CAAA,GAAA,IAAA;EACC,YAAA,CAAA,EAAA,CAAA,aAAA,EAAA;IACnD,EAAA,EAAA,MAAA;IAAqB,YAAA,EAAA,MAAA;EAAO,CAAA,EAAA,GAFsB,OAEtB,CAAA,IAAA,CAAA,GAAA,IAAA;EAMxC,aAAA,CAAA,EAAY,CAAA,aAAA,EAAA;IACC,EAAA,EAAA,MAAA;IAAuB,YAAA,EAAA,MAAA;EAC9B,CAAA,EAAA,GATyD,OASzD,CAAA,IAAA,CAAA,GAAA,IAAA;EAA2B,YAAA,CAAA,EAAA,CAAA,IAAA,EARrB,gBAQqB,EAAA,GARA,OAQA,CAAA,IAAA,CAAA,GAAA,IAAA;CAAwB;;;;AAEP,KAJlD,YAAA,GAIkD;EAAR,aAAA,EAAA,CAAA,KAAA,EAH7B,kBAG6B,EAAA,GAHN,iBAGM;EAAO,MAAA,EAAA,CAAA,KAAA,EAF3C,iBAE2C,EAAA,KAAA,CAAA,EAFhB,WAEgB,EAAA,GAFA,OAEA,CAFQ,gBAER,CAAA;EAMxD,WAAA,EAAA,CAAA,GAAA,EAAA,MAAA,EAAuB,EAAA,GAPM,OAON,CAAA,IAAA,CAAA;EAEP,UAAA,EAAA,CAAA,EAAA,EAAA,MAAA,EAAA,IAAA,EARY,gBAQZ,EAAA,GARiC,OAQjC,CARyC,gBAQzC,CAAA;CAAsB;;;;KAFtC,uBAAA,GAIyC;EACF,SAAA,EAAA;IAA+B,OAAA,EAAA,CAAA,KAAA,EAHtD,iBAGsD,EAAA,GAHhC,OAGgC,CAAA;MAAR,EAAA,EAAA,MAAA;MAAO,YAAA,EAAA,MAAA;IAI1D,CAAA,CAAA;;;UANwB,QAAQ;yBACvB,qBAAqB;;;YACF;UAAuB,QAAQ;;;iBAI3D,kBAAA,QAA0B,0BAA0B"}
@@ -0,0 +1,98 @@
1
+ import { generateDefaultUUID } from "../../config/db/helpers.mjs";
2
+
3
+ //#region src/modules/storage/lib/create-upload.client.ts
4
+ function createUploadClient(props) {
5
+ /**
6
+ * Create input schema for the upload method
7
+ * @param {PrepareUploadInput} input
8
+ * @returns {UploadFileRequest}
9
+ */
10
+ function prepareUpload(input) {
11
+ return {
12
+ ...input,
13
+ id: generateDefaultUUID(),
14
+ name: input.file.name,
15
+ size: input.file.size,
16
+ contentType: input.file.type
17
+ };
18
+ }
19
+ /**
20
+ * Upload file to the server
21
+ * @param {UploadFileRequest} input
22
+ * @returns {Promise<SelectNodeSchema>}
23
+ */
24
+ async function upload(input, hooks) {
25
+ try {
26
+ /**
27
+ * Presign
28
+ */
29
+ await hooks?.beforePresign?.(input);
30
+ const presignResult = await props.endpoints.presign(input);
31
+ await hooks?.afterPresign?.(presignResult);
32
+ /**
33
+ * Upload the file
34
+ */
35
+ const uploadResponse = await fetch(presignResult.presignedUrl, {
36
+ method: "PUT",
37
+ headers: { "Content-Type": input.file.type },
38
+ body: input.file
39
+ });
40
+ if (!uploadResponse.ok) {
41
+ const message = await uploadResponse.text();
42
+ throw new Error(`Upload failed: ${message}`);
43
+ }
44
+ /**
45
+ * Confirm
46
+ */
47
+ await hooks?.beforeConfirm?.(presignResult);
48
+ const node = await props.endpoints.confirm({ id: presignResult.id });
49
+ await hooks?.afterConfirm?.(node);
50
+ /**
51
+ * Return
52
+ */
53
+ return node;
54
+ } catch (err) {
55
+ console.error("Upload error: ", err);
56
+ /**
57
+ * Delete if upload failed
58
+ * */
59
+ try {
60
+ await deleteFiles([input.id]);
61
+ } catch (cleanupErr) {
62
+ console.error("Cleanup delete failed:", cleanupErr);
63
+ }
64
+ throw err;
65
+ }
66
+ }
67
+ /**
68
+ * Delete files
69
+ * @param {string[]} ids
70
+ */
71
+ async function deleteFiles(ids) {
72
+ await props.endpoints.deleteNodes({ ids });
73
+ }
74
+ /**
75
+ * Update node
76
+ * @param {string} id
77
+ * @param {UpdateNodeSchema} data
78
+ */
79
+ async function updateNode(id, data) {
80
+ return props.endpoints.updateNode({
81
+ id,
82
+ data
83
+ });
84
+ }
85
+ /**
86
+ * Upload client
87
+ */
88
+ return {
89
+ prepareUpload,
90
+ upload,
91
+ deleteFiles,
92
+ updateNode
93
+ };
94
+ }
95
+
96
+ //#endregion
97
+ export { createUploadClient };
98
+ //# sourceMappingURL=create-upload.client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-upload.client.mjs","names":[],"sources":["../../../../src/modules/storage/lib/create-upload.client.ts"],"sourcesContent":["import { generateDefaultUUID } from \"@/modules/config/entry\";\nimport { BulkActionSchema } from \"@/modules/router/entry\";\nimport { PresignFileSchema, SelectNodeSchema, UpdateNodeSchema } from \"./validators\";\n\n/**\n * Upload request\n */\nexport type PrepareUploadInput = Omit<UploadFileRequest, \"id\" | \"name\" | \"size\" | \"contentType\">;\nexport type UploadFileRequest = PresignFileSchema & { file: File };\nexport type UploadHooks = {\n beforePresign?: (input: UploadFileRequest) => Promise<void> | void;\n afterPresign?: (presignResult: { id: string; presignedUrl: string }) => Promise<void> | void;\n beforeConfirm?: (presignResult: { id: string; presignedUrl: string }) => Promise<void> | void;\n afterConfirm?: (node: SelectNodeSchema) => Promise<void> | void;\n};\n\n/**\n * Upload client\n */\nexport type UploadClient = {\n prepareUpload: (input: PrepareUploadInput) => UploadFileRequest;\n upload: (input: UploadFileRequest, hooks?: UploadHooks) => Promise<SelectNodeSchema>;\n deleteFiles: (ids: string[]) => Promise<void>;\n updateNode: (id: string, data: UpdateNodeSchema) => Promise<SelectNodeSchema>;\n};\n\n/**\n * Create upload client\n */\ntype CreateUploadClientProps = {\n endpoints: {\n presign: (input: PresignFileSchema) => Promise<{ id: string; presignedUrl: string }>;\n confirm: (input: { id: string }) => Promise<SelectNodeSchema>;\n deleteNodes: (input: BulkActionSchema) => Promise<void>;\n updateNode: (input: { id: string; data: UpdateNodeSchema }) => Promise<SelectNodeSchema>;\n };\n};\n\nexport function createUploadClient(props: CreateUploadClientProps): UploadClient {\n /**\n * Create input schema for the upload method\n * @param {PrepareUploadInput} input\n * @returns {UploadFileRequest}\n */\n function prepareUpload(input: PrepareUploadInput): UploadFileRequest {\n return {\n ...input,\n id: generateDefaultUUID(),\n name: input.file.name,\n size: input.file.size,\n contentType: input.file.type,\n };\n }\n\n /**\n * Upload file to the server\n * @param {UploadFileRequest} input\n * @returns {Promise<SelectNodeSchema>}\n */\n async function upload(input: UploadFileRequest, hooks?: UploadHooks): Promise<SelectNodeSchema> {\n try {\n /**\n * Presign\n */\n await hooks?.beforePresign?.(input);\n const presignResult = await props.endpoints.presign(input);\n await hooks?.afterPresign?.(presignResult);\n /**\n * Upload the file\n */\n const uploadResponse = await fetch(presignResult.presignedUrl, {\n method: \"PUT\",\n headers: { \"Content-Type\": input.file.type },\n body: input.file,\n });\n\n if (!uploadResponse.ok) {\n const message = await uploadResponse.text();\n throw new Error(`Upload failed: ${message}`);\n }\n\n /**\n * Confirm\n */\n await hooks?.beforeConfirm?.(presignResult);\n const node = await props.endpoints.confirm({ id: presignResult.id });\n await hooks?.afterConfirm?.(node);\n /**\n * Return\n */\n return node;\n } catch (err) {\n console.error(\"Upload error: \", err);\n\n /**\n * Delete if upload failed\n * */\n try {\n await deleteFiles([input.id]);\n } catch (cleanupErr) {\n console.error(\"Cleanup delete failed:\", cleanupErr);\n }\n\n throw err;\n }\n }\n\n /**\n * Delete files\n * @param {string[]} ids\n */\n async function deleteFiles(ids: string[]) {\n await props.endpoints.deleteNodes({ ids });\n }\n\n /**\n * Update node\n * @param {string} id\n * @param {UpdateNodeSchema} data\n */\n async function updateNode(id: string, data: UpdateNodeSchema) {\n return props.endpoints.updateNode({ id, data });\n }\n\n /**\n * Upload client\n */\n return {\n prepareUpload,\n upload,\n deleteFiles,\n updateNode,\n };\n}\n"],"mappings":";;;AAsCA,SAAgB,mBAAmB,OAA8C;;;;;;CAM/E,SAAS,cAAc,OAA8C;AACnE,SAAO;GACL,GAAG;GACH,IAAI,qBAAqB;GACzB,MAAM,MAAM,KAAK;GACjB,MAAM,MAAM,KAAK;GACjB,aAAa,MAAM,KAAK;GACzB;;;;;;;CAQH,eAAe,OAAO,OAA0B,OAAgD;AAC9F,MAAI;;;;AAIF,SAAM,OAAO,gBAAgB,MAAM;GACnC,MAAM,gBAAgB,MAAM,MAAM,UAAU,QAAQ,MAAM;AAC1D,SAAM,OAAO,eAAe,cAAc;;;;GAI1C,MAAM,iBAAiB,MAAM,MAAM,cAAc,cAAc;IAC7D,QAAQ;IACR,SAAS,EAAE,gBAAgB,MAAM,KAAK,MAAM;IAC5C,MAAM,MAAM;IACb,CAAC;AAEF,OAAI,CAAC,eAAe,IAAI;IACtB,MAAM,UAAU,MAAM,eAAe,MAAM;AAC3C,UAAM,IAAI,MAAM,kBAAkB,UAAU;;;;;AAM9C,SAAM,OAAO,gBAAgB,cAAc;GAC3C,MAAM,OAAO,MAAM,MAAM,UAAU,QAAQ,EAAE,IAAI,cAAc,IAAI,CAAC;AACpE,SAAM,OAAO,eAAe,KAAK;;;;AAIjC,UAAO;WACA,KAAK;AACZ,WAAQ,MAAM,kBAAkB,IAAI;;;;AAKpC,OAAI;AACF,UAAM,YAAY,CAAC,MAAM,GAAG,CAAC;YACtB,YAAY;AACnB,YAAQ,MAAM,0BAA0B,WAAW;;AAGrD,SAAM;;;;;;;CAQV,eAAe,YAAY,KAAe;AACxC,QAAM,MAAM,UAAU,YAAY,EAAE,KAAK,CAAC;;;;;;;CAQ5C,eAAe,WAAW,IAAY,MAAwB;AAC5D,SAAO,MAAM,UAAU,WAAW;GAAE;GAAI;GAAM,CAAC;;;;;AAMjD,QAAO;EACL;EACA;EACA;EACA;EACD"}
@@ -1,5 +1,5 @@
1
- import { FileNode, GetFileURLSchema, Node } from "./validators.mjs";
2
1
  import { ImageVariant } from "./constants.mjs";
2
+ import { CreateNodeSchema, GetFileURLSchema, Node } from "./validators.mjs";
3
3
  import { ImageLoaderProps } from "next/image";
4
4
 
5
5
  //#region src/modules/storage/lib/helpers.d.ts
@@ -35,7 +35,7 @@ type NodeSubtype = (typeof nodeSubtypes)[number];
35
35
  declare function inferNodeSubtype({
36
36
  name,
37
37
  contentType
38
- }: Pick<FileNode, "name" | "contentType">): NodeSubtype;
38
+ }: Pick<CreateNodeSchema, "name" | "contentType">): NodeSubtype;
39
39
  /**
40
40
  * Render bytes
41
41
  */
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.mts","names":[],"sources":["../../../../src/modules/storage/lib/helpers.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAOA;AAAkC,iBAAlB,MAAA,CAAkB,IAAA,EAAL,IAAK,CAAA,IAAA,EAAA,MAAA,CAAA,CAAA,EAAA,IAAA,IAAwB,IAAxB;;;;AAOlB,iBAAA,QAAA,CAAQ,IAAA,EAAO,IAAP,CAAY,IAAZ,EAAA,MAAA,CAAA,CAAA,EAAA,IAAA,IAAoC,IAApC;;;;AAAwC,iBAOhD,UAAA,CAPgD,EAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAOhB,OAPgB,CAOR,gBAPQ,CAAA,CAAA,EAAA,MAAA;AAOhE;AAYA;;AAAmC,iBAAnB,WAAA,CAAmB;EAAA,GAAA;EAAA;AAAA,CAAA,EAAS,gBAAT,CAAA,EAAA,MAAA;;;AASnC;AAOa,iBAPG,iBAAA,CAeN,EAAA,EAAA,MAAA,EAAA,OAAA,EAf6C,YAe7C,CAAA,EAAA,MAAA;AAEV;AAEA;;AAEE,cAdW,YAcX,EAAA,SAAA,CAAA,OAAA,EAAA,UAAA,EAAA,aAAA,EAAA,OAAA,EAAA,OAAA,EAAA,SAAA,EAAA,OAAA,CAAA;AACM,KALI,WAAA,GAKJ,CAAA,OAL0B,YAK1B,CAAA,CAAA,MAAA,CAAA;AAAL,iBAHa,gBAAA,CAGb;EAAA,IAAA;EAAA;AAAA,CAAA,EAAA,IAAA,CAAK,QAAL,EAAA,MAAA,GAAA,aAAA,CAAA,CAAA,EAAyC,WAAzC;;;AAoBH;iBAAgB,WAAA"}
1
+ {"version":3,"file":"helpers.d.mts","names":[],"sources":["../../../../src/modules/storage/lib/helpers.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAOA;AAAkC,iBAAlB,MAAA,CAAkB,IAAA,EAAL,IAAK,CAAA,IAAA,EAAA,MAAA,CAAA,CAAA,EAAA,IAAA,IAAwB,IAAxB;;;;AAOlB,iBAAA,QAAA,CAAQ,IAAA,EAAO,IAAP,CAAY,IAAZ,EAAA,MAAA,CAAA,CAAA,EAAA,IAAA,IAAoC,IAApC;;;;AAAwC,iBAOhD,UAAA,CAPgD,EAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAOhB,OAPgB,CAOR,gBAPQ,CAAA,CAAA,EAAA,MAAA;AAOhE;AAYA;;AAAmC,iBAAnB,WAAA,CAAmB;EAAA,GAAA;EAAA;AAAA,CAAA,EAAS,gBAAT,CAAA,EAAA,MAAA;;;AASnC;AAOa,iBAPG,iBAAA,CAeN,EAAA,EAAA,MAAA,EAAA,OAAA,EAf6C,YAe7C,CAAA,EAAA,MAAA;AAEV;AAEA;;AAEE,cAdW,YAcX,EAAA,SAAA,CAAA,OAAA,EAAA,UAAA,EAAA,aAAA,EAAA,OAAA,EAAA,OAAA,EAAA,SAAA,EAAA,OAAA,CAAA;AACM,KALI,WAAA,GAKJ,CAAA,OAL0B,YAK1B,CAAA,CAAA,MAAA,CAAA;AAAL,iBAHa,gBAAA,CAGb;EAAA,IAAA;EAAA;AAAA,CAAA,EAAA,IAAA,CAAK,gBAAL,EAAA,MAAA,GAAA,aAAA,CAAA,CAAA,EAAiD,WAAjD;;;AAqBH;iBAAgB,WAAA"}
@@ -48,6 +48,7 @@ const nodeSubtypes = [
48
48
  "other"
49
49
  ];
50
50
  function inferNodeSubtype({ name, contentType }) {
51
+ if (!contentType) return "other";
51
52
  if (contentType?.startsWith("image/")) return "image";
52
53
  if (contentType === "application/pdf") return "document";
53
54
  if (contentType?.includes("spreadsheet")) return "spreadsheet";
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.mjs","names":[],"sources":["../../../../src/modules/storage/lib/helpers.ts"],"sourcesContent":["import { ImageLoaderProps } from \"next/image\";\nimport { ImageVariant } from \"./constants\";\nimport { FileNode, GetFileURLSchema, Node } from \"./validators\";\n\n/**\n * Check if the node is a file\n */\nexport function isFile(node: Pick<Node, \"type\">): node is Node {\n return node.type === \"file\";\n}\n\n/**\n * Check if the node is a folder\n */\nexport function isFolder(node: Pick<Node, \"type\">): node is Node {\n return node.type === \"folder\";\n}\n\n/**\n * Get file url\n */\nexport function getFileUrl(id: string, options: Partial<GetFileURLSchema> = {}) {\n const searchParams = new URLSearchParams();\n searchParams.set(\"id\", id);\n if (options?.variant) searchParams.set(\"variant\", options.variant);\n if (options?.disposition) searchParams.set(\"disposition\", options.disposition);\n\n return `/api/storage/files?${searchParams.toString()}`;\n}\n\n/**\n * Image loader\n */\nexport function imageLoader({ src, width }: ImageLoaderProps) {\n const url = new URL(src, window.location.origin);\n url.searchParams.set(\"variant\", `preview-${width}`);\n return url.toString();\n}\n\n/**\n * Generate object key\n */\nexport function getDriveBucketKey(id: string, variant: ImageVariant) {\n return `storage/${id}/${variant}`;\n}\n\n/**\n * Node subtype\n */\nexport const nodeSubtypes = [\n \"image\",\n \"document\",\n \"spreadsheet\",\n \"video\",\n \"audio\",\n \"archive\",\n \"other\",\n] as const;\n\nexport type NodeSubtype = (typeof nodeSubtypes)[number];\n\nexport function inferNodeSubtype({\n name,\n contentType,\n}: Pick<FileNode, \"name\" | \"contentType\">): NodeSubtype {\n if (contentType?.startsWith(\"image/\")) return \"image\";\n if (contentType === \"application/pdf\") return \"document\";\n if (contentType?.includes(\"spreadsheet\")) return \"spreadsheet\";\n if (contentType?.startsWith(\"video/\")) return \"video\";\n if (contentType?.startsWith(\"audio/\")) return \"audio\";\n if (contentType?.includes(\"zip\") || contentType?.includes(\"archive\")) return \"archive\";\n\n const ext = name.split(\".\").pop()?.toLowerCase();\n if ([\"jpg\", \"jpeg\", \"png\", \"gif\", \"bmp\", \"webp\"].includes(ext ?? \"\")) return \"image\";\n if ([\"pdf\", \"doc\", \"docx\", \"txt\"].includes(ext ?? \"\")) return \"document\";\n if ([\"xls\", \"xlsx\", \"ods\"].includes(ext ?? \"\")) return \"spreadsheet\";\n if ([\"zip\", \"rar\", \"7z\"].includes(ext ?? \"\")) return \"archive\";\n\n return \"other\";\n}\n\n/**\n * Render bytes\n */\nexport function renderBytes(bytes: number) {\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"];\n\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(2)}${units[unitIndex]}`;\n}\n"],"mappings":";;;;AAOA,SAAgB,OAAO,MAAwC;AAC7D,QAAO,KAAK,SAAS;;;;;AAMvB,SAAgB,SAAS,MAAwC;AAC/D,QAAO,KAAK,SAAS;;;;;AAMvB,SAAgB,WAAW,IAAY,UAAqC,EAAE,EAAE;CAC9E,MAAM,eAAe,IAAI,iBAAiB;AAC1C,cAAa,IAAI,MAAM,GAAG;AAC1B,KAAI,SAAS,QAAS,cAAa,IAAI,WAAW,QAAQ,QAAQ;AAClE,KAAI,SAAS,YAAa,cAAa,IAAI,eAAe,QAAQ,YAAY;AAE9E,QAAO,sBAAsB,aAAa,UAAU;;;;;AAMtD,SAAgB,YAAY,EAAE,KAAK,SAA2B;CAC5D,MAAM,MAAM,IAAI,IAAI,KAAK,OAAO,SAAS,OAAO;AAChD,KAAI,aAAa,IAAI,WAAW,WAAW,QAAQ;AACnD,QAAO,IAAI,UAAU;;;;;AAMvB,SAAgB,kBAAkB,IAAY,SAAuB;AACnE,QAAO,WAAW,GAAG,GAAG;;;;;AAM1B,MAAa,eAAe;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAID,SAAgB,iBAAiB,EAC/B,MACA,eACsD;AACtD,KAAI,aAAa,WAAW,SAAS,CAAE,QAAO;AAC9C,KAAI,gBAAgB,kBAAmB,QAAO;AAC9C,KAAI,aAAa,SAAS,cAAc,CAAE,QAAO;AACjD,KAAI,aAAa,WAAW,SAAS,CAAE,QAAO;AAC9C,KAAI,aAAa,WAAW,SAAS,CAAE,QAAO;AAC9C,KAAI,aAAa,SAAS,MAAM,IAAI,aAAa,SAAS,UAAU,CAAE,QAAO;CAE7E,MAAM,MAAM,KAAK,MAAM,IAAI,CAAC,KAAK,EAAE,aAAa;AAChD,KAAI;EAAC;EAAO;EAAQ;EAAO;EAAO;EAAO;EAAO,CAAC,SAAS,OAAO,GAAG,CAAE,QAAO;AAC7E,KAAI;EAAC;EAAO;EAAO;EAAQ;EAAM,CAAC,SAAS,OAAO,GAAG,CAAE,QAAO;AAC9D,KAAI;EAAC;EAAO;EAAQ;EAAM,CAAC,SAAS,OAAO,GAAG,CAAE,QAAO;AACvD,KAAI;EAAC;EAAO;EAAO;EAAK,CAAC,SAAS,OAAO,GAAG,CAAE,QAAO;AAErD,QAAO;;;;;AAMT,SAAgB,YAAY,OAAe;CACzC,MAAM,QAAQ;EAAC;EAAK;EAAM;EAAM;EAAM;EAAM;EAAK;CAEjD,IAAI,OAAO;CACX,IAAI,YAAY;AAEhB,QAAO,QAAQ,QAAQ,YAAY,MAAM,SAAS,GAAG;AACnD,UAAQ;AACR;;AAGF,QAAO,GAAG,KAAK,QAAQ,EAAE,GAAG,MAAM"}
1
+ {"version":3,"file":"helpers.mjs","names":[],"sources":["../../../../src/modules/storage/lib/helpers.ts"],"sourcesContent":["import { ImageLoaderProps } from \"next/image\";\nimport { ImageVariant } from \"./constants\";\nimport { CreateNodeSchema, GetFileURLSchema, Node } from \"./validators\";\n\n/**\n * Check if the node is a file\n */\nexport function isFile(node: Pick<Node, \"type\">): node is Node {\n return node.type === \"file\";\n}\n\n/**\n * Check if the node is a folder\n */\nexport function isFolder(node: Pick<Node, \"type\">): node is Node {\n return node.type === \"folder\";\n}\n\n/**\n * Get file url\n */\nexport function getFileUrl(id: string, options: Partial<GetFileURLSchema> = {}) {\n const searchParams = new URLSearchParams();\n searchParams.set(\"id\", id);\n if (options?.variant) searchParams.set(\"variant\", options.variant);\n if (options?.disposition) searchParams.set(\"disposition\", options.disposition);\n\n return `/api/storage/files?${searchParams.toString()}`;\n}\n\n/**\n * Image loader\n */\nexport function imageLoader({ src, width }: ImageLoaderProps) {\n const url = new URL(src, window.location.origin);\n url.searchParams.set(\"variant\", `preview-${width}`);\n return url.toString();\n}\n\n/**\n * Generate object key\n */\nexport function getDriveBucketKey(id: string, variant: ImageVariant) {\n return `storage/${id}/${variant}`;\n}\n\n/**\n * Node subtype\n */\nexport const nodeSubtypes = [\n \"image\",\n \"document\",\n \"spreadsheet\",\n \"video\",\n \"audio\",\n \"archive\",\n \"other\",\n] as const;\n\nexport type NodeSubtype = (typeof nodeSubtypes)[number];\n\nexport function inferNodeSubtype({\n name,\n contentType,\n}: Pick<CreateNodeSchema, \"name\" | \"contentType\">): NodeSubtype {\n if (!contentType) return \"other\";\n if (contentType?.startsWith(\"image/\")) return \"image\";\n if (contentType === \"application/pdf\") return \"document\";\n if (contentType?.includes(\"spreadsheet\")) return \"spreadsheet\";\n if (contentType?.startsWith(\"video/\")) return \"video\";\n if (contentType?.startsWith(\"audio/\")) return \"audio\";\n if (contentType?.includes(\"zip\") || contentType?.includes(\"archive\")) return \"archive\";\n\n const ext = name.split(\".\").pop()?.toLowerCase();\n if ([\"jpg\", \"jpeg\", \"png\", \"gif\", \"bmp\", \"webp\"].includes(ext ?? \"\")) return \"image\";\n if ([\"pdf\", \"doc\", \"docx\", \"txt\"].includes(ext ?? \"\")) return \"document\";\n if ([\"xls\", \"xlsx\", \"ods\"].includes(ext ?? \"\")) return \"spreadsheet\";\n if ([\"zip\", \"rar\", \"7z\"].includes(ext ?? \"\")) return \"archive\";\n\n return \"other\";\n}\n\n/**\n * Render bytes\n */\nexport function renderBytes(bytes: number) {\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"];\n\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(2)}${units[unitIndex]}`;\n}\n"],"mappings":";;;;AAOA,SAAgB,OAAO,MAAwC;AAC7D,QAAO,KAAK,SAAS;;;;;AAMvB,SAAgB,SAAS,MAAwC;AAC/D,QAAO,KAAK,SAAS;;;;;AAMvB,SAAgB,WAAW,IAAY,UAAqC,EAAE,EAAE;CAC9E,MAAM,eAAe,IAAI,iBAAiB;AAC1C,cAAa,IAAI,MAAM,GAAG;AAC1B,KAAI,SAAS,QAAS,cAAa,IAAI,WAAW,QAAQ,QAAQ;AAClE,KAAI,SAAS,YAAa,cAAa,IAAI,eAAe,QAAQ,YAAY;AAE9E,QAAO,sBAAsB,aAAa,UAAU;;;;;AAMtD,SAAgB,YAAY,EAAE,KAAK,SAA2B;CAC5D,MAAM,MAAM,IAAI,IAAI,KAAK,OAAO,SAAS,OAAO;AAChD,KAAI,aAAa,IAAI,WAAW,WAAW,QAAQ;AACnD,QAAO,IAAI,UAAU;;;;;AAMvB,SAAgB,kBAAkB,IAAY,SAAuB;AACnE,QAAO,WAAW,GAAG,GAAG;;;;;AAM1B,MAAa,eAAe;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAID,SAAgB,iBAAiB,EAC/B,MACA,eAC8D;AAC9D,KAAI,CAAC,YAAa,QAAO;AACzB,KAAI,aAAa,WAAW,SAAS,CAAE,QAAO;AAC9C,KAAI,gBAAgB,kBAAmB,QAAO;AAC9C,KAAI,aAAa,SAAS,cAAc,CAAE,QAAO;AACjD,KAAI,aAAa,WAAW,SAAS,CAAE,QAAO;AAC9C,KAAI,aAAa,WAAW,SAAS,CAAE,QAAO;AAC9C,KAAI,aAAa,SAAS,MAAM,IAAI,aAAa,SAAS,UAAU,CAAE,QAAO;CAE7E,MAAM,MAAM,KAAK,MAAM,IAAI,CAAC,KAAK,EAAE,aAAa;AAChD,KAAI;EAAC;EAAO;EAAQ;EAAO;EAAO;EAAO;EAAO,CAAC,SAAS,OAAO,GAAG,CAAE,QAAO;AAC7E,KAAI;EAAC;EAAO;EAAO;EAAQ;EAAM,CAAC,SAAS,OAAO,GAAG,CAAE,QAAO;AAC9D,KAAI;EAAC;EAAO;EAAQ;EAAM,CAAC,SAAS,OAAO,GAAG,CAAE,QAAO;AACvD,KAAI;EAAC;EAAO;EAAO;EAAK,CAAC,SAAS,OAAO,GAAG,CAAE,QAAO;AAErD,QAAO;;;;;AAMT,SAAgB,YAAY,OAAe;CACzC,MAAM,QAAQ;EAAC;EAAK;EAAM;EAAM;EAAM;EAAM;EAAK;CAEjD,IAAI,OAAO;CACX,IAAI,YAAY;AAEhB,QAAO,QAAQ,QAAQ,YAAY,MAAM,SAAS,GAAG;AACnD,UAAQ;AACR;;AAGF,QAAO,GAAG,KAAK,QAAQ,EAAE,GAAG,MAAM"}