@tulip-systems/core 0.6.1 → 0.8.0
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.
- package/LICENSE +662 -0
- package/dist/auth/server.d.mts +3 -3
- package/dist/auth/server.mjs +3 -3
- package/dist/components/client.d.mts +19 -3
- package/dist/components/client.mjs +18 -2
- package/dist/components/editor/components/editor.client.d.mts +4 -3
- package/dist/components/editor/components/editor.client.d.mts.map +1 -1
- package/dist/components/editor/components/editor.client.mjs +5 -2
- package/dist/components/editor/components/editor.client.mjs.map +1 -1
- package/dist/components/editor/components/menu-nodes.client.mjs +1 -1
- package/dist/components/editor/components/menu-nodes.client.mjs.map +1 -1
- package/dist/components/editor/components/menu.client.mjs +3 -3
- package/dist/components/editor/components/menu.client.mjs.map +1 -1
- package/dist/components/editor/extensions/file-handler/extension.d.mts +4 -4
- package/dist/components/editor/extensions/file-handler/extension.d.mts.map +1 -1
- package/dist/components/editor/extensions/file-handler/extension.mjs.map +1 -1
- package/dist/components/editor/extensions/file-handler/strategy.d.mts +4 -6
- package/dist/components/editor/extensions/file-handler/strategy.d.mts.map +1 -1
- package/dist/components/editor/extensions/file-handler/strategy.mjs +11 -11
- package/dist/components/editor/extensions/file-handler/strategy.mjs.map +1 -1
- package/dist/components/editor/extensions/file-handler/utils.mjs +1 -1
- package/dist/components/editor/extensions/file-handler/utils.mjs.map +1 -1
- package/dist/components/editor/extensions/image/extension.mjs +9 -9
- package/dist/components/editor/extensions/image/extension.mjs.map +1 -1
- package/dist/components/editor/lib/constants.d.mts +1 -1
- package/dist/components/editor/lib/constants.mjs +1 -1
- package/dist/components/editor/lib/extensions.d.mts +1 -1
- package/dist/components/editor/lib/helpers.d.mts +11 -3
- package/dist/components/editor/lib/helpers.d.mts.map +1 -1
- package/dist/components/editor/lib/helpers.mjs +27 -13
- package/dist/components/editor/lib/helpers.mjs.map +1 -1
- package/dist/components/emails/forget-password-email.mjs +6 -6
- package/dist/components/emails/forget-password-email.mjs.map +1 -1
- package/dist/components/header/back-button.client.mjs +2 -2
- package/dist/components/header/back-button.client.mjs.map +1 -1
- package/dist/components/header/bottom-bar.client.mjs +2 -2
- package/dist/components/header/bottom-bar.client.mjs.map +1 -1
- package/dist/components/header/breadcrumbs.client.mjs +1 -1
- package/dist/components/header/breadcrumbs.client.mjs.map +1 -1
- package/dist/components/header/header.client.mjs +1 -1
- package/dist/components/header/header.client.mjs.map +1 -1
- package/dist/components/layouts/empty-page.mjs +2 -2
- package/dist/components/layouts/empty-page.mjs.map +1 -1
- package/dist/components/layouts/error-page.mjs +3 -3
- package/dist/components/layouts/error-page.mjs.map +1 -1
- package/dist/components/layouts/providers.client.d.mts.map +1 -1
- package/dist/components/layouts/providers.client.mjs +3 -2
- package/dist/components/layouts/providers.client.mjs.map +1 -1
- package/dist/components/layouts/root-loading.mjs +2 -2
- package/dist/components/layouts/root-loading.mjs.map +1 -1
- package/dist/components/layouts/tab-layout.mjs +1 -1
- package/dist/components/layouts/tab-layout.mjs.map +1 -1
- package/dist/components/lists/data-list.mjs +1 -1
- package/dist/components/lists/data-list.mjs.map +1 -1
- package/dist/components/lists/data-stack.mjs +1 -1
- package/dist/components/lists/data-stack.mjs.map +1 -1
- package/dist/components/navigation/admin-sidebar-header.client.mjs +3 -3
- package/dist/components/navigation/admin-sidebar-header.client.mjs.map +1 -1
- package/dist/components/navigation/admin-sidebar-skeleton.mjs +1 -1
- package/dist/components/navigation/admin-sidebar-skeleton.mjs.map +1 -1
- package/dist/components/ui/accordion.client.d.mts +26 -0
- package/dist/components/ui/accordion.client.d.mts.map +1 -0
- package/dist/components/ui/accordion.client.mjs +58 -0
- package/dist/components/ui/accordion.client.mjs.map +1 -0
- package/dist/components/ui/alert-dialog.client.d.mts +61 -0
- package/dist/components/ui/alert-dialog.client.d.mts.map +1 -0
- package/dist/components/ui/alert-dialog.client.mjs +104 -0
- package/dist/components/ui/alert-dialog.client.mjs.map +1 -0
- package/dist/components/ui/alert.d.mts +5 -1
- package/dist/components/ui/alert.d.mts.map +1 -1
- package/dist/components/ui/alert.mjs +13 -6
- package/dist/components/ui/alert.mjs.map +1 -1
- package/dist/components/ui/aspect-ratio.client.d.mts +10 -0
- package/dist/components/ui/aspect-ratio.client.d.mts.map +1 -0
- package/dist/components/ui/aspect-ratio.client.mjs +16 -0
- package/dist/components/ui/aspect-ratio.client.mjs.map +1 -0
- package/dist/components/ui/avatar.client.d.mts +21 -6
- package/dist/components/ui/avatar.client.d.mts.map +1 -1
- package/dist/components/ui/avatar.client.mjs +31 -9
- package/dist/components/ui/avatar.client.mjs.map +1 -1
- package/dist/components/ui/badge.d.mts +1 -1
- package/dist/components/ui/badge.d.mts.map +1 -1
- package/dist/components/ui/badge.mjs +11 -8
- package/dist/components/ui/badge.mjs.map +1 -1
- package/dist/components/ui/breadcrumb.d.mts +3 -1
- package/dist/components/ui/breadcrumb.d.mts.map +1 -1
- package/dist/components/ui/breadcrumb.mjs +7 -7
- package/dist/components/ui/breadcrumb.mjs.map +1 -1
- package/dist/components/ui/button-group.d.mts +29 -0
- package/dist/components/ui/button-group.d.mts.map +1 -0
- package/dist/components/ui/button-group.mjs +41 -0
- package/dist/components/ui/button-group.mjs.map +1 -0
- package/dist/components/ui/button.d.mts +2 -2
- package/dist/components/ui/button.d.mts.map +1 -1
- package/dist/components/ui/button.mjs +19 -13
- package/dist/components/ui/button.mjs.map +1 -1
- package/dist/components/ui/{calendar.d.mts → calendar.client.d.mts} +7 -4
- package/dist/components/ui/calendar.client.d.mts.map +1 -0
- package/dist/components/ui/{calendar.mjs → calendar.client.mjs} +22 -19
- package/dist/components/ui/calendar.client.mjs.map +1 -0
- package/dist/components/ui/card.d.mts +9 -2
- package/dist/components/ui/card.d.mts.map +1 -1
- package/dist/components/ui/card.mjs +15 -7
- package/dist/components/ui/card.mjs.map +1 -1
- package/dist/components/ui/{carousel.d.mts → carousel.client.d.mts} +12 -3
- package/dist/components/ui/carousel.client.d.mts.map +1 -0
- package/dist/components/ui/{carousel.mjs → carousel.client.mjs} +10 -10
- package/dist/components/ui/carousel.client.mjs.map +1 -0
- package/dist/components/ui/chart.client.d.mts.map +1 -1
- package/dist/components/ui/chart.client.mjs +11 -11
- package/dist/components/ui/chart.client.mjs.map +1 -1
- package/dist/components/ui/checkbox.client.d.mts +12 -0
- package/dist/components/ui/checkbox.client.d.mts.map +1 -0
- package/dist/components/ui/checkbox.client.mjs +24 -0
- package/dist/components/ui/checkbox.client.mjs.map +1 -0
- package/dist/components/ui/collapsible.client.d.mts +6 -6
- package/dist/components/ui/collapsible.client.d.mts.map +1 -1
- package/dist/components/ui/collapsible.client.mjs +6 -6
- package/dist/components/ui/collapsible.client.mjs.map +1 -1
- package/dist/components/ui/combobox-dropdown.client.d.mts +1 -1
- package/dist/components/ui/combobox-dropdown.client.mjs +3 -2
- package/dist/components/ui/combobox-dropdown.client.mjs.map +1 -1
- package/dist/components/ui/combobox.client.mjs +5 -5
- package/dist/components/ui/combobox.client.mjs.map +1 -1
- package/dist/components/ui/{command.d.mts → command.client.d.mts} +8 -2
- package/dist/components/ui/command.client.d.mts.map +1 -0
- package/dist/components/ui/command.client.mjs +91 -0
- package/dist/components/ui/command.client.mjs.map +1 -0
- package/dist/components/ui/context-menu.client.d.mts +86 -0
- package/dist/components/ui/context-menu.client.d.mts.map +1 -0
- package/dist/components/ui/context-menu.client.mjs +128 -0
- package/dist/components/ui/context-menu.client.mjs.map +1 -0
- package/dist/components/ui/date-picker.client.mjs +2 -2
- package/dist/components/ui/date-picker.client.mjs.map +1 -1
- package/dist/components/ui/dialog.client.d.mts +19 -12
- package/dist/components/ui/dialog.client.d.mts.map +1 -1
- package/dist/components/ui/dialog.client.mjs +40 -29
- package/dist/components/ui/dialog.client.mjs.map +1 -1
- package/dist/components/ui/drawer.client.d.mts.map +1 -1
- package/dist/components/ui/drawer.client.mjs +5 -5
- package/dist/components/ui/drawer.client.mjs.map +1 -1
- package/dist/components/ui/dropdown-menu.client.d.mts +85 -0
- package/dist/components/ui/dropdown-menu.client.d.mts.map +1 -0
- package/dist/components/ui/dropdown-menu.client.mjs +131 -0
- package/dist/components/ui/dropdown-menu.client.mjs.map +1 -0
- package/dist/components/ui/empty.d.mts +36 -0
- package/dist/components/ui/empty.d.mts.map +1 -0
- package/dist/components/ui/empty.mjs +62 -0
- package/dist/components/ui/empty.mjs.map +1 -0
- package/dist/components/ui/field.client.d.mts +65 -0
- package/dist/components/ui/field.client.d.mts.map +1 -0
- package/dist/components/ui/field.client.mjs +114 -0
- package/dist/components/ui/field.client.mjs.map +1 -0
- package/dist/components/ui/form.client.d.mts +3 -4
- package/dist/components/ui/form.client.d.mts.map +1 -1
- package/dist/components/ui/form.client.mjs +4 -4
- package/dist/components/ui/form.client.mjs.map +1 -1
- package/dist/components/ui/hover-card.client.d.mts +6 -6
- package/dist/components/ui/hover-card.client.d.mts.map +1 -1
- package/dist/components/ui/hover-card.client.mjs +14 -11
- package/dist/components/ui/hover-card.client.mjs.map +1 -1
- package/dist/components/ui/{input-date-time.d.mts → input-date-time.client.d.mts} +3 -3
- package/dist/components/ui/input-date-time.client.d.mts.map +1 -0
- package/dist/components/ui/{input-date-time.mjs → input-date-time.client.mjs} +4 -2
- package/dist/components/ui/input-date-time.client.mjs.map +1 -0
- package/dist/components/ui/input-date.d.mts +1 -1
- package/dist/components/ui/input-date.d.mts.map +1 -1
- package/dist/components/ui/input-date.mjs.map +1 -1
- package/dist/components/ui/input-group.client.d.mts +59 -0
- package/dist/components/ui/input-group.client.d.mts.map +1 -0
- package/dist/components/ui/input-group.client.mjs +106 -0
- package/dist/components/ui/input-group.client.mjs.map +1 -0
- package/dist/components/ui/input-recipient.mjs +11 -11
- package/dist/components/ui/input-recipient.mjs.map +1 -1
- package/dist/components/ui/input-time.d.mts +1 -1
- package/dist/components/ui/input-time.d.mts.map +1 -1
- package/dist/components/ui/input-time.mjs.map +1 -1
- package/dist/components/ui/input.d.mts.map +1 -1
- package/dist/components/ui/input.mjs +1 -1
- package/dist/components/ui/input.mjs.map +1 -1
- package/dist/components/ui/item.d.mts +63 -0
- package/dist/components/ui/item.d.mts.map +1 -0
- package/dist/components/ui/item.mjs +119 -0
- package/dist/components/ui/item.mjs.map +1 -0
- package/dist/components/ui/kbd.d.mts +14 -0
- package/dist/components/ui/kbd.d.mts.map +1 -0
- package/dist/components/ui/kbd.mjs +22 -0
- package/dist/components/ui/kbd.mjs.map +1 -0
- package/dist/components/ui/label.d.mts +4 -4
- package/dist/components/ui/label.d.mts.map +1 -1
- package/dist/components/ui/label.mjs +5 -5
- package/dist/components/ui/label.mjs.map +1 -1
- package/dist/components/ui/loader.d.mts +7 -0
- package/dist/components/ui/loader.d.mts.map +1 -0
- package/dist/components/ui/loader.mjs +63 -0
- package/dist/components/ui/loader.mjs.map +1 -0
- package/dist/components/ui/navigation-menu.d.mts +11 -11
- package/dist/components/ui/navigation-menu.d.mts.map +1 -1
- package/dist/components/ui/navigation-menu.mjs +18 -20
- package/dist/components/ui/navigation-menu.mjs.map +1 -1
- package/dist/components/ui/pagination.d.mts +8 -2
- package/dist/components/ui/pagination.d.mts.map +1 -1
- package/dist/components/ui/pagination.mjs +29 -21
- package/dist/components/ui/pagination.mjs.map +1 -1
- package/dist/components/ui/popover.client.d.mts +35 -0
- package/dist/components/ui/popover.client.d.mts.map +1 -0
- package/dist/components/ui/popover.client.mjs +59 -0
- package/dist/components/ui/popover.client.mjs.map +1 -0
- package/dist/components/ui/progress.client.d.mts +4 -4
- package/dist/components/ui/progress.client.d.mts.map +1 -1
- package/dist/components/ui/progress.client.mjs +7 -7
- package/dist/components/ui/progress.client.mjs.map +1 -1
- package/dist/components/ui/radio-group.d.mts +5 -5
- package/dist/components/ui/radio-group.d.mts.map +1 -1
- package/dist/components/ui/radio-group.mjs +10 -11
- package/dist/components/ui/radio-group.mjs.map +1 -1
- package/dist/components/ui/resizable.client.d.mts +3 -4
- package/dist/components/ui/resizable.client.d.mts.map +1 -1
- package/dist/components/ui/resizable.client.mjs +5 -9
- package/dist/components/ui/resizable.client.mjs.map +1 -1
- package/dist/components/ui/scroll-area.d.mts +5 -5
- package/dist/components/ui/scroll-area.d.mts.map +1 -1
- package/dist/components/ui/scroll-area.mjs +12 -11
- package/dist/components/ui/scroll-area.mjs.map +1 -1
- package/dist/components/ui/select.client.d.mts +18 -13
- package/dist/components/ui/select.client.d.mts.map +1 -1
- package/dist/components/ui/select.client.mjs +37 -32
- package/dist/components/ui/select.client.mjs.map +1 -1
- package/dist/components/ui/separator.d.mts +4 -4
- package/dist/components/ui/separator.d.mts.map +1 -1
- package/dist/components/ui/separator.mjs +6 -6
- package/dist/components/ui/separator.mjs.map +1 -1
- package/dist/components/ui/sheet.client.d.mts +9 -7
- package/dist/components/ui/sheet.client.d.mts.map +1 -1
- package/dist/components/ui/sheet.client.mjs +30 -20
- package/dist/components/ui/sheet.client.mjs.map +1 -1
- package/dist/components/ui/sidebar.client.mjs +28 -28
- package/dist/components/ui/sidebar.client.mjs.map +1 -1
- package/dist/components/ui/skeleton.mjs +1 -1
- package/dist/components/ui/skeleton.mjs.map +1 -1
- package/dist/components/ui/slider.d.mts +4 -4
- package/dist/components/ui/slider.d.mts.map +1 -1
- package/dist/components/ui/slider.mjs +11 -11
- package/dist/components/ui/slider.mjs.map +1 -1
- package/dist/components/ui/spinner.d.mts +10 -0
- package/dist/components/ui/spinner.d.mts.map +1 -0
- package/dist/components/ui/spinner.mjs +9 -55
- package/dist/components/ui/spinner.mjs.map +1 -1
- package/dist/components/ui/switch.client.d.mts +15 -0
- package/dist/components/ui/switch.client.d.mts.map +1 -0
- package/dist/components/ui/switch.client.mjs +23 -0
- package/dist/components/ui/switch.client.mjs.map +1 -0
- package/dist/components/ui/tabs.client.d.mts +31 -0
- package/dist/components/ui/tabs.client.d.mts.map +1 -0
- package/dist/components/ui/tabs.client.mjs +49 -0
- package/dist/components/ui/tabs.client.mjs.map +1 -0
- package/dist/components/ui/textarea.d.mts.map +1 -1
- package/dist/components/ui/textarea.mjs +1 -1
- package/dist/components/ui/textarea.mjs.map +1 -1
- package/dist/components/ui/toggle-group.client.d.mts +11 -6
- package/dist/components/ui/toggle-group.client.d.mts.map +1 -1
- package/dist/components/ui/toggle-group.client.mjs +20 -12
- package/dist/components/ui/toggle-group.client.mjs.map +1 -1
- package/dist/components/ui/{toggle.d.mts → toggle.client.d.mts} +6 -6
- package/dist/components/ui/toggle.client.d.mts.map +1 -0
- package/dist/components/ui/toggle.client.mjs +40 -0
- package/dist/components/ui/toggle.client.mjs.map +1 -0
- package/dist/components/ui/tooltip.client.d.mts +7 -7
- package/dist/components/ui/tooltip.client.d.mts.map +1 -1
- package/dist/components/ui/tooltip.client.mjs +10 -10
- package/dist/components/ui/tooltip.client.mjs.map +1 -1
- package/dist/components.d.mts +13 -21
- package/dist/components.mjs +13 -21
- package/dist/config/server.d.mts +1 -3
- package/dist/config/server.mjs +1 -4
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +1 -1
- package/dist/data-tables/client.d.mts +2 -1
- package/dist/data-tables/client.mjs +2 -1
- package/dist/data-tables.d.mts +1 -1
- package/dist/database/client.d.mts +1 -0
- package/dist/database/client.mjs +1 -0
- package/dist/database/server.d.mts +2 -0
- package/dist/database/server.mjs +3 -0
- package/dist/database.d.mts +3 -0
- package/dist/database.mjs +3 -0
- package/dist/emails/client.d.mts +1 -0
- package/dist/emails/client.mjs +1 -0
- package/dist/emails/server.d.mts +2 -0
- package/dist/emails/server.mjs +3 -0
- package/dist/emails.d.mts +1 -0
- package/dist/emails.mjs +1 -0
- package/dist/inline-edit/client.d.mts +2 -2
- package/dist/lib/hooks/use-indicator.d.mts.map +1 -1
- package/dist/lib/hooks/use-indicator.mjs.map +1 -1
- package/dist/lib/utils/markdown.d.mts +10 -0
- package/dist/lib/utils/markdown.d.mts.map +1 -0
- package/dist/lib/utils/markdown.mjs +15 -0
- package/dist/lib/utils/markdown.mjs.map +1 -0
- package/dist/lib/utils/url.mjs +2 -1
- package/dist/lib/utils/url.mjs.map +1 -1
- package/dist/lib/utils/user-agent.mjs +15 -0
- package/dist/lib/utils/user-agent.mjs.map +1 -1
- package/dist/lib.d.mts +2 -2
- package/dist/lib.mjs +2 -2
- package/dist/modules/auth/components/auth-layout.server.mjs +2 -2
- package/dist/modules/auth/components/auth-layout.server.mjs.map +1 -1
- package/dist/modules/auth/components/create-first-user-guard.server.d.mts +16 -0
- package/dist/modules/auth/components/create-first-user-guard.server.d.mts.map +1 -0
- package/dist/modules/auth/components/create-first-user-guard.server.mjs +16 -0
- package/dist/modules/auth/components/create-first-user-guard.server.mjs.map +1 -0
- package/dist/modules/auth/components/create-first-user-page.client.mjs +3 -3
- package/dist/modules/auth/components/create-first-user-page.client.mjs.map +1 -1
- package/dist/modules/auth/components/forget-password-page.client.mjs +1 -1
- package/dist/modules/auth/components/forget-password-page.client.mjs.map +1 -1
- package/dist/modules/auth/components/guard.server.d.mts +2 -2
- package/dist/modules/auth/components/guard.server.mjs +1 -1
- package/dist/modules/auth/components/guard.server.mjs.map +1 -1
- package/dist/modules/auth/components/login-page.client.mjs +5 -5
- package/dist/modules/auth/components/login-page.client.mjs.map +1 -1
- package/dist/modules/auth/components/reset-password-page.client.mjs +5 -5
- package/dist/modules/auth/components/reset-password-page.client.mjs.map +1 -1
- package/dist/modules/auth/db/schema.d.mts +1 -1
- package/dist/modules/auth/db/schema.mjs +2 -2
- package/dist/modules/auth/handler/create-client.client.d.mts +4838 -229
- package/dist/modules/auth/handler/create-client.client.d.mts.map +1 -1
- package/dist/modules/auth/handler/create-client.client.mjs.map +1 -1
- package/dist/modules/auth/handler/proxy.server.mjs +2 -2
- package/dist/modules/auth/handler/proxy.server.mjs.map +1 -1
- package/dist/modules/auth/handler/route.server.d.mts +2 -2
- package/dist/modules/auth/handler/route.server.d.mts.map +1 -1
- package/dist/modules/auth/handler/route.server.mjs.map +1 -1
- package/dist/modules/auth/handler/{init.d.mts → service.server.d.mts} +322 -90
- package/dist/modules/auth/handler/service.server.d.mts.map +1 -0
- package/dist/modules/auth/handler/{init.mjs → service.server.mjs} +19 -8
- package/dist/modules/auth/handler/service.server.mjs.map +1 -0
- package/dist/modules/auth/hooks/use-session.d.mts +9 -4
- package/dist/modules/auth/hooks/use-session.d.mts.map +1 -1
- package/dist/modules/auth/lib/helpers.server.d.mts +1 -1
- package/dist/modules/auth/lib/permissions.d.mts +1 -1
- package/dist/modules/auth/lib/validators.mjs +1 -1
- package/dist/modules/commands/components/alert-dialog-command.client.d.mts +9 -9
- package/dist/modules/commands/components/alert-dialog-command.client.d.mts.map +1 -1
- package/dist/modules/commands/components/alert-dialog-command.client.mjs +3 -3
- package/dist/modules/commands/components/alert-dialog-command.client.mjs.map +1 -1
- package/dist/modules/commands/components/click-command.client.mjs +2 -2
- package/dist/modules/commands/components/click-command.client.mjs.map +1 -1
- package/dist/modules/commands/components/dialog-command.client.d.mts +8 -8
- package/dist/modules/commands/components/dialog-command.client.d.mts.map +1 -1
- package/dist/modules/commands/components/dialog-command.client.mjs +2 -2
- package/dist/modules/commands/components/dialog-command.client.mjs.map +1 -1
- package/dist/modules/commands/components/dropdown-command.client.mjs +2 -2
- package/dist/modules/commands/components/dropdown-command.client.mjs.map +1 -1
- package/dist/modules/commands/components/empty-command.client.mjs +2 -2
- package/dist/modules/commands/components/empty-command.client.mjs.map +1 -1
- package/dist/modules/commands/components/form-dialog-command.client.d.mts +11 -9
- package/dist/modules/commands/components/form-dialog-command.client.d.mts.map +1 -1
- package/dist/modules/commands/components/form-dialog-command.client.mjs +8 -6
- package/dist/modules/commands/components/form-dialog-command.client.mjs.map +1 -1
- package/dist/modules/commands/menus/context-menu.client.d.mts +3 -3
- package/dist/modules/commands/menus/context-menu.client.d.mts.map +1 -1
- package/dist/modules/commands/menus/context-menu.client.mjs +1 -1
- package/dist/modules/commands/menus/context-menu.client.mjs.map +1 -1
- package/dist/modules/commands/menus/dropdown-menu.client.mjs +2 -2
- package/dist/modules/commands/menus/dropdown-menu.client.mjs.map +1 -1
- package/dist/modules/commands/menus/floating-menu.client.mjs +2 -2
- package/dist/modules/commands/menus/floating-menu.client.mjs.map +1 -1
- package/dist/modules/commands/utils/archive-command.client.mjs +1 -1
- package/dist/modules/commands/utils/archive-command.client.mjs.map +1 -1
- package/dist/modules/commands/utils/delete-command.client.mjs +1 -1
- package/dist/modules/commands/utils/delete-command.client.mjs.map +1 -1
- package/dist/modules/config/lib/context.d.mts +9 -10
- package/dist/modules/config/lib/context.d.mts.map +1 -1
- package/dist/modules/config/lib/context.mjs.map +1 -1
- package/dist/modules/data-tables/components/cell/select.client.mjs +1 -1
- package/dist/modules/data-tables/components/cell/select.client.mjs.map +1 -1
- package/dist/modules/data-tables/components/column-header.mjs +5 -5
- package/dist/modules/data-tables/components/column-header.mjs.map +1 -1
- package/dist/modules/data-tables/components/filters/combobox.client.mjs +6 -6
- package/dist/modules/data-tables/components/filters/combobox.client.mjs.map +1 -1
- package/dist/modules/data-tables/components/filters/slider.client.mjs +2 -2
- package/dist/modules/data-tables/components/filters/slider.client.mjs.map +1 -1
- package/dist/modules/data-tables/components/header.mjs +1 -1
- package/dist/modules/data-tables/components/header.mjs.map +1 -1
- package/dist/modules/data-tables/components/skeleton.mjs +2 -2
- package/dist/modules/data-tables/components/skeleton.mjs.map +1 -1
- package/dist/modules/data-tables/components/table.mjs +3 -3
- package/dist/modules/data-tables/components/table.mjs.map +1 -1
- package/dist/modules/data-tables/components/toolbar.mjs +1 -1
- package/dist/modules/data-tables/components/toolbar.mjs.map +1 -1
- package/dist/modules/data-tables/lib/converters/search.d.mts +1 -1
- package/dist/modules/data-tables/lib/converters/sorting.d.mts +1 -1
- package/dist/modules/data-tables/server/get-data.server.d.mts +3 -3
- package/dist/modules/data-tables/server/get-data.server.mjs +1 -1
- package/dist/modules/data-tables/server/get-data.server.mjs.map +1 -1
- package/dist/modules/data-tables/strategies/infinite/strategy.d.mts +1 -1
- package/dist/modules/data-tables/strategies/infinite/strategy.mjs +3 -0
- package/dist/modules/data-tables/strategies/infinite/strategy.mjs.map +1 -1
- package/dist/modules/data-tables/strategies/pagination/components.mjs +3 -3
- package/dist/modules/data-tables/strategies/pagination/components.mjs.map +1 -1
- package/dist/modules/data-tables/tables/data-table/components/row.mjs +5 -15
- package/dist/modules/data-tables/tables/data-table/components/row.mjs.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/body.mjs +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/body.mjs.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/cells/common.mjs +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/cells/common.mjs.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.mjs +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.mjs.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/cells/read-only.mjs +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/cells/read-only.mjs.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/row.client.mjs +13 -23
- package/dist/modules/data-tables/tables/inline-table/components/row.client.mjs.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/table.d.mts +1 -0
- package/dist/modules/data-tables/tables/inline-table/components/table.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/components/table.mjs +2 -1
- package/dist/modules/data-tables/tables/inline-table/components/table.mjs.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts +5 -1
- package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/hooks/context.client.mjs +2 -1
- package/dist/modules/data-tables/tables/inline-table/hooks/context.client.mjs.map +1 -1
- package/dist/modules/data-tables/tables/inline-table/hooks/use-hotkeys.client.d.mts +30 -0
- package/dist/modules/data-tables/tables/inline-table/hooks/use-hotkeys.client.d.mts.map +1 -0
- package/dist/modules/data-tables/tables/inline-table/hooks/use-hotkeys.client.mjs +77 -9
- package/dist/modules/data-tables/tables/inline-table/hooks/use-hotkeys.client.mjs.map +1 -1
- package/dist/modules/{config/db → database/lib}/helpers.d.mts +2 -2
- package/dist/modules/database/lib/helpers.d.mts.map +1 -0
- package/dist/modules/{config/db → database/lib}/helpers.mjs +1 -1
- package/dist/modules/database/lib/helpers.mjs.map +1 -0
- package/dist/modules/database/lib/service.server.d.mts +34 -0
- package/dist/modules/database/lib/service.server.d.mts.map +1 -0
- package/dist/modules/database/lib/service.server.mjs +24 -0
- package/dist/modules/database/lib/service.server.mjs.map +1 -0
- package/dist/modules/{config/db → database/lib}/types.d.mts +1 -1
- package/dist/modules/database/lib/types.d.mts.map +1 -0
- package/dist/modules/emails/lib/service.server.d.mts +29 -0
- package/dist/modules/emails/lib/service.server.d.mts.map +1 -0
- package/dist/modules/emails/lib/service.server.mjs +21 -0
- package/dist/modules/emails/lib/service.server.mjs.map +1 -0
- package/dist/modules/inline-edit/components/date-input.client.mjs +4 -4
- package/dist/modules/inline-edit/components/date-input.client.mjs.map +1 -1
- package/dist/modules/inline-edit/components/date-picker.client.mjs +3 -2
- package/dist/modules/inline-edit/components/date-picker.client.mjs.map +1 -1
- package/dist/modules/inline-edit/components/date-time.client.d.mts +1 -1
- package/dist/modules/inline-edit/components/date-time.client.mjs +2 -1
- package/dist/modules/inline-edit/components/date-time.client.mjs.map +1 -1
- package/dist/modules/inline-edit/components/editor.client.mjs +1 -0
- package/dist/modules/inline-edit/components/editor.client.mjs.map +1 -1
- package/dist/modules/inline-edit/components/input-recipient.client.mjs +1 -0
- package/dist/modules/inline-edit/components/input-recipient.client.mjs.map +1 -1
- package/dist/modules/inline-edit/components/input-toggle.client.mjs +3 -2
- package/dist/modules/inline-edit/components/input-toggle.client.mjs.map +1 -1
- package/dist/modules/inline-edit/components/input.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/input.client.mjs +6 -3
- package/dist/modules/inline-edit/components/input.client.mjs.map +1 -1
- package/dist/modules/inline-edit/components/select.client.d.mts +8 -11
- package/dist/modules/inline-edit/components/select.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/select.client.mjs +1 -0
- package/dist/modules/inline-edit/components/select.client.mjs.map +1 -1
- package/dist/modules/inline-edit/components/switch.client.d.mts +3 -2
- package/dist/modules/inline-edit/components/switch.client.d.mts.map +1 -1
- package/dist/modules/inline-edit/components/switch.client.mjs +2 -1
- package/dist/modules/inline-edit/components/switch.client.mjs.map +1 -1
- package/dist/modules/inline-edit/components/toggle.client.mjs +2 -1
- package/dist/modules/inline-edit/components/toggle.client.mjs.map +1 -1
- package/dist/modules/inline-edit/lib/variants.d.mts +1 -1
- package/dist/modules/inline-edit/lib/variants.mjs +1 -1
- package/dist/modules/inline-edit/lib/variants.mjs.map +1 -1
- package/dist/modules/router/handler/context.server.d.mts +12 -10
- package/dist/modules/router/handler/context.server.d.mts.map +1 -1
- package/dist/modules/router/handler/init.server.d.mts +13 -11
- package/dist/modules/router/handler/init.server.d.mts.map +1 -1
- package/dist/modules/router/handler/init.server.mjs +2 -2
- package/dist/modules/router/handler/init.server.mjs.map +1 -1
- package/dist/modules/router/handler/route.server.d.mts +1 -1
- package/dist/modules/storage/components/dropzone.client.d.mts +2 -2
- package/dist/modules/storage/components/dropzone.client.d.mts.map +1 -1
- package/dist/modules/storage/components/dropzone.client.mjs +8 -8
- package/dist/modules/storage/components/dropzone.client.mjs.map +1 -1
- package/dist/modules/storage/components/image-grid.client.d.mts +3 -3
- package/dist/modules/storage/components/image-grid.client.d.mts.map +1 -1
- package/dist/modules/storage/components/image-grid.client.mjs +24 -26
- package/dist/modules/storage/components/image-grid.client.mjs.map +1 -1
- package/dist/modules/storage/components/image.client.d.mts +8 -0
- package/dist/modules/storage/components/image.client.d.mts.map +1 -0
- package/dist/modules/storage/components/image.client.mjs +17 -0
- package/dist/modules/storage/components/image.client.mjs.map +1 -0
- package/dist/modules/storage/components/upload-button.client.d.mts +12 -0
- package/dist/modules/storage/components/upload-button.client.d.mts.map +1 -0
- package/dist/modules/storage/components/upload-button.client.mjs +34 -0
- package/dist/modules/storage/components/upload-button.client.mjs.map +1 -0
- package/dist/modules/storage/components/upload-zone-context.client.d.mts +5 -5
- package/dist/modules/storage/components/upload-zone-context.client.d.mts.map +1 -1
- package/dist/modules/storage/components/upload-zone-context.client.mjs +2 -2
- package/dist/modules/storage/components/upload-zone-context.client.mjs.map +1 -1
- package/dist/modules/storage/components/upload-zone.client.d.mts +4 -4
- package/dist/modules/storage/components/upload-zone.client.d.mts.map +1 -1
- package/dist/modules/storage/components/upload-zone.client.mjs +17 -10
- package/dist/modules/storage/components/upload-zone.client.mjs.map +1 -1
- package/dist/modules/storage/lib/constants.d.mts +1 -5
- package/dist/modules/storage/lib/constants.d.mts.map +1 -1
- package/dist/modules/storage/lib/constants.mjs +1 -13
- package/dist/modules/storage/lib/constants.mjs.map +1 -1
- package/dist/modules/storage/lib/helpers.d.mts +14 -28
- package/dist/modules/storage/lib/helpers.d.mts.map +1 -1
- package/dist/modules/storage/lib/helpers.mjs +17 -75
- package/dist/modules/storage/lib/helpers.mjs.map +1 -1
- package/dist/modules/storage/lib/procedures.server.d.mts +1991 -0
- package/dist/modules/{auth/handler/init.d.mts.map → storage/lib/procedures.server.d.mts.map} +1 -1
- package/dist/modules/storage/lib/procedures.server.mjs +22 -0
- package/dist/modules/storage/lib/procedures.server.mjs.map +1 -0
- package/dist/modules/storage/lib/router-handlers.server.d.mts +41 -0
- package/dist/modules/storage/lib/router-handlers.server.d.mts.map +1 -0
- package/dist/modules/storage/lib/router-handlers.server.mjs +124 -0
- package/dist/modules/storage/lib/router-handlers.server.mjs.map +1 -0
- package/dist/modules/storage/lib/schema.d.mts +68 -958
- package/dist/modules/storage/lib/schema.d.mts.map +1 -1
- package/dist/modules/storage/lib/schema.mjs +28 -65
- package/dist/modules/storage/lib/schema.mjs.map +1 -1
- package/dist/modules/storage/lib/service.server.d.mts +2155 -141
- package/dist/modules/storage/lib/service.server.d.mts.map +1 -1
- package/dist/modules/storage/lib/service.server.mjs +453 -242
- package/dist/modules/storage/lib/service.server.mjs.map +1 -1
- package/dist/modules/storage/lib/upload.client.d.mts +58 -0
- package/dist/modules/storage/lib/upload.client.d.mts.map +1 -0
- package/dist/modules/storage/lib/upload.client.mjs +87 -0
- package/dist/modules/storage/lib/upload.client.mjs.map +1 -0
- package/dist/modules/storage/lib/validators.d.mts +297 -835
- package/dist/modules/storage/lib/validators.d.mts.map +1 -1
- package/dist/modules/storage/lib/validators.mjs +32 -76
- package/dist/modules/storage/lib/validators.mjs.map +1 -1
- package/dist/modules/storage/providers/adapters/s3.server.d.mts +19 -0
- package/dist/modules/storage/providers/adapters/s3.server.d.mts.map +1 -0
- package/dist/modules/storage/providers/adapters/s3.server.mjs +173 -0
- package/dist/modules/storage/providers/adapters/s3.server.mjs.map +1 -0
- package/dist/modules/storage/providers/lib/constants.d.mts +6 -0
- package/dist/modules/storage/providers/lib/constants.d.mts.map +1 -0
- package/dist/modules/storage/providers/lib/constants.mjs +6 -0
- package/dist/modules/storage/providers/lib/constants.mjs.map +1 -0
- package/dist/modules/storage/providers/lib/errors.d.mts +12 -0
- package/dist/modules/storage/providers/lib/errors.d.mts.map +1 -0
- package/dist/modules/storage/providers/lib/errors.mjs +13 -0
- package/dist/modules/storage/providers/lib/errors.mjs.map +1 -0
- package/dist/modules/storage/providers/lib/types.d.mts +21 -0
- package/dist/modules/storage/providers/lib/types.d.mts.map +1 -0
- package/dist/modules/storage/providers/lib/validators.d.mts +112 -0
- package/dist/modules/storage/providers/lib/validators.d.mts.map +1 -0
- package/dist/modules/storage/providers/lib/validators.mjs +75 -0
- package/dist/modules/storage/providers/lib/validators.mjs.map +1 -0
- package/dist/router/server.d.mts +1 -1
- package/dist/storage/client.d.mts +4 -2
- package/dist/storage/client.mjs +4 -2
- package/dist/storage/server.d.mts +5 -4
- package/dist/storage/server.mjs +5 -4
- package/dist/storage.d.mts +9 -6
- package/dist/storage.mjs +8 -6
- package/package.json +20 -32
- package/src/components/editor/components/editor.client.tsx +9 -1
- package/src/components/editor/components/menu-nodes.client.tsx +1 -1
- package/src/components/editor/components/menu.client.tsx +3 -3
- package/src/components/editor/extensions/file-handler/extension.ts +4 -4
- package/src/components/editor/extensions/file-handler/strategy.ts +15 -40
- package/src/components/editor/extensions/file-handler/utils.ts +1 -1
- package/src/components/editor/extensions/image/extension.ts +10 -10
- package/src/components/editor/lib/helpers.ts +28 -11
- package/src/components/emails/forget-password-email.tsx +6 -6
- package/src/components/entry.client.ts +16 -0
- package/src/components/entry.ts +6 -14
- package/src/components/header/back-button.client.tsx +2 -2
- package/src/components/header/bottom-bar.client.tsx +2 -2
- package/src/components/header/breadcrumbs.client.tsx +1 -1
- package/src/components/header/header.client.tsx +1 -1
- package/src/components/layouts/empty-page.tsx +2 -2
- package/src/components/layouts/error-page.tsx +3 -3
- package/src/components/layouts/providers.client.tsx +8 -5
- package/src/components/layouts/root-loading.tsx +2 -2
- package/src/components/layouts/tab-layout.tsx +1 -1
- package/src/components/lists/data-list.tsx +1 -1
- package/src/components/lists/data-stack.tsx +1 -1
- package/src/components/navigation/admin-sidebar-header.client.tsx +3 -3
- package/src/components/navigation/admin-sidebar-skeleton.tsx +1 -1
- package/src/components/ui/accordion.client.tsx +83 -0
- package/src/components/ui/alert-dialog.client.tsx +181 -0
- package/src/components/ui/alert.tsx +15 -7
- package/src/components/ui/aspect-ratio.client.tsx +9 -0
- package/src/components/ui/avatar.client.tsx +61 -6
- package/src/components/ui/badge.tsx +17 -11
- package/src/components/ui/breadcrumb.tsx +9 -10
- package/src/components/ui/button-group.tsx +78 -0
- package/src/components/ui/button.tsx +26 -16
- package/src/components/ui/{calendar.tsx → calendar.client.tsx} +27 -20
- package/src/components/ui/card.tsx +41 -8
- package/src/components/ui/{carousel.tsx → carousel.client.tsx} +12 -12
- package/src/components/ui/chart.client.tsx +105 -103
- package/src/components/ui/checkbox.client.tsx +28 -0
- package/src/components/ui/collapsible.client.tsx +2 -2
- package/src/components/ui/combobox-dropdown.client.tsx +3 -2
- package/src/components/ui/combobox.client.tsx +7 -7
- package/src/components/ui/command.client.tsx +179 -0
- package/src/components/ui/{context-menu.tsx → context-menu.client.tsx} +86 -64
- package/src/components/ui/date-picker.client.tsx +2 -2
- package/src/components/ui/dialog.client.tsx +43 -20
- package/src/components/ui/drawer.client.tsx +13 -15
- package/src/components/ui/{dropdown-menu.tsx → dropdown-menu.client.tsx} +45 -24
- package/src/components/ui/empty.tsx +93 -0
- package/src/components/ui/field.client.tsx +224 -0
- package/src/components/ui/form.client.tsx +4 -4
- package/src/components/ui/hover-card.client.tsx +14 -13
- package/src/components/ui/{input-date-time.tsx → input-date-time.client.tsx} +6 -1
- package/src/components/ui/input-date.tsx +1 -1
- package/src/components/ui/input-group.client.tsx +191 -0
- package/src/components/ui/input-recipient.tsx +12 -12
- package/src/components/ui/input-time.tsx +1 -1
- package/src/components/ui/input.tsx +2 -4
- package/src/components/ui/item.tsx +182 -0
- package/src/components/ui/kbd.tsx +26 -0
- package/src/components/ui/label.tsx +2 -3
- package/src/components/ui/loader.tsx +51 -0
- package/src/components/ui/navigation-menu.tsx +14 -17
- package/src/components/ui/pagination.tsx +31 -26
- package/src/components/ui/popover.client.tsx +73 -0
- package/src/components/ui/progress.client.tsx +6 -4
- package/src/components/ui/radio-group.tsx +5 -7
- package/src/components/ui/resizable.client.tsx +9 -19
- package/src/components/ui/scroll-area.tsx +5 -7
- package/src/components/ui/select.client.tsx +40 -22
- package/src/components/ui/separator.tsx +3 -4
- package/src/components/ui/sheet.client.tsx +19 -18
- package/src/components/ui/sidebar.client.tsx +27 -27
- package/src/components/ui/skeleton.tsx +1 -1
- package/src/components/ui/slider.tsx +5 -10
- package/src/components/ui/spinner.tsx +12 -48
- package/src/components/ui/switch.client.tsx +32 -0
- package/src/components/ui/tabs.client.tsx +79 -0
- package/src/components/ui/textarea.tsx +1 -2
- package/src/components/ui/toggle-group.client.tsx +26 -10
- package/src/components/ui/toggle.client.tsx +44 -0
- package/src/components/ui/tooltip.client.tsx +4 -9
- package/src/entry.ts +12 -51
- package/src/lib/entry.ts +1 -5
- package/src/lib/hooks/use-indicator.tsx +4 -7
- package/src/lib/utils/markdown.ts +10 -0
- package/src/lib/utils/url.ts +2 -1
- package/src/lib/utils/user-agent.ts +15 -0
- package/src/modules/auth/components/auth-layout.server.tsx +2 -2
- package/src/modules/auth/components/{guard-first-user.server.tsx → create-first-user-guard.server.tsx} +8 -8
- package/src/modules/auth/components/create-first-user-page.client.tsx +4 -4
- package/src/modules/auth/components/forget-password-page.client.tsx +1 -1
- package/src/modules/auth/components/guard.server.tsx +1 -1
- package/src/modules/auth/components/login-page.client.tsx +6 -6
- package/src/modules/auth/components/reset-password-page.client.tsx +7 -7
- package/src/modules/auth/entry.server.ts +4 -5
- package/src/modules/auth/handler/create-client.client.ts +2 -2
- package/src/modules/auth/handler/proxy.server.ts +1 -1
- package/src/modules/auth/handler/route.server.ts +2 -2
- package/src/modules/auth/handler/{init.ts → service.server.ts} +30 -9
- package/src/modules/commands/components/alert-dialog-command.client.tsx +20 -16
- package/src/modules/commands/components/click-command.client.tsx +2 -2
- package/src/modules/commands/components/dialog-command.client.tsx +11 -10
- package/src/modules/commands/components/dropdown-command.client.tsx +2 -2
- package/src/modules/commands/components/empty-command.client.tsx +2 -2
- package/src/modules/commands/components/form-dialog-command.client.tsx +29 -13
- package/src/modules/commands/menus/context-menu.client.tsx +3 -4
- package/src/modules/commands/menus/dropdown-menu.client.tsx +2 -2
- package/src/modules/commands/menus/floating-menu.client.tsx +2 -2
- package/src/modules/commands/utils/archive-command.client.tsx +1 -1
- package/src/modules/commands/utils/delete-command.client.tsx +1 -1
- package/src/modules/config/entry.server.ts +0 -9
- package/src/modules/config/entry.ts +2 -2
- package/src/modules/config/lib/context.ts +9 -9
- package/src/modules/data-tables/components/cell/select.client.tsx +1 -1
- package/src/modules/data-tables/components/column-header.tsx +5 -5
- package/src/modules/data-tables/components/filters/combobox.client.tsx +6 -6
- package/src/modules/data-tables/components/filters/slider.client.tsx +2 -2
- package/src/modules/data-tables/components/header.tsx +1 -1
- package/src/modules/data-tables/components/skeleton.tsx +2 -2
- package/src/modules/data-tables/components/table.tsx +3 -3
- package/src/modules/data-tables/components/toolbar.tsx +1 -1
- package/src/modules/data-tables/entry.client.ts +1 -0
- package/src/modules/data-tables/server/get-data.server.ts +1 -1
- package/src/modules/data-tables/strategies/infinite/strategy.ts +4 -1
- package/src/modules/data-tables/strategies/pagination/components.tsx +3 -3
- package/src/modules/data-tables/tables/data-table/components/row.tsx +12 -21
- package/src/modules/data-tables/tables/inline-table/components/body.tsx +1 -1
- package/src/modules/data-tables/tables/inline-table/components/cells/common.tsx +1 -1
- package/src/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.tsx +1 -1
- package/src/modules/data-tables/tables/inline-table/components/cells/read-only.tsx +1 -1
- package/src/modules/data-tables/tables/inline-table/components/row.client.tsx +24 -30
- package/src/modules/data-tables/tables/inline-table/components/table.tsx +6 -1
- package/src/modules/data-tables/tables/inline-table/hooks/context.client.tsx +5 -0
- package/src/modules/data-tables/tables/inline-table/hooks/use-hotkeys.client.ts +119 -91
- package/src/modules/database/entry.client.ts +0 -0
- package/src/modules/database/entry.server.ts +4 -0
- package/src/modules/database/entry.ts +5 -0
- package/src/modules/database/lib/service.server.ts +33 -0
- package/src/modules/emails/entry.client.ts +0 -0
- package/src/modules/emails/entry.server.ts +4 -0
- package/src/modules/emails/entry.ts +0 -0
- package/src/modules/emails/lib/service.server.ts +29 -0
- package/src/modules/inline-edit/components/date-input.client.tsx +4 -4
- package/src/modules/inline-edit/components/date-picker.client.tsx +3 -2
- package/src/modules/inline-edit/components/date-time.client.tsx +2 -1
- package/src/modules/inline-edit/components/editor.client.tsx +3 -0
- package/src/modules/inline-edit/components/input-recipient.client.tsx +1 -0
- package/src/modules/inline-edit/components/input-toggle.client.tsx +3 -2
- package/src/modules/inline-edit/components/input.client.tsx +6 -3
- package/src/modules/inline-edit/components/select.client.tsx +11 -21
- package/src/modules/inline-edit/components/switch.client.tsx +4 -3
- package/src/modules/inline-edit/components/toggle.client.tsx +2 -1
- package/src/modules/inline-edit/lib/variants.ts +1 -1
- package/src/modules/router/handler/init.server.ts +2 -2
- package/src/modules/storage/components/dropzone.client.tsx +9 -9
- package/src/modules/storage/components/image-grid.client.tsx +28 -25
- package/src/modules/storage/components/image.client.tsx +8 -0
- package/src/modules/storage/components/upload-zone-context.client.tsx +11 -8
- package/src/modules/storage/components/upload-zone.client.tsx +23 -17
- package/src/modules/storage/entry.client.ts +3 -1
- package/src/modules/storage/entry.server.ts +9 -3
- package/src/modules/storage/entry.ts +13 -1
- package/src/modules/storage/lib/constants.ts +0 -11
- package/src/modules/storage/lib/helpers.ts +18 -65
- package/src/modules/storage/lib/procedures.server.ts +60 -0
- package/src/modules/storage/lib/router-handlers.server.ts +178 -0
- package/src/modules/storage/lib/schema.ts +26 -97
- package/src/modules/storage/lib/service.server.ts +636 -374
- package/src/modules/storage/lib/upload.client.ts +156 -0
- package/src/modules/storage/lib/validators.ts +50 -111
- package/src/modules/storage/providers/adapters/s3.server.ts +281 -0
- package/src/modules/storage/providers/lib/constants.ts +3 -0
- package/src/modules/storage/providers/lib/errors.ts +21 -0
- package/src/modules/storage/providers/lib/types.ts +28 -0
- package/src/modules/storage/providers/lib/validators.ts +122 -0
- package/src/styles.css +1 -3
- package/dist/components/ui/accordion.d.mts +0 -25
- package/dist/components/ui/accordion.d.mts.map +0 -1
- package/dist/components/ui/accordion.mjs +0 -46
- package/dist/components/ui/accordion.mjs.map +0 -1
- package/dist/components/ui/alert-dialog.d.mts +0 -43
- package/dist/components/ui/alert-dialog.d.mts.map +0 -1
- package/dist/components/ui/alert-dialog.mjs +0 -84
- package/dist/components/ui/alert-dialog.mjs.map +0 -1
- package/dist/components/ui/aspect-ratio.d.mts +0 -8
- package/dist/components/ui/aspect-ratio.d.mts.map +0 -1
- package/dist/components/ui/aspect-ratio.mjs +0 -16
- package/dist/components/ui/aspect-ratio.mjs.map +0 -1
- package/dist/components/ui/calendar.d.mts.map +0 -1
- package/dist/components/ui/calendar.mjs.map +0 -1
- package/dist/components/ui/carousel.d.mts.map +0 -1
- package/dist/components/ui/carousel.mjs.map +0 -1
- package/dist/components/ui/checkbox.d.mts +0 -12
- package/dist/components/ui/checkbox.d.mts.map +0 -1
- package/dist/components/ui/checkbox.mjs +0 -24
- package/dist/components/ui/checkbox.mjs.map +0 -1
- package/dist/components/ui/command.d.mts.map +0 -1
- package/dist/components/ui/command.mjs +0 -88
- package/dist/components/ui/command.mjs.map +0 -1
- package/dist/components/ui/context-menu.d.mts +0 -77
- package/dist/components/ui/context-menu.d.mts.map +0 -1
- package/dist/components/ui/context-menu.mjs +0 -125
- package/dist/components/ui/context-menu.mjs.map +0 -1
- package/dist/components/ui/dropdown-menu.d.mts +0 -78
- package/dist/components/ui/dropdown-menu.d.mts.map +0 -1
- package/dist/components/ui/dropdown-menu.mjs +0 -126
- package/dist/components/ui/dropdown-menu.mjs.map +0 -1
- package/dist/components/ui/input-date-time.d.mts.map +0 -1
- package/dist/components/ui/input-date-time.mjs.map +0 -1
- package/dist/components/ui/popover.d.mts +0 -23
- package/dist/components/ui/popover.d.mts.map +0 -1
- package/dist/components/ui/popover.mjs +0 -38
- package/dist/components/ui/popover.mjs.map +0 -1
- package/dist/components/ui/switch.d.mts +0 -12
- package/dist/components/ui/switch.d.mts.map +0 -1
- package/dist/components/ui/switch.mjs +0 -22
- package/dist/components/ui/switch.mjs.map +0 -1
- package/dist/components/ui/tabs.d.mts +0 -24
- package/dist/components/ui/tabs.d.mts.map +0 -1
- package/dist/components/ui/tabs.mjs +0 -39
- package/dist/components/ui/tabs.mjs.map +0 -1
- package/dist/components/ui/toggle.d.mts.map +0 -1
- package/dist/components/ui/toggle.mjs +0 -40
- package/dist/components/ui/toggle.mjs.map +0 -1
- package/dist/lib/config/constants.d.mts +0 -5
- package/dist/lib/config/constants.d.mts.map +0 -1
- package/dist/lib/config/constants.mjs +0 -6
- package/dist/lib/config/constants.mjs.map +0 -1
- package/dist/modules/auth/components/guard-first-user.server.d.mts +0 -18
- package/dist/modules/auth/components/guard-first-user.server.d.mts.map +0 -1
- package/dist/modules/auth/components/guard-first-user.server.mjs +0 -16
- package/dist/modules/auth/components/guard-first-user.server.mjs.map +0 -1
- package/dist/modules/auth/handler/init.mjs.map +0 -1
- package/dist/modules/config/db/helpers.d.mts.map +0 -1
- package/dist/modules/config/db/helpers.mjs.map +0 -1
- package/dist/modules/config/db/init.d.mts +0 -20
- package/dist/modules/config/db/init.d.mts.map +0 -1
- package/dist/modules/config/db/init.mjs +0 -15
- package/dist/modules/config/db/init.mjs.map +0 -1
- package/dist/modules/config/db/types.d.mts.map +0 -1
- package/dist/modules/config/providers/email.d.mts +0 -12
- package/dist/modules/config/providers/email.d.mts.map +0 -1
- package/dist/modules/config/providers/email.mjs +0 -11
- package/dist/modules/config/providers/email.mjs.map +0 -1
- package/dist/modules/storage/config/filters.d.mts +0 -17
- package/dist/modules/storage/config/filters.d.mts.map +0 -1
- package/dist/modules/storage/config/filters.mjs +0 -17
- package/dist/modules/storage/config/filters.mjs.map +0 -1
- package/dist/modules/storage/lib/create-client.server.d.mts +0 -11
- package/dist/modules/storage/lib/create-client.server.d.mts.map +0 -1
- package/dist/modules/storage/lib/create-client.server.mjs +0 -11
- package/dist/modules/storage/lib/create-client.server.mjs.map +0 -1
- package/dist/modules/storage/lib/create-upload.client.d.mts +0 -56
- package/dist/modules/storage/lib/create-upload.client.d.mts.map +0 -1
- package/dist/modules/storage/lib/create-upload.client.mjs +0 -98
- package/dist/modules/storage/lib/create-upload.client.mjs.map +0 -1
- package/dist/modules/storage/lib/proxy.server.d.mts +0 -21
- package/dist/modules/storage/lib/proxy.server.d.mts.map +0 -1
- package/dist/modules/storage/lib/proxy.server.mjs +0 -46
- package/dist/modules/storage/lib/proxy.server.mjs.map +0 -1
- package/dist/modules/storage/lib/router.server.d.mts +0 -31002
- package/dist/modules/storage/lib/router.server.d.mts.map +0 -1
- package/dist/modules/storage/lib/router.server.mjs +0 -86
- package/dist/modules/storage/lib/router.server.mjs.map +0 -1
- package/src/components/ui/accordion.tsx +0 -63
- package/src/components/ui/alert-dialog.tsx +0 -133
- package/src/components/ui/aspect-ratio.tsx +0 -9
- package/src/components/ui/checkbox.tsx +0 -29
- package/src/components/ui/command.tsx +0 -154
- package/src/components/ui/popover.tsx +0 -42
- package/src/components/ui/switch.tsx +0 -28
- package/src/components/ui/tabs.tsx +0 -54
- package/src/components/ui/toggle.tsx +0 -46
- package/src/lib/config/constants.ts +0 -1
- package/src/lib/utils/time-picker.ts +0 -139
- package/src/modules/config/db/init.ts +0 -21
- package/src/modules/config/providers/email.ts +0 -13
- package/src/modules/storage/config/filters.ts +0 -12
- package/src/modules/storage/lib/create-client.server.ts +0 -14
- package/src/modules/storage/lib/create-upload.client.ts +0 -134
- package/src/modules/storage/lib/proxy.server.ts +0 -63
- package/src/modules/storage/lib/router.server.ts +0 -182
- /package/src/modules/{config/db → database/lib}/helpers.ts +0 -0
- /package/src/modules/{config/db → database/lib}/types.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.server.mjs","names":["#db","#blob","#createGetCommand","#putObject","#createPutCommand"],"sources":["../../../../src/modules/storage/lib/service.server.ts"],"sourcesContent":["import {\n DeleteObjectsCommand,\n GetObjectCommand,\n PutObjectCommand,\n S3Client,\n type S3ClientConfig,\n} from \"@aws-sdk/client-s3\";\nimport { getSignedUrl } from \"@aws-sdk/s3-request-presigner\";\nimport { addSeconds } from \"date-fns\";\nimport { and, asc, eq, inArray, isNotNull, isNull, type SQL } from \"drizzle-orm\";\nimport { after } from \"next/server\";\nimport { BUCKET_NAME } from \"@/lib/config/constants\";\nimport { generateDefaultUUID, type TDatabaseSchema } from \"@/modules/config/entry\";\nimport type { DatabaseClient } from \"@/modules/config/entry.server\";\nimport {\n convertOrderByToQueryParams,\n convertSearchToQueryParams,\n} from \"@/modules/data-tables/entry.server\";\nimport type { BulkActionSchema } from \"@/modules/router/entry\";\nimport { ServerError } from \"@/modules/router/lib/error.server\";\nimport { deviceSizes } from \"./constants\";\nimport { getDriveBucketKey, inferNodeSubtype, isFile, isFolder } from \"./helpers\";\nimport { nodePresignedUrls, nodes, nodeVariants } from \"./schema\";\nimport {\n type CreateFolderNodeSchema,\n type FileNode,\n type GetFileURLSchema,\n type GetNodesByParentIdInput,\n type GetObjectInput,\n getFileURLSchemaDefaults,\n getObjectSchema,\n type Node,\n type PresignFileSchema,\n type PutObjectInput,\n putObjectSchema,\n type UpdateNodeSchema,\n type UploadFileSchema,\n} from \"./validators\";\n\n/**\n * Storage Service Config\n */\nexport type StorageServiceConfig<TSchema extends TDatabaseSchema> = {\n db: DatabaseClient<TSchema>;\n config: S3ClientConfig;\n};\n\n/**\n * Storage Service\n */\nexport class StorageService<TSchema extends TDatabaseSchema> {\n /**\n * S3 Client\n */\n #blob: S3Client;\n #db: DatabaseClient<TSchema>;\n\n /**\n * Constructor\n */\n constructor({ db, config }: StorageServiceConfig<TSchema>) {\n this.#db = db;\n this.#blob = new S3Client(config);\n }\n\n /**\n * Get Blob\n */\n blob() {\n return this.#blob;\n }\n\n /**\n * Create get command\n */\n #createGetCommand(props: GetObjectInput) {\n const input = getObjectSchema.parse(props);\n\n return new GetObjectCommand({\n Bucket: BUCKET_NAME,\n Key: getDriveBucketKey(input.id, input.variant),\n ResponseContentDisposition: input.disposition,\n });\n }\n\n /**\n * Get object\n */\n async getObject(id: string, options: GetFileURLSchema = getFileURLSchemaDefaults) {\n const getCommand = this.#createGetCommand({ ...options, id });\n\n return await this.#blob.send(getCommand);\n }\n\n /**\n * Create put command\n */\n #createPutCommand(props: PutObjectInput) {\n const input = putObjectSchema.parse(props);\n\n return new PutObjectCommand({\n Bucket: BUCKET_NAME,\n Key: getDriveBucketKey(input.id, input.variant),\n Body: input.body,\n ContentType: input.contentType ?? undefined,\n ContentLength: input.size ?? undefined,\n Metadata: {\n nodeId: input.id,\n },\n });\n }\n\n /**\n * Put object\n */\n async #putObject(props: PutObjectInput) {\n const putCommand = this.#createPutCommand(props);\n return await this.#blob.send(putCommand);\n }\n\n /**\n * Get node by id\n */\n async getNodeById(id: string) {\n return this.#db.select().from(nodes).where(eq(nodes.id, id));\n }\n\n /**\n * Get nodes by parent id\n */\n async getNodesByParentId({ filters, ...query }: GetNodesByParentIdInput) {\n const orderBy = convertOrderByToQueryParams(query, nodes, asc(nodes.createdAt));\n const search = convertSearchToQueryParams(query, [nodes.name]);\n\n return this.#db\n .select()\n .from(nodes)\n .where(\n and(\n filters.nodeIds != null ? inArray(nodes.id, filters.nodeIds) : undefined,\n filters.types != null ? inArray(nodes.type, filters.types) : undefined,\n filters.isDeleted != null ? eq(nodes.isDeleted, filters.isDeleted) : undefined,\n filters.isOrphaned === true\n ? isNotNull(nodes.orphanedAt)\n : filters.isOrphaned === false\n ? isNull(nodes.orphanedAt)\n : undefined,\n filters.hidden != null ? eq(nodes.hidden, filters.hidden) : undefined,\n filters.parentId ? eq(nodes.parentId, filters.parentId) : isNull(nodes.parentId),\n eq(nodes.namespace, filters.namespace),\n search,\n ),\n )\n .orderBy(orderBy as SQL);\n }\n\n /**\n * Get file url\n */\n async getSignedURL(node: Node, options: GetFileURLSchema = getFileURLSchemaDefaults) {\n const [presignedUrl] = await this.#db\n .select({ url: nodePresignedUrls.url, expiresAt: nodePresignedUrls.expiresAt })\n .from(nodePresignedUrls)\n .where(\n and(\n eq(nodePresignedUrls.nodeId, node.id),\n eq(nodePresignedUrls.variant, options.variant),\n eq(nodePresignedUrls.disposition, options.disposition),\n ),\n );\n\n if (presignedUrl && presignedUrl.expiresAt > new Date()) return presignedUrl.url;\n\n const expiresIn = 3600 * 24;\n\n // Get the variants\n const variants = await this.#db\n .select()\n .from(nodeVariants)\n .where(eq(nodeVariants.nodeId, node.id));\n\n // If the requested variant does not exist, fallback to main\n const variantExists = variants.find((v) => v.variant === options.variant);\n const variant = variantExists ? options.variant : \"main\";\n\n console.info(\n `Generating new signed url for file: ${node.id} with variant: ${variant} and disposition: ${options.disposition}`,\n );\n\n // Generate the get command\n const getCommand = this.#createGetCommand({\n id: node.id,\n variant,\n disposition: `${options.disposition}; filename=\"${node.name}\"`,\n });\n\n // Generate the presigned url that expires in 24 hours\n const url = await getSignedUrl(this.#blob, getCommand, { expiresIn });\n\n // Add the presigned url to the database\n after(async () => {\n await this.#db\n .insert(nodePresignedUrls)\n .values({\n nodeId: node.id,\n url,\n variant,\n disposition: options.disposition,\n expiresAt: addSeconds(new Date(), expiresIn),\n })\n .onConflictDoUpdate({\n target: [\n nodePresignedUrls.nodeId,\n nodePresignedUrls.variant,\n nodePresignedUrls.disposition,\n ],\n set: { url, expiresAt: addSeconds(new Date(), expiresIn) },\n });\n });\n\n return url;\n }\n\n /**\n * Upload file to S3 and add it to the database\n **/\n async uploadFile(input: UploadFileSchema & Pick<PutObjectInput, \"body\">) {\n const id = input.id ?? generateDefaultUUID();\n\n return await this.#db.transaction(async (tx) => {\n const [result] = await tx\n .insert(nodes)\n .values({\n id,\n type: \"file\",\n name: input.name,\n namespace: input.namespace,\n parentId: input.parentId,\n size: input.size,\n contentType: input.contentType,\n mode: input.mode,\n subtype: inferNodeSubtype(input),\n })\n .returning();\n\n if (!result) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Oep! Er is iets fout gegaan\",\n });\n }\n\n await this.#putObject({\n id,\n body: input.body,\n variant: \"main\",\n name: input.name,\n contentType: input.contentType,\n size: input.size,\n });\n\n return result;\n });\n }\n\n /**\n * Presign a new upload\n */\n async presignUpload(input: PresignFileSchema) {\n // Generate the put command\n const putCommand = this.#createPutCommand({\n id: input.id,\n name: input.name,\n variant: \"main\",\n contentType: input.contentType,\n size: input.size,\n });\n\n // Generate the presigned url\n const presignedUrl = await getSignedUrl(this.#blob, putCommand, { expiresIn: 3600 });\n\n const [node] = await this.#db\n .insert(nodes)\n .values({\n ...input,\n subtype: inferNodeSubtype(input),\n isPending: true,\n type: \"file\",\n id: input.id,\n })\n .returning();\n\n if (!node) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Oep! Er is iets fout gegaan\",\n });\n }\n\n // Return the result\n return { id: input.id, presignedUrl, node };\n }\n\n /**\n * Confirm a new upload\n */\n async confirmUpload(input: { id: string }) {\n const [result] = await this.#db\n .update(nodes)\n .set({ isPending: false })\n .where(eq(nodes.id, input.id))\n .returning();\n\n if (!result) {\n throw new ServerError(\"NOT_FOUND\", { message: \"File not found\" });\n }\n\n /**\n * Generate the preview version of the file\n */\n // after(async () => {});\n await this.generatePreviews(input);\n\n return result as FileNode;\n }\n\n /**\n * Generate preview version of the file\n */\n async generatePreviews(input: { id: string }) {\n /**\n * Get the main version of the file\n */\n const getCommand = this.#createGetCommand({ id: input.id, variant: \"main\" });\n\n const response = await this.#blob.send(getCommand);\n const contentType = response.ContentType;\n if (!response.Body) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Oep! Er is iets fout gegaan\",\n });\n }\n\n /**\n * Transform the main version of the file to a buffer\n */\n const byteArray = await response.Body.transformToByteArray();\n const buffer = Buffer.from(byteArray);\n\n /**\n * Generate the preview versions for images\n */\n if (contentType?.startsWith(\"image/\")) {\n const sharp = await import(\"sharp\");\n\n // Generate the preview versions\n await Promise.allSettled(\n deviceSizes.flatMap(async (width) => {\n // Generate the preview\n const preview = await sharp.default(buffer).resize({ width }).webp().toBuffer();\n\n // Upload the preview and add the variant to the database\n return this.#db.transaction(async (tx) => {\n await this.#putObject({\n id: input.id,\n body: preview,\n variant: `preview-${width}`,\n contentType: \"image/webp\",\n size: preview.byteLength,\n });\n\n await tx.insert(nodeVariants).values({\n nodeId: input.id,\n variant: `preview-${width}`,\n width,\n });\n });\n }),\n );\n }\n }\n\n /**\n * Create a new folder\n */\n async createFolder(input: CreateFolderNodeSchema) {\n const [parent] = input.parentId\n ? await this.#db.select().from(nodes).where(eq(nodes.id, input.parentId))\n : [];\n\n /**\n * Validate\n */\n if (input.parentId && !parent) {\n throw new ServerError(\"BAD_REQUEST\", { message: \"Parent not found\" });\n }\n\n if (parent && !isFolder(parent)) {\n throw new ServerError(\"BAD_REQUEST\", { message: \"Parent is not a folder\" });\n }\n\n if (parent && parent.namespace !== input.namespace) {\n throw new ServerError(\"BAD_REQUEST\", {\n message: \"Parent is not in the same namespace\",\n });\n }\n\n /**\n * Create the folder\n */\n const [result] = await this.#db\n .insert(nodes)\n .values({ ...input, type: \"folder\" })\n .returning();\n\n if (!result) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Folder kon niet worden aangemaakt\",\n });\n }\n\n return result;\n }\n\n /**\n * Update a node\n */\n async updateNode(input: { id: string; data: UpdateNodeSchema }) {\n const [node] = await this.#db\n .select({ readonly: nodes.readonly })\n .from(nodes)\n .where(eq(nodes.id, input.id));\n\n if (node?.readonly) {\n throw new ServerError(\"BAD_REQUEST\", { message: \"Node is readonly\" });\n }\n\n const [result] = await this.#db\n .update(nodes)\n .set(input.data)\n .where(eq(nodes.id, input.id))\n .returning();\n\n if (!result) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Node kon niet worden gewijzigd\",\n });\n }\n\n return result;\n }\n\n /**\n * Delete nodes\n */\n async deleteNodes(input: BulkActionSchema) {\n const items = await this.#db\n .select({ id: nodes.id, type: nodes.type, readonly: nodes.readonly })\n .from(nodes)\n .where(inArray(nodes.id, input.ids));\n\n // Check if the nodes are readonly\n if (items.some((item) => item.readonly)) {\n throw new ServerError(\"BAD_REQUEST\", { message: \"Nodes are readonly\" });\n }\n\n // Split the nodes into folders and files\n const folders = items.filter(isFolder).map((folder) => folder.id);\n const files = items.filter(isFile).map((file) => file.id);\n\n // Delete command for S3\n const deleteCommand =\n files.length > 0\n ? new DeleteObjectsCommand({\n Bucket: BUCKET_NAME,\n Delete: {\n Objects: files.map((id) => ({ Key: id })),\n },\n })\n : undefined;\n\n /**\n * Delete files and folders in a transaction\n */\n await this.#db.transaction(async (tx) => {\n await tx.delete(nodes).where(inArray(nodes.id, folders));\n await tx.delete(nodes).where(inArray(nodes.id, files));\n\n if (deleteCommand) await this.#blob.send(deleteCommand);\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.server.mjs","names":["#db","#adapter","#generateKey","#parseError"],"sources":["../../../../src/modules/storage/lib/service.server.ts"],"sourcesContent":["import { and, desc, eq, inArray, isNotNull, isNull } from \"drizzle-orm\";\nimport z from \"zod\";\nimport { generateDefaultUUID } from \"@/modules/database/lib/helpers\";\nimport type { Database } from \"@/modules/database/lib/service.server\";\nimport type { TDatabaseSchema } from \"@/modules/database/lib/types\";\nimport { ServerError } from \"@/modules/router/lib/error.server\";\nimport { StorageAdapterError } from \"../providers/lib/errors\";\nimport type { StorageAdapter } from \"../providers/lib/types\";\nimport type { GetObjectURLOptions } from \"../providers/lib/validators\";\nimport { storageAssets } from \"./schema\";\nimport {\n type ConfirmUploadInput,\n confirmUploadInputSchema,\n type PresignUploadInput,\n presignUploadInputSchema,\n type UploadInput,\n uploadInputSchema,\n} from \"./validators\";\n\nexport type StorageConfig<TSchema extends TDatabaseSchema> = {\n db: Database<TSchema>;\n adapter: StorageAdapter;\n prefix?: string;\n};\n\n/**\n * Storage service for working with asset metadata and object storage.\n *\n * Use `Storage.init()` to create a fully configured instance in app code.\n *\n * @param props - Storage configuration, including `db` and `adapter`\n * @returns A ready-to-use `Storage` instance\n * @example\n * const storage = Storage.init({\n * db: drizzle(dbConnection),\n * adapter: new StorageS3Adapter({\n * bucketName: \"my-app-uploads\",\n * region: \"us-east-1\",\n * credentials: {\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n * },\n * }),\n * });\n */\n\nexport class Storage<TSchema extends TDatabaseSchema> {\n #adapter: StorageAdapter;\n #db: Database<TSchema>;\n prefix: string;\n\n private constructor({ db, adapter, prefix }: StorageConfig<TSchema>) {\n this.#db = db;\n this.#adapter = adapter;\n this.prefix = prefix ?? \"uploads\";\n }\n\n /**\n * Create a storage service instance.\n *\n * This keeps the public API aligned with other Tulip services such as\n * `Database.init()`, `Email.init()`, and `Auth.init()`.\n *\n * @param props - Storage configuration, including `db` and `adapter`\n * @returns A new `Storage` instance\n * @example\n * const storage = Storage.init({\n * db: drizzle(dbConnection),\n * adapter: new StorageS3Adapter({\n * bucketName: \"my-app-uploads\",\n * region: \"us-east-1\",\n * credentials: {\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n * },\n * }),\n * });\n */\n static init<TSchema extends TDatabaseSchema>(props: StorageConfig<TSchema>) {\n return new Storage(props);\n }\n\n /**\n * Generates the canonical object key for a storage asset id.\n *\n * This keeps bucket key structure internal to the storage service,\n * so callers only work with asset ids and do not manage object paths.\n *\n * @param input - Storage asset id (UUID)\n * @returns Canonical storage key (e.g. `uploads/<id>`)\n * @example\n * const key = this.#generateKey(\"019d0051-2c0d-741e-9e3c-e5a5bc4d16a2\");\n * // key => \"uploads/019d0051-2c0d-741e-9e3c-e5a5bc4d16a2\"\n */\n #generateKey(input: string) {\n const id = z.uuid().parse(input);\n return `${this.prefix}/${id}`;\n }\n\n /**\n * Builds a query to fetch a storage asset by its ID and the current adapter's provider.\n * @param id - The asset ID to search for\n * @returns A dynamic Drizzle query for fetching a single asset\n * @example\n * let query = storageService.getAssetByIdQuery(\"asset-123\");\n * query = query.where(eq(storageAssets.contentType, \"image/png\")); // Add additional conditions if needed\n * const [asset] = await query;\n */\n getAssetByIdQuery(id: string) {\n return this.#db\n .select()\n .from(storageAssets)\n .where(\n and(\n eq(storageAssets.id, id),\n eq(storageAssets.provider, this.#adapter.key),\n eq(storageAssets.bucket, this.#adapter.bucketName),\n isNull(storageAssets.deletedAt),\n ),\n )\n .limit(1)\n .$dynamic();\n }\n\n /**\n * Fetches a storage asset by its ID.\n * @param id - The asset ID to retrieve\n * @returns The asset object if found, otherwise null\n * @example\n * const asset = await storageService.getAssetById(\"asset-123\");\n * if (asset) {\n * console.log(asset.key, asset.contentType);\n * }\n */\n async getAssetById(id: string) {\n const parsedId = z.uuid().parse(id);\n const [asset] = await this.getAssetByIdQuery(parsedId);\n return asset ?? null;\n }\n\n /**\n * Builds a query to fetch a single ready storage asset by key\n * within the current adapter provider scope.\n *\n * Notes:\n * - Scoped to `this.#adapter.key` to avoid cross-provider leakage.\n * - Targets ready assets by default.\n * - Uniqueness is expected on `(provider, bucket, key)`.\n *\n * @param key - The object key stored in `storage_assets.key`\n * @returns A dynamic Drizzle query returning max 1 row\n * @example\n * let query = storageService.getAssetByKeyQuery(\"uploads/abc/main\");\n * query = query.leftJoin(otherTable, eq(otherTable.assetId, storageAssets.id));\n * const [asset] = await query;\n */\n getAssetByKeyQuery(key: string) {\n return this.#db\n .select()\n .from(storageAssets)\n .where(\n and(\n eq(storageAssets.key, key),\n eq(storageAssets.provider, this.#adapter.key),\n eq(storageAssets.bucket, this.#adapter.bucketName),\n eq(storageAssets.status, \"ready\"),\n isNull(storageAssets.deletedAt),\n ),\n )\n .limit(1)\n .$dynamic();\n }\n\n /**\n * Fetches a single ready storage asset by key.\n *\n * This is the convenience wrapper around `getAssetByKeyQuery`.\n * Use this when you only need the result, not query composition.\n *\n * @param key - Asset key to look up\n * @returns The matching asset or `null` if none is found\n * @example\n * const asset = await storageService.getAssetByKey(\"uploads/abc/main\");\n * if (!asset) return;\n */\n async getAssetByKey(key: string) {\n const [result] = await this.getAssetByKeyQuery(key);\n return result ?? null;\n }\n\n /**\n * Builds a base query for listing storage assets.\n *\n * Scope and defaults:\n * - Scoped to the current adapter provider (`this.#adapter.key`)\n * - Orders by newest first (`createdAt DESC`)\n *\n * This method returns a dynamic query so callers can extend it with\n * custom filters, joins, pagination, and limits.\n *\n * @returns A dynamic Drizzle query for listing assets\n * @example\n * const query = storageService\n * .listAssetsQuery()\n * .limit(50);\n * const assets = await query;\n */\n listAssetsQuery() {\n return this.#db\n .select()\n .from(storageAssets)\n .where(\n and(\n eq(storageAssets.provider, this.#adapter.key),\n eq(storageAssets.bucket, this.#adapter.bucketName),\n isNull(storageAssets.deletedAt),\n ),\n )\n .orderBy(desc(storageAssets.createdAt))\n .$dynamic();\n }\n\n /**\n * Lists storage assets using safe default pagination.\n *\n * This is the convenience wrapper around `listAssetsQuery`.\n * Use `listAssetsQuery()` directly when you need custom query composition.\n *\n * @returns Up to 100 storage assets sorted by newest first\n * @example\n * const assets = await storageService.listAssets();\n */\n async listAssets() {\n const assets = await this.listAssetsQuery().limit(100);\n return assets ?? [];\n }\n\n /**\n * Creates a pending storage asset record and generates a presigned upload URL.\n *\n * Flow:\n * - Validates the input payload\n * - Inserts a `pending` asset in the catalog (`storage_assets`)\n * - Requests a presigned PUT URL from the storage adapter\n * - Marks the asset as `error` if URL generation fails\n *\n * This method is intended for direct-to-storage browser uploads.\n * The upload should be finalized later via a confirm step.\n *\n * @param props - Presign upload input (contentType, size, metadata)\n * @returns The created asset id, key, and presigned URL\n * @throws {ServerError} If intent creation or URL generation fails\n * @example\n * const result = await storageService.presignUpload({\n * contentType: \"image/png\",\n * size: 120_000,\n * metadata: { uploadToken: crypto.randomUUID() },\n * });\n * // result => { id, uploadId, key, presignedUrl }\n */\n async presignUpload(props: PresignUploadInput) {\n const input = presignUploadInputSchema.parse(props);\n\n const id = generateDefaultUUID();\n const uploadId = input.uploadId ?? generateDefaultUUID();\n const key = this.#generateKey(id);\n\n const { contentType, size, metadata, name, visibility } = input;\n\n const [record] = await this.#db\n .insert(storageAssets)\n .values({\n id,\n uploadId,\n key,\n size,\n contentType,\n name,\n visibility,\n provider: this.#adapter.key,\n bucket: this.#adapter.bucketName,\n status: \"pending\",\n metadata,\n })\n .returning();\n\n if (!record) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to create upload intent\",\n });\n }\n\n try {\n const presignedUrl = await this.#adapter.putObjectURL({ key, contentType, size, metadata });\n\n return { ...record, presignedUrl };\n } catch (error) {\n await this.#db\n .update(storageAssets)\n .set({ status: \"error\" })\n .where(eq(storageAssets.id, record.id));\n\n throw this.#parseError(error, {\n fallbackMessage: \"Failed to generate upload URL\",\n });\n }\n }\n\n /**\n * Confirms a direct-to-storage upload by verifying object existence\n * and transitioning the asset from `pending` to `ready`.\n *\n * Flow:\n * - Validates confirm input\n * - Loads the asset record scoped to the current adapter provider\n * - Returns early if asset is already `ready` (idempotent behavior)\n * - Verifies object existence/metadata via `adapter.headObject`\n * - Marks asset as `error` if verification fails\n * - Updates status to `ready` and stores upload metadata\n *\n * @param props - Confirm upload payload containing the asset uploadId\n * @returns The updated storage asset record\n * @throws {ServerError} If the asset is missing, verification fails, or update fails\n * @example\n * const asset = await storageService.confirmUpload(\"019d0051-2c0d-741e-9e3c-e5a5bc4d16a2\");\n * // asset.status === \"ready\"\n */\n async confirmUpload(props: ConfirmUploadInput) {\n const uploadId = confirmUploadInputSchema.parse(props);\n\n const [record] = await this.#db\n .select()\n .from(storageAssets)\n .where(\n and(\n eq(storageAssets.uploadId, uploadId),\n eq(storageAssets.provider, this.#adapter.key),\n eq(storageAssets.bucket, this.#adapter.bucketName),\n isNull(storageAssets.deletedAt),\n ),\n )\n .limit(1);\n\n if (!record) {\n throw new ServerError(\"NOT_FOUND\", { message: \"Storage asset not found\" });\n }\n\n if (record.status === \"ready\") return record;\n\n let head: Awaited<ReturnType<StorageAdapter[\"headObject\"]>>;\n\n try {\n head = await this.#adapter.headObject({ key: record.key });\n } catch (error) {\n await this.#db\n .update(storageAssets)\n .set({ status: \"error\" })\n .where(eq(storageAssets.id, record.id));\n\n throw this.#parseError(error, {\n fallbackMessage: \"Failed to verify uploaded object\",\n });\n }\n\n const [updated] = await this.#db\n .update(storageAssets)\n .set({\n status: \"ready\",\n uploadedAt: new Date(),\n size: head.size ?? record.size,\n contentType: head.contentType ?? record.contentType,\n })\n .where(and(eq(storageAssets.id, record.id), eq(storageAssets.status, \"pending\")))\n .returning();\n\n if (!updated) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", { message: \"Failed to confirm upload\" });\n }\n\n return updated;\n }\n\n /**\n * Uploads an asset directly from the server and persists its catalog record.\n *\n * Flow:\n * - Validates upload input\n * - Creates a `pending` asset record in `storage_assets`\n * - Uploads the object bytes through the storage adapter\n * - Marks the record as `ready` and stores upload metadata\n * - Marks the record as `error` if upload fails\n *\n * This method is intended for server-side uploads (non-presigned flow).\n * For browser direct uploads, use `presignUpload` + `confirmUpload`.\n *\n * @param props - Upload payload (body, contentType, size)\n * @returns The finalized storage asset record\n * @throws {ServerError} If record creation, upload, or finalization fails\n * @example\n * const asset = await storageService.upload({\n * body: fileBuffer,\n * contentType: \"application/pdf\",\n * size: fileBuffer.byteLength,\n * });\n */\n async upload(props: UploadInput) {\n const { body, contentType, size, metadata, name, visibility } = uploadInputSchema.parse(props);\n\n const id = generateDefaultUUID();\n const key = this.#generateKey(id);\n\n const [record] = await this.#db\n .insert(storageAssets)\n .values({\n id,\n key,\n size,\n contentType,\n name,\n visibility,\n provider: this.#adapter.key,\n bucket: this.#adapter.bucketName,\n status: \"pending\",\n metadata,\n })\n .returning();\n\n if (!record) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to create upload record\",\n });\n }\n\n try {\n const uploaded = await this.#adapter.putObject({ key, body, contentType, size, metadata });\n\n const [updated] = await this.#db\n .update(storageAssets)\n .set({\n status: \"ready\",\n uploadedAt: new Date(),\n size: uploaded.size ?? size ?? record.size,\n contentType: uploaded.contentType ?? contentType ?? record.contentType,\n })\n .where(and(eq(storageAssets.id, record.id), eq(storageAssets.status, \"pending\")))\n .returning();\n\n if (!updated) {\n throw new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to finalize upload\",\n });\n }\n\n return updated;\n } catch (error) {\n await this.#db\n .update(storageAssets)\n .set({ status: \"error\" })\n .where(and(eq(storageAssets.id, record.id), eq(storageAssets.status, \"pending\")));\n\n throw this.#parseError(error, {\n fallbackMessage: \"Failed to upload object\",\n });\n }\n }\n\n /**\n * Retrieves the object content for a ready storage asset.\n *\n * Flow:\n * - Looks up the asset record by id, scoped to the current adapter provider\n * - Ensures the asset is in `ready` status\n * - Fetches object content from the storage adapter using the stored key\n *\n * @param id - The storage asset id\n * @returns The adapter object response (stream/body + metadata)\n * @throws {ServerError} If the asset does not exist or is not ready\n * @example\n * const object = await storageService.getObject(\"asset-123\");\n * // object.body can be streamed/consumed by caller\n */\n async getObject(input: string) {\n const id = z.uuid().parse(input);\n\n const [record] = await this.#db\n .select()\n .from(storageAssets)\n .where(\n and(\n eq(storageAssets.id, id),\n eq(storageAssets.provider, this.#adapter.key),\n eq(storageAssets.bucket, this.#adapter.bucketName),\n eq(storageAssets.status, \"ready\"),\n isNull(storageAssets.deletedAt),\n ),\n )\n .limit(1);\n\n if (!record) {\n throw new ServerError(\"NOT_FOUND\", { message: \"Storage asset not found\" });\n }\n\n return this.#adapter.getObject(record.key).catch((error) => {\n throw this.#parseError(error, {\n fallbackMessage: \"Failed to retrieve object\",\n });\n });\n }\n\n /**\n * Generates a presigned read URL for a ready storage asset.\n *\n * Flow:\n * - Validates the asset id\n * - Resolves the asset record scoped to the current adapter provider\n * - Ensures the asset is in `ready` status\n * - Delegates URL signing to the storage adapter using the stored key\n *\n * @param input - Storage asset id (UUID)\n * @param options - Optional URL options (for example expiration/disposition)\n * @returns A presigned URL for reading the object\n * @throws {ServerError} If the asset does not exist or is not ready\n * @example\n * const url = await storageService.getObjectURL(\"019d0051-2c0d-741e-9e3c-e5a5bc4d16a2\", {\n * expiresIn: 3600,\n * });\n */\n\n async getObjectURL(input: string, options?: GetObjectURLOptions) {\n const id = z.uuid().parse(input);\n\n const [record] = await this.#db\n .select()\n .from(storageAssets)\n .where(\n and(\n eq(storageAssets.id, id),\n eq(storageAssets.provider, this.#adapter.key),\n eq(storageAssets.bucket, this.#adapter.bucketName),\n eq(storageAssets.status, \"ready\"),\n isNull(storageAssets.deletedAt),\n ),\n )\n .limit(1);\n\n if (!record) {\n throw new ServerError(\"NOT_FOUND\", { message: \"Storage asset not found\" });\n }\n\n return this.#adapter.getObjectURL(record.key, options).catch((error) => {\n throw this.#parseError(error, {\n fallbackMessage: \"Failed to generate object URL\",\n });\n });\n }\n\n /**\n * Soft deletes a single storage asset by id.\n *\n * This is a convenience wrapper around `deleteAssets`.\n *\n * @param input - Storage asset id (UUID)\n * @returns The soft-deleted asset record, or null if not found/already deleted\n */\n async deleteAsset(input: string) {\n const id = z.uuid().parse(input);\n const [deleted] = await this.deleteAssets([id]);\n return deleted ?? null;\n }\n\n /**\n * Soft deletes multiple storage assets by setting `deletedAt`.\n *\n * Flow:\n * - Validates and de-duplicates ids\n * - Resolves provider-scoped active records\n * - Marks matching rows as deleted by setting `deletedAt`\n *\n * @param input - Storage asset ids (UUID[])\n * @returns Soft-deleted asset records\n */\n async deleteAssets(input: string[]) {\n const ids = [...new Set(z.array(z.uuid()).parse(input))];\n if (ids.length === 0) return [];\n\n return this.#db\n .update(storageAssets)\n .set({ deletedAt: new Date() })\n .where(\n and(\n inArray(storageAssets.id, ids),\n eq(storageAssets.provider, this.#adapter.key),\n eq(storageAssets.bucket, this.#adapter.bucketName),\n isNull(storageAssets.deletedAt),\n ),\n )\n .returning();\n }\n\n /**\n * Restores a single soft-deleted storage asset.\n *\n * This is a convenience wrapper around `restoreAssets`.\n *\n * @param input - Storage asset id (UUID)\n * @returns The restored asset record, or null if not found/not deleted\n */\n async restoreAsset(input: string) {\n const id = z.uuid().parse(input);\n const [restored] = await this.restoreAssets([id]);\n return restored ?? null;\n }\n\n /**\n * Restores multiple soft-deleted storage assets by clearing `deletedAt`.\n *\n * @param input - Storage asset ids (UUID[])\n * @returns Restored asset records\n */\n async restoreAssets(input: string[]) {\n const ids = [...new Set(z.array(z.uuid()).parse(input))];\n if (ids.length === 0) return [];\n\n return this.#db\n .update(storageAssets)\n .set({ deletedAt: null })\n .where(\n and(\n inArray(storageAssets.id, ids),\n eq(storageAssets.provider, this.#adapter.key),\n eq(storageAssets.bucket, this.#adapter.bucketName),\n isNotNull(storageAssets.deletedAt),\n ),\n )\n .returning();\n }\n\n /**\n * Hard deletes a single storage asset.\n *\n * This is a convenience wrapper around `purgeAssets`.\n *\n * @param input - Storage asset id (UUID)\n * @returns The purged asset record, or null if not found\n */\n async purgeAsset(input: string) {\n const id = z.uuid().parse(input);\n const [deleted] = await this.purgeAssets([id]);\n return deleted ?? null;\n }\n\n /**\n * Hard deletes multiple storage assets.\n *\n * Flow:\n * - Validates and de-duplicates ids\n * - Resolves provider-scoped records\n * - Deletes physical objects from the adapter by key\n * - Hard deletes DB records\n *\n * @param input - Storage asset ids (UUID[])\n * @returns Purged asset records\n * @throws {ServerError} If provider deletion fails\n */\n async purgeAssets(input: string[]) {\n const ids = [...new Set(z.array(z.uuid()).parse(input))];\n if (ids.length === 0) return [];\n\n const records = await this.#db\n .select({\n id: storageAssets.id,\n key: storageAssets.key,\n })\n .from(storageAssets)\n .where(\n and(\n inArray(storageAssets.id, ids),\n eq(storageAssets.provider, this.#adapter.key),\n eq(storageAssets.bucket, this.#adapter.bucketName),\n ),\n );\n\n if (records.length === 0) return [];\n\n const keys = records.map((r) => r.key);\n\n try {\n await this.#adapter.deleteObjects(keys);\n } catch (error) {\n throw this.#parseError(error, {\n fallbackMessage: \"Failed to delete storage objects\",\n });\n }\n\n const deletedIds = records.map((r) => r.id);\n\n const deleted = await this.#db\n .delete(storageAssets)\n .where(\n and(\n inArray(storageAssets.id, deletedIds),\n eq(storageAssets.provider, this.#adapter.key),\n eq(storageAssets.bucket, this.#adapter.bucketName),\n ),\n )\n .returning();\n\n return deleted;\n }\n\n /**\n * Normalizes unknown adapter/service errors into a consistent `ServerError`.\n *\n * Behavior:\n * - Returns existing `ServerError` instances unchanged\n * - Maps known storage adapter errors to application-level server errors\n * - Falls back to a generic internal server error for unknown failures\n *\n * This keeps adapter-specific errors inside the storage layer while exposing\n * a stable error contract to route handlers and RPC procedures.\n *\n * @param error - The unknown error to normalize\n * @param options - Optional fallback message for non-specific failures\n * @returns A normalized `ServerError`\n */\n\n #parseError(error: unknown, options?: { fallbackMessage: string }) {\n // If it's already a ServerError, return as-is to avoid double-wrapping\n if (error instanceof ServerError) return error;\n\n // Handle known storage adapter errors and convert to appropriate ServerError\n if (error instanceof StorageAdapterError) {\n if (error.code === \"OBJECT_NOT_FOUND\") {\n return new ServerError(\"NOT_FOUND\", {\n message: \"Storage asset not found\",\n cause: error,\n });\n }\n\n return new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: options?.fallbackMessage ?? \"Storage adapter error\",\n cause: error,\n });\n }\n\n // For unknown errors, return a generic server error with limited information to avoid leaking details\n return new ServerError(\"INTERNAL_SERVER_ERROR\", {\n message: options?.fallbackMessage ?? \"Unknown storage error\",\n cause: error instanceof Error ? error : undefined,\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { PresignUploadInput, PresignUploadOutput, SelectStorageAssetSchema } from "./validators.mjs";
|
|
2
|
+
import { StorageRouterInputs, StorageRouterOutputs } from "./procedures.server.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/modules/storage/lib/upload.client.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Upload request
|
|
7
|
+
*/
|
|
8
|
+
type UploadFileRequest = PresignUploadInput & {
|
|
9
|
+
file: File;
|
|
10
|
+
};
|
|
11
|
+
type PrepareUploadInput = Omit<UploadFileRequest, "name" | "contentType" | "size">;
|
|
12
|
+
type UploadHooks = {
|
|
13
|
+
beforePresign?: (input: UploadFileRequest) => Promise<void> | void;
|
|
14
|
+
afterPresign?: (presignResult: PresignUploadOutput) => Promise<void> | void;
|
|
15
|
+
beforeConfirm?: (presignResult: PresignUploadOutput) => Promise<void> | void;
|
|
16
|
+
afterConfirm?: (asset: SelectStorageAssetSchema) => Promise<void> | void;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Upload client
|
|
20
|
+
*/
|
|
21
|
+
type UploadClient = {
|
|
22
|
+
prepareUpload: (input: PrepareUploadInput) => UploadFileRequest;
|
|
23
|
+
upload: (input: UploadFileRequest, hooks?: UploadHooks) => Promise<SelectStorageAssetSchema>;
|
|
24
|
+
deleteAsset: (id: string) => Promise<SelectStorageAssetSchema | null>;
|
|
25
|
+
deleteAssets: (ids: string[]) => Promise<SelectStorageAssetSchema[]>;
|
|
26
|
+
restoreAsset: (id: string) => Promise<SelectStorageAssetSchema | null>;
|
|
27
|
+
restoreAssets: (ids: string[]) => Promise<SelectStorageAssetSchema[]>;
|
|
28
|
+
purgeAsset: (id: string) => Promise<SelectStorageAssetSchema | null>;
|
|
29
|
+
purgeAssets: (ids: string[]) => Promise<SelectStorageAssetSchema[]>;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Create upload client props
|
|
33
|
+
*/
|
|
34
|
+
type CreateStorageClientProps = {
|
|
35
|
+
endpoints: {
|
|
36
|
+
presign: (input: StorageRouterInputs["presign"]) => Promise<StorageRouterOutputs["presign"]>;
|
|
37
|
+
confirm: (uploadId: StorageRouterInputs["confirm"]) => Promise<StorageRouterOutputs["confirm"]>;
|
|
38
|
+
deleteAssets: (ids: StorageRouterInputs["deleteAssets"]) => Promise<StorageRouterOutputs["deleteAssets"]>;
|
|
39
|
+
deleteAsset: (id: StorageRouterInputs["deleteAsset"]) => Promise<StorageRouterOutputs["deleteAsset"]>;
|
|
40
|
+
restoreAsset: (id: StorageRouterInputs["restoreAsset"]) => Promise<StorageRouterOutputs["restoreAsset"]>;
|
|
41
|
+
restoreAssets: (ids: StorageRouterInputs["restoreAssets"]) => Promise<StorageRouterOutputs["restoreAssets"]>;
|
|
42
|
+
purgeAsset: (id: StorageRouterInputs["purgeAsset"]) => Promise<StorageRouterOutputs["purgeAsset"]>;
|
|
43
|
+
purgeAssets: (ids: StorageRouterInputs["purgeAssets"]) => Promise<StorageRouterOutputs["purgeAssets"]>;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Creates an upload client for the storage module.
|
|
48
|
+
*
|
|
49
|
+
* Flow:
|
|
50
|
+
* - Prepare upload payload with generated id and resolved key
|
|
51
|
+
* - Request presigned upload URL from server
|
|
52
|
+
* - Upload file bytes directly to object storage
|
|
53
|
+
* - Confirm upload and receive finalized storage asset
|
|
54
|
+
*/
|
|
55
|
+
declare function createUploadClient(props: CreateStorageClientProps): UploadClient;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { PrepareUploadInput, UploadClient, UploadFileRequest, UploadHooks, createUploadClient };
|
|
58
|
+
//# sourceMappingURL=upload.client.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.client.d.mts","names":[],"sources":["../../../../src/modules/storage/lib/upload.client.ts"],"mappings":";;;;;;AAUA;KAAY,iBAAA,GAAoB,kBAAA;EAAuB,IAAA,EAAM,IAAA;AAAA;AAAA,KACjD,kBAAA,GAAqB,IAAA,CAAK,iBAAA;AAAA,KAC1B,WAAA;EACV,aAAA,IAAiB,KAAA,EAAO,iBAAA,KAAsB,OAAA;EAC9C,YAAA,IAAgB,aAAA,EAAe,mBAAA,KAAwB,OAAA;EACvD,aAAA,IAAiB,aAAA,EAAe,mBAAA,KAAwB,OAAA;EACxD,YAAA,IAAgB,KAAA,EAAO,wBAAA,KAA6B,OAAA;AAAA;;;;KAM1C,YAAA;EACV,aAAA,GAAgB,KAAA,EAAO,kBAAA,KAAuB,iBAAA;EAC9C,MAAA,GAAS,KAAA,EAAO,iBAAA,EAAmB,KAAA,GAAQ,WAAA,KAAgB,OAAA,CAAQ,wBAAA;EACnE,WAAA,GAAc,EAAA,aAAe,OAAA,CAAQ,wBAAA;EACrC,YAAA,GAAe,GAAA,eAAkB,OAAA,CAAQ,wBAAA;EACzC,YAAA,GAAe,EAAA,aAAe,OAAA,CAAQ,wBAAA;EACtC,aAAA,GAAgB,GAAA,eAAkB,OAAA,CAAQ,wBAAA;EAC1C,UAAA,GAAa,EAAA,aAAe,OAAA,CAAQ,wBAAA;EACpC,WAAA,GAAc,GAAA,eAAkB,OAAA,CAAQ,wBAAA;AAAA;;;;KAMrC,wBAAA;EACH,SAAA;IACE,OAAA,GAAU,KAAA,EAAO,mBAAA,gBAAmC,OAAA,CAAQ,oBAAA;IAC5D,OAAA,GAAU,QAAA,EAAU,mBAAA,gBAAmC,OAAA,CAAQ,oBAAA;IAC/D,YAAA,GACE,GAAA,EAAK,mBAAA,qBACF,OAAA,CAAQ,oBAAA;IACb,WAAA,GACE,EAAA,EAAI,mBAAA,oBACD,OAAA,CAAQ,oBAAA;IACb,YAAA,GACE,EAAA,EAAI,mBAAA,qBACD,OAAA,CAAQ,oBAAA;IACb,aAAA,GACE,GAAA,EAAK,mBAAA,sBACF,OAAA,CAAQ,oBAAA;IACb,UAAA,GACE,EAAA,EAAI,mBAAA,mBACD,OAAA,CAAQ,oBAAA;IACb,WAAA,GACE,GAAA,EAAK,mBAAA,oBACF,OAAA,CAAQ,oBAAA;EAAA;AAAA;;;;;;;AAnCjB;;;iBAgDgB,kBAAA,CAAmB,KAAA,EAAO,wBAAA,GAA2B,YAAA"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//#region src/modules/storage/lib/upload.client.ts
|
|
2
|
+
/**
|
|
3
|
+
* Creates an upload client for the storage module.
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* - Prepare upload payload with generated id and resolved key
|
|
7
|
+
* - Request presigned upload URL from server
|
|
8
|
+
* - Upload file bytes directly to object storage
|
|
9
|
+
* - Confirm upload and receive finalized storage asset
|
|
10
|
+
*/
|
|
11
|
+
function createUploadClient(props) {
|
|
12
|
+
/**
|
|
13
|
+
* Create input schema for the upload method
|
|
14
|
+
* @param {PrepareUploadInput} input
|
|
15
|
+
* @returns {UploadFileRequest}
|
|
16
|
+
*/
|
|
17
|
+
function prepareUpload(input) {
|
|
18
|
+
return {
|
|
19
|
+
...input,
|
|
20
|
+
uploadId: input.uploadId ?? crypto.randomUUID(),
|
|
21
|
+
name: input.file.name,
|
|
22
|
+
size: input.file.size,
|
|
23
|
+
contentType: input.file.type || "application/octet-stream"
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Upload file to the server
|
|
28
|
+
* @param {UploadFileRequest} input
|
|
29
|
+
* @returns {Promise<SelectStorageAssetSchema>}
|
|
30
|
+
*/
|
|
31
|
+
async function upload(input, hooks) {
|
|
32
|
+
let presignResult = null;
|
|
33
|
+
try {
|
|
34
|
+
/**
|
|
35
|
+
* Presign
|
|
36
|
+
*/
|
|
37
|
+
await hooks?.beforePresign?.(input);
|
|
38
|
+
presignResult = await props.endpoints.presign(input);
|
|
39
|
+
await hooks?.afterPresign?.(presignResult);
|
|
40
|
+
/**
|
|
41
|
+
* Upload the file
|
|
42
|
+
*/
|
|
43
|
+
const uploadResponse = await fetch(presignResult.presignedUrl, {
|
|
44
|
+
method: "PUT",
|
|
45
|
+
headers: { "Content-Type": input.contentType || "application/octet-stream" },
|
|
46
|
+
body: input.file
|
|
47
|
+
});
|
|
48
|
+
if (!uploadResponse.ok) {
|
|
49
|
+
const message = await uploadResponse.text();
|
|
50
|
+
throw new Error(`Upload failed: ${message}`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Confirm
|
|
54
|
+
*/
|
|
55
|
+
await hooks?.beforeConfirm?.(presignResult);
|
|
56
|
+
const asset = await props.endpoints.confirm(presignResult.uploadId);
|
|
57
|
+
await hooks?.afterConfirm?.(asset);
|
|
58
|
+
/**
|
|
59
|
+
* Return
|
|
60
|
+
*/
|
|
61
|
+
return asset;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error("Upload error: ", err);
|
|
64
|
+
/**
|
|
65
|
+
* Delete if upload failed
|
|
66
|
+
* */
|
|
67
|
+
if (presignResult) await props.endpoints.deleteAsset(presignResult.id).catch((deleteErr) => {
|
|
68
|
+
console.error("Failed to delete asset after upload error: ", deleteErr);
|
|
69
|
+
});
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
prepareUpload,
|
|
75
|
+
upload,
|
|
76
|
+
deleteAsset: props.endpoints.deleteAsset,
|
|
77
|
+
deleteAssets: props.endpoints.deleteAssets,
|
|
78
|
+
restoreAsset: props.endpoints.restoreAsset,
|
|
79
|
+
restoreAssets: props.endpoints.restoreAssets,
|
|
80
|
+
purgeAsset: props.endpoints.purgeAsset,
|
|
81
|
+
purgeAssets: props.endpoints.purgeAssets
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
export { createUploadClient };
|
|
87
|
+
//# sourceMappingURL=upload.client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.client.mjs","names":[],"sources":["../../../../src/modules/storage/lib/upload.client.ts"],"sourcesContent":["import type { StorageRouterInputs, StorageRouterOutputs } from \"./procedures.server\";\nimport type {\n PresignUploadInput,\n PresignUploadOutput,\n SelectStorageAssetSchema,\n} from \"./validators\";\n\n/**\n * Upload request\n */\nexport type UploadFileRequest = PresignUploadInput & { file: File };\nexport type PrepareUploadInput = Omit<UploadFileRequest, \"name\" | \"contentType\" | \"size\">;\nexport type UploadHooks = {\n beforePresign?: (input: UploadFileRequest) => Promise<void> | void;\n afterPresign?: (presignResult: PresignUploadOutput) => Promise<void> | void;\n beforeConfirm?: (presignResult: PresignUploadOutput) => Promise<void> | void;\n afterConfirm?: (asset: SelectStorageAssetSchema) => 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<SelectStorageAssetSchema>;\n deleteAsset: (id: string) => Promise<SelectStorageAssetSchema | null>;\n deleteAssets: (ids: string[]) => Promise<SelectStorageAssetSchema[]>;\n restoreAsset: (id: string) => Promise<SelectStorageAssetSchema | null>;\n restoreAssets: (ids: string[]) => Promise<SelectStorageAssetSchema[]>;\n purgeAsset: (id: string) => Promise<SelectStorageAssetSchema | null>;\n purgeAssets: (ids: string[]) => Promise<SelectStorageAssetSchema[]>;\n};\n\n/**\n * Create upload client props\n */\ntype CreateStorageClientProps = {\n endpoints: {\n presign: (input: StorageRouterInputs[\"presign\"]) => Promise<StorageRouterOutputs[\"presign\"]>;\n confirm: (uploadId: StorageRouterInputs[\"confirm\"]) => Promise<StorageRouterOutputs[\"confirm\"]>;\n deleteAssets: (\n ids: StorageRouterInputs[\"deleteAssets\"],\n ) => Promise<StorageRouterOutputs[\"deleteAssets\"]>;\n deleteAsset: (\n id: StorageRouterInputs[\"deleteAsset\"],\n ) => Promise<StorageRouterOutputs[\"deleteAsset\"]>;\n restoreAsset: (\n id: StorageRouterInputs[\"restoreAsset\"],\n ) => Promise<StorageRouterOutputs[\"restoreAsset\"]>;\n restoreAssets: (\n ids: StorageRouterInputs[\"restoreAssets\"],\n ) => Promise<StorageRouterOutputs[\"restoreAssets\"]>;\n purgeAsset: (\n id: StorageRouterInputs[\"purgeAsset\"],\n ) => Promise<StorageRouterOutputs[\"purgeAsset\"]>;\n purgeAssets: (\n ids: StorageRouterInputs[\"purgeAssets\"],\n ) => Promise<StorageRouterOutputs[\"purgeAssets\"]>;\n };\n};\n\n/**\n * Creates an upload client for the storage module.\n *\n * Flow:\n * - Prepare upload payload with generated id and resolved key\n * - Request presigned upload URL from server\n * - Upload file bytes directly to object storage\n * - Confirm upload and receive finalized storage asset\n */\nexport function createUploadClient(props: CreateStorageClientProps): 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 uploadId: input.uploadId ?? crypto.randomUUID(),\n name: input.file.name,\n size: input.file.size,\n contentType: input.file.type || \"application/octet-stream\",\n };\n }\n\n /**\n * Upload file to the server\n * @param {UploadFileRequest} input\n * @returns {Promise<SelectStorageAssetSchema>}\n */\n async function upload(\n input: UploadFileRequest,\n hooks?: UploadHooks,\n ): Promise<SelectStorageAssetSchema> {\n let presignResult: PresignUploadOutput | null = null;\n\n try {\n /**\n * Presign\n */\n await hooks?.beforePresign?.(input);\n presignResult = await props.endpoints.presign(input);\n await hooks?.afterPresign?.(presignResult);\n\n /**\n * Upload the file\n */\n const uploadResponse = await fetch(presignResult.presignedUrl, {\n method: \"PUT\",\n headers: { \"Content-Type\": input.contentType || \"application/octet-stream\" },\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 asset = await props.endpoints.confirm(presignResult.uploadId);\n await hooks?.afterConfirm?.(asset);\n /**\n * Return\n */\n return asset;\n } catch (err) {\n console.error(\"Upload error: \", err);\n\n /**\n * Delete if upload failed\n * */\n if (presignResult) {\n await props.endpoints.deleteAsset(presignResult.id).catch((deleteErr) => {\n console.error(\"Failed to delete asset after upload error: \", deleteErr);\n });\n }\n\n throw err;\n }\n }\n\n return {\n prepareUpload,\n upload,\n deleteAsset: props.endpoints.deleteAsset,\n deleteAssets: props.endpoints.deleteAssets,\n restoreAsset: props.endpoints.restoreAsset,\n restoreAssets: props.endpoints.restoreAssets,\n purgeAsset: props.endpoints.purgeAsset,\n purgeAssets: props.endpoints.purgeAssets,\n };\n}\n"],"mappings":";;;;;;;;;;AAsEA,SAAgB,mBAAmB,OAA+C;;;;;;CAMhF,SAAS,cAAc,OAA8C;AACnE,SAAO;GACL,GAAG;GACH,UAAU,MAAM,YAAY,OAAO,YAAY;GAC/C,MAAM,MAAM,KAAK;GACjB,MAAM,MAAM,KAAK;GACjB,aAAa,MAAM,KAAK,QAAQ;GACjC;;;;;;;CAQH,eAAe,OACb,OACA,OACmC;EACnC,IAAI,gBAA4C;AAEhD,MAAI;;;;AAIF,SAAM,OAAO,gBAAgB,MAAM;AACnC,mBAAgB,MAAM,MAAM,UAAU,QAAQ,MAAM;AACpD,SAAM,OAAO,eAAe,cAAc;;;;GAK1C,MAAM,iBAAiB,MAAM,MAAM,cAAc,cAAc;IAC7D,QAAQ;IACR,SAAS,EAAE,gBAAgB,MAAM,eAAe,4BAA4B;IAC5E,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,QAAQ,MAAM,MAAM,UAAU,QAAQ,cAAc,SAAS;AACnE,SAAM,OAAO,eAAe,MAAM;;;;AAIlC,UAAO;WACA,KAAK;AACZ,WAAQ,MAAM,kBAAkB,IAAI;;;;AAKpC,OAAI,cACF,OAAM,MAAM,UAAU,YAAY,cAAc,GAAG,CAAC,OAAO,cAAc;AACvE,YAAQ,MAAM,+CAA+C,UAAU;KACvE;AAGJ,SAAM;;;AAIV,QAAO;EACL;EACA;EACA,aAAa,MAAM,UAAU;EAC7B,cAAc,MAAM,UAAU;EAC9B,cAAc,MAAM,UAAU;EAC9B,eAAe,MAAM,UAAU;EAC/B,YAAY,MAAM,UAAU;EAC5B,aAAa,MAAM,UAAU;EAC9B"}
|