From 6abbf4107fa1bbf83bbb16d5b5f8052122e12045 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 4 Nov 2024 11:55:50 +0200 Subject: [PATCH 01/36] feat: Add update functionality for attachments realtime --- .../organizations/attachments-table/index.tsx | 78 ++++++++++++++----- pnpm-lock.yaml | 7 +- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/frontend/src/modules/organizations/attachments-table/index.tsx b/frontend/src/modules/organizations/attachments-table/index.tsx index 671d54c97..5297cbf9a 100644 --- a/frontend/src/modules/organizations/attachments-table/index.tsx +++ b/frontend/src/modules/organizations/attachments-table/index.tsx @@ -221,16 +221,17 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro ); }; - // Subscribe to task updates + // Subscribe to attachments updates useEffect(() => { if (networkMode !== 'online' || !config.has.sync || !env.VITE_HAS_SYNC) return; const shapeStream = new ShapeStream(attachmentShape(organization.id)); + const queryKey = attachmentsQueryOptions({ orgIdOrSlug: organization.id }).queryKey; const unsubscribe = shapeStream.subscribe((messages) => { const createMessage = messages.find((m) => m.headers.operation === 'insert') as ChangeMessage | undefined; if (createMessage) { const value = createMessage.value; - queryClient.setQueryData(attachmentsQueryOptions({ orgIdOrSlug: organization.id }).queryKey, (data) => { + queryClient.setQueryData(queryKey, (data) => { if (!data) return; const created = {} as unknown as Attachment; // TODO: Refactor @@ -264,24 +265,65 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro }); } + const updateMessage = messages.find((m) => m.headers.operation === 'update') as ChangeMessage | undefined; + if (updateMessage) { + const value = updateMessage.value; + queryClient.setQueryData(queryKey, (data) => { + if (!data) return; + return { + ...data, + pages: data.pages.map((page) => { + return { + ...page, + items: page.items.map((attachment) => { + if (attachment.id === value.id) { + const updated = { + ...attachment, + } as unknown as Attachment; + // TODO: Refactor + for (const key of objectKeys(value)) { + if (key === 'content_type') { + updated.contentType = value[key]; + } else if (key === 'organization_id') { + updated.organizationId = value[key]; + } else if (key === 'created_at') { + updated.createdAt = value[key]; + } else if (key === 'created_by') { + updated.createdBy = value[key]; + } else if (key === 'modified_at') { + updated.modifiedAt = value[key]; + } else if (key === 'modified_by') { + updated.modifiedBy = value[key]; + } else { + updated[key] = value[key] as never; + } + } + return updated; + } + + return attachment; + }), + }; + }), + }; + }); + } + const deleteMessage = messages.find((m) => m.headers.operation === 'delete') as ChangeMessage | undefined; if (deleteMessage) { - queryClient.setQueryData( - attachmentsQueryOptions({ orgIdOrSlug: organization.id, rowsLength: rows.length }).queryKey, - (data) => { - if (!data) return; - return { - ...data, - pages: [ - { - ...data.pages[0], - items: data.pages[0].items.filter((item) => item.id !== deleteMessage.value.id), - }, - ...data.pages.slice(1), - ], - }; - }, - ); + queryClient.setQueryData(queryKey, (data) => { + if (!data) return; + return { + ...data, + pages: [ + { + ...data.pages[0], + items: data.pages[0].items.filter((item) => item.id !== deleteMessage.value.id), + }, + ...data.pages.slice(1), + ], + }; + }); } }); return () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e00ab147e..aeb2ed596 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -318,9 +318,6 @@ importers: '@hookform/resolvers': specifier: ^3.9.1 version: 3.9.1(react-hook-form@7.53.1(react@18.3.1)) - '@million/lint': - specifier: ^1.0.11 - version: 1.0.11(rollup@2.79.2)(webpack-sources@3.2.3) '@oslojs/encoding': specifier: ^1.1.0 version: 1.1.0 @@ -601,6 +598,9 @@ importers: '@faker-js/faker': specifier: ^9.1.0 version: 9.1.0 + '@million/lint': + specifier: ^1.0.11 + version: 1.0.11(rollup@2.79.2)(webpack-sources@3.2.3) '@redux-devtools/extension': specifier: ^3.3.0 version: 3.3.0(redux@5.0.1) @@ -2794,7 +2794,6 @@ packages: '@evilmartians/lefthook@1.8.2': resolution: {integrity: sha512-SZdQk3W9q7tcJwnSwEMUubQqVIK7SHxv52hEAnV7o3nPI+xKcmd+rN0hZIJg07wjBaJRAjzdvoQySKQQYPW5Qw==} - cpu: [x64, arm64, ia32] os: [darwin, linux, win32] hasBin: true From ff1fb611cb3ed97841682e96fe17f0609796ddab Mon Sep 17 00:00:00 2001 From: David Hordiienko Date: Mon, 4 Nov 2024 12:02:29 +0200 Subject: [PATCH 02/36] add: pragmatic DnD auto-scroll --- frontend/package.json | 1 + frontend/src/modules/common/nav-sheet/sheet-menu.tsx | 11 +++++++++-- frontend/src/modules/ui/scroll-area.tsx | 9 ++++++--- pnpm-lock.yaml | 12 ++++++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index fb4dfd890..eaf97f53e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.4.0", + "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^1.4.0", "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3", "@blocknote/core": "^0.17.1", "@blocknote/react": "^0.17.1", diff --git a/frontend/src/modules/common/nav-sheet/sheet-menu.tsx b/frontend/src/modules/common/nav-sheet/sheet-menu.tsx index fa14c0c02..007f7ee63 100644 --- a/frontend/src/modules/common/nav-sheet/sheet-menu.tsx +++ b/frontend/src/modules/common/nav-sheet/sheet-menu.tsx @@ -1,8 +1,9 @@ -import { memo, useCallback, useEffect, useMemo, useState } from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import type { ContextEntity, DraggableItemData, UserMenu, UserMenuItem } from '~/types/common'; import { useNavigationStore } from '~/store/navigation'; +import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-scroll/element'; import { type Edge, extractClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge'; import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine'; import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'; @@ -44,6 +45,7 @@ export const SheetMenu = memo(() => { const [searchTerm, setSearchTerm] = useState(''); const [searchResults, setSearchResults] = useState([]); + const scrollViewportRef = useRef(null); const pwaEnabled = config.has.pwa; const searchResultsListItems = useCallback(() => { @@ -69,7 +71,12 @@ export const SheetMenu = memo(() => { // monitoring drop event useEffect(() => { + if (!scrollViewportRef.current) return; return combine( + autoScrollForElements({ + element: scrollViewportRef.current, + getAllowedAxis: () => 'vertical', + }), monitorForElements({ canMonitor({ source }) { return isPageData(source.data); @@ -101,7 +108,7 @@ export const SheetMenu = memo(() => { }, [menu]); return ( - +
diff --git a/frontend/src/modules/ui/scroll-area.tsx b/frontend/src/modules/ui/scroll-area.tsx index 01a697bf1..ffd8c88a5 100644 --- a/frontend/src/modules/ui/scroll-area.tsx +++ b/frontend/src/modules/ui/scroll-area.tsx @@ -23,10 +23,13 @@ const scrollbarVariants = cva('flex touch-none transition-colors z-20', { const ScrollArea = React.forwardRef< React.ElementRef, - VariantProps & React.ComponentPropsWithoutRef ->(({ className, children, id, size, ...props }, ref) => ( + VariantProps & + React.ComponentPropsWithoutRef & { + viewPortRef?: React.Ref; + } +>(({ className, children, id, size, viewPortRef, ...props }, ref) => ( - + {children} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aeb2ed596..cb66028f3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -294,6 +294,9 @@ importers: '@atlaskit/pragmatic-drag-and-drop': specifier: ^1.4.0 version: 1.4.0 + '@atlaskit/pragmatic-drag-and-drop-auto-scroll': + specifier: ^1.4.0 + version: 1.4.0 '@atlaskit/pragmatic-drag-and-drop-hitbox': specifier: ^1.0.3 version: 1.0.3 @@ -738,6 +741,9 @@ packages: peerDependencies: zod: ^3.20.2 + '@atlaskit/pragmatic-drag-and-drop-auto-scroll@1.4.0': + resolution: {integrity: sha512-5GoikoTSW13UX76F9TDeWB8x3jbbGlp/Y+3aRkHe1MOBMkrWkwNpJ42MIVhhX/6NSeaZiPumP0KbGJVs2tOWSQ==} + '@atlaskit/pragmatic-drag-and-drop-hitbox@1.0.3': resolution: {integrity: sha512-/Sbu/HqN2VGLYBhnsG7SbRNg98XKkbF6L7XDdBi+izRybfaK1FeMfodPpm/xnBHPJzwYMdkE0qtLyv6afhgMUA==} @@ -2794,6 +2800,7 @@ packages: '@evilmartians/lefthook@1.8.2': resolution: {integrity: sha512-SZdQk3W9q7tcJwnSwEMUubQqVIK7SHxv52hEAnV7o3nPI+xKcmd+rN0hZIJg07wjBaJRAjzdvoQySKQQYPW5Qw==} + cpu: [x64, arm64, ia32] os: [darwin, linux, win32] hasBin: true @@ -11056,6 +11063,11 @@ snapshots: openapi3-ts: 4.4.0 zod: 3.23.8 + '@atlaskit/pragmatic-drag-and-drop-auto-scroll@1.4.0': + dependencies: + '@atlaskit/pragmatic-drag-and-drop': 1.4.0 + '@babel/runtime': 7.26.0 + '@atlaskit/pragmatic-drag-and-drop-hitbox@1.0.3': dependencies: '@atlaskit/pragmatic-drag-and-drop': 1.4.0 From 559c29f1a78d1e3733e509105a2ee9ba016fa84b Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 4 Nov 2024 13:08:38 +0200 Subject: [PATCH 03/36] feat: Add offline prefetch throttle --- frontend/src/modules/common/query-client-provider/index.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/modules/common/query-client-provider/index.tsx b/frontend/src/modules/common/query-client-provider/index.tsx index 292218fe2..d310fddb6 100644 --- a/frontend/src/modules/common/query-client-provider/index.tsx +++ b/frontend/src/modules/common/query-client-provider/index.tsx @@ -25,6 +25,8 @@ async function prefetchQuery(options: UseQueryOptions | UseInfiniteQueryOptions) return offlineFetch(options); } +const waitFor = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + const prefetchMembers = async ( item: { slug: string; @@ -64,6 +66,8 @@ export const QueryClientProvider = ({ children }: { children: React.ReactNode }) const options = organizationQueryOptions(item.slug); prefetchQuery(options); prefetchMembers(item, item.slug); + + await waitFor(1000); // wait for a second to avoid server overload } } } From 0559656af7570206f71398f65381fe652b704e62 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 4 Nov 2024 13:12:34 +0200 Subject: [PATCH 04/36] feat: Wait 1 second after init load before prefetch --- frontend/src/modules/common/query-client-provider/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/modules/common/query-client-provider/index.tsx b/frontend/src/modules/common/query-client-provider/index.tsx index d310fddb6..b480898ae 100644 --- a/frontend/src/modules/common/query-client-provider/index.tsx +++ b/frontend/src/modules/common/query-client-provider/index.tsx @@ -45,6 +45,8 @@ export const QueryClientProvider = ({ children }: { children: React.ReactNode }) if (networkMode === 'online') return; (async () => { + await waitFor(1000); // wait for a second to avoid server overload + // Invalidate and prefetch me and menu const meQueryOptions: UseQueryOptions = { queryKey: ['me'], From 5ec24f556691f271601ceec8253d9c5f38f95ac3 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 4 Nov 2024 14:15:35 +0200 Subject: [PATCH 05/36] feat: Close sentry to avoid sending errors when offline --- frontend/src/lib/sentry.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frontend/src/lib/sentry.ts b/frontend/src/lib/sentry.ts index aa6595f95..2984156ad 100644 --- a/frontend/src/lib/sentry.ts +++ b/frontend/src/lib/sentry.ts @@ -1,6 +1,16 @@ import * as Sentry from '@sentry/react'; import { config } from 'config'; +// Initialize Sentry +window.ononline = () => { + initSentry(); +}; + +// Close Sentry when offline to avoid sending errors +window.onoffline = () => { + Sentry.close(); +}; + export const initSentry = () => { // Send errors to Sentry Sentry.init({ From 20a6629e05f469c0666b1e0a7e272f4b0c68d172 Mon Sep 17 00:00:00 2001 From: flipvanhaaren Date: Tue, 5 Nov 2024 14:56:00 +0100 Subject: [PATCH 06/36] auth helpers refactor --- backend/src/modules/auth/helpers/cookies.ts | 91 ++++++++----------- .../src/modules/auth/helpers/device-info.ts | 26 ++++++ backend/src/modules/auth/helpers/oauth.ts | 44 +++++---- .../src/modules/auth/helpers/verify-email.ts | 2 +- backend/src/modules/auth/index.ts | 12 +-- 5 files changed, 90 insertions(+), 85 deletions(-) create mode 100644 backend/src/modules/auth/helpers/device-info.ts diff --git a/backend/src/modules/auth/helpers/cookies.ts b/backend/src/modules/auth/helpers/cookies.ts index 4db0b382d..11309ba4b 100644 --- a/backend/src/modules/auth/helpers/cookies.ts +++ b/backend/src/modules/auth/helpers/cookies.ts @@ -3,64 +3,58 @@ import { eq } from 'drizzle-orm'; import type { Context } from 'hono'; import { setCookie as baseSetCookie } from 'hono/cookie'; import type { User } from 'lucia'; -import uaParser from 'ua-parser-js'; import { db } from '#/db/db'; import { auth } from '#/db/lucia'; import { supportedOauthProviders } from '#/db/schema/oauth-accounts'; import { usersTable } from '#/db/schema/users'; import { logEvent } from '#/middlewares/logger/log-event'; +import { deviceInfo } from './device-info'; + +// Cookie session is a regular or impersonation session +type SessionType = 'regular' | 'impersonation'; // The authentication strategies supported by cella export const supportedAuthStrategies = ['oauth', 'password', 'passkey'] as const; const isProduction = config.mode === 'production'; -const getDevice = (ctx: Context) => { - const parsedUserAgent = uaParser(ctx.req.header('User-Agent')); - const name = - parsedUserAgent.device.model && parsedUserAgent.device.vendor - ? `${parsedUserAgent.device.vendor} ${parsedUserAgent.device.model}` - : parsedUserAgent.device.model || parsedUserAgent.device.vendor || null; - const type: 'desktop' | 'mobile' = - parsedUserAgent.device.type && ['wearable', 'mobile'].includes(parsedUserAgent.device.type) ? 'mobile' : 'desktop'; - const os = - parsedUserAgent.os.name && parsedUserAgent.os.version - ? `${parsedUserAgent.os.name} ${parsedUserAgent.os.version}` - : parsedUserAgent.os.name || null; - const browser = - parsedUserAgent.browser.name && parsedUserAgent.browser.version - ? `${parsedUserAgent.browser.name} ${parsedUserAgent.browser.version}` - : parsedUserAgent.browser.name || null; - - return { name, type, os, browser }; -}; - +// Type guard to check if strategy is supported const isAuthStrategy = (strategy: string): strategy is (typeof allSupportedStrategies)[number] => { - const [, ...elseStrategies] = supportedAuthStrategies; // Destructure to exclude 'oauth' + const [, ...elseStrategies] = supportedAuthStrategies; const allSupportedStrategies = [...supportedOauthProviders, ...elseStrategies]; - // Check if strategy is supported return (allSupportedStrategies as string[]).includes(strategy); }; -const getAuthStrategy = (strategy: string) => (isAuthStrategy(strategy) ? strategy : null); -export const setCookie = (ctx: Context, name: string, value: string) => +// Validate auth strategy +const validateAuthStrategy = (strategy: string) => (isAuthStrategy(strategy) ? strategy : null); + +export const setCookie = (ctx: Context, name: string, value: string, singleSession?: boolean) => baseSetCookie(ctx, name, value, { secure: isProduction, // set `Secure` flag in HTTPS path: '/', domain: isProduction ? config.domain : undefined, httpOnly: true, - sameSite: isProduction ? 'lax' : 'lax', - maxAge: 60 * 10, // 10 min + sameSite: isProduction ? 'lax' : 'lax', // Strict is possible if we use a proxy for api + ...(singleSession ? {} : { maxAge: 60 * 10 }), // 10 min, omitted if singleSession is true }); -export const setSessionCookie = async (ctx: Context, userId: User['id'], strategy: string) => { - const device = getDevice(ctx); - const authStrategy = getAuthStrategy(strategy); +export const setSessionCookie = async ( + ctx: Context, + userId: User['id'], + strategy: string | null, + sessionType: SessionType = 'regular', + adminUserId?: User['id'], +) => { + // Get device information + const device = deviceInfo(ctx); + + // Validate auth strategy + const authStrategy = sessionType === 'regular' ? validateAuthStrategy(strategy || '') : null; const session = await auth.createSession(userId, { - type: 'regular', - adminUserId: null, + type: sessionType, + adminUserId: sessionType === 'impersonation' ? (adminUserId ?? null) : null, deviceName: device.name, deviceType: device.type, deviceOs: device.os, @@ -68,32 +62,19 @@ export const setSessionCookie = async (ctx: Context, userId: User['id'], strateg authStrategy, createdAt: new Date(), }); - const sessionCookie = auth.createSessionCookie(session.id); - - const lastSignInAt = new Date(); - await db.update(usersTable).set({ lastSignInAt }).where(eq(usersTable.id, userId)); - - logEvent('User signed in', { user: userId, strategy: strategy }); - ctx.header('Set-Cookie', sessionCookie.serialize()); -}; - -export const setImpersonationSessionCookie = async (ctx: Context, userId: User['id'], adminUserId: User['id']) => { - const device = getDevice(ctx); - - const session = await auth.createSession(userId, { - type: 'impersonation', - adminUserId, - deviceName: device.name, - deviceType: device.type, - deviceOs: device.os, - browser: device.browser, - authStrategy: null, - createdAt: new Date(), - }); const sessionCookie = auth.createSessionCookie(session.id); - logEvent('Admin impersonation signed in', { user: userId, strategy: 'impersonation' }); + // If it's a regular session, we need to update the user's last sign + if (sessionType === 'regular') { + const lastSignInAt = new Date(); + await db.update(usersTable).set({ lastSignInAt }).where(eq(usersTable.id, userId)); + logEvent('User signed in', { user: userId, strategy: strategy }); + + // If it's an impersonation session, we only need to log the event + } else { + logEvent('Impersonation started', { user: userId, strategy: 'impersonation' }); + } ctx.header('Set-Cookie', sessionCookie.serialize()); }; diff --git a/backend/src/modules/auth/helpers/device-info.ts b/backend/src/modules/auth/helpers/device-info.ts new file mode 100644 index 000000000..c9dfece58 --- /dev/null +++ b/backend/src/modules/auth/helpers/device-info.ts @@ -0,0 +1,26 @@ +import type { Context } from 'hono'; +import uaParser from 'ua-parser-js'; + +// Get device information from user agent +export const deviceInfo = (ctx: Context) => { + const { device, os, browser } = uaParser(ctx.req.header('User-Agent')); + + const getName = () => { + if (device.model && device.vendor) return `${device.vendor} ${device.model}`; + return device.model || device.vendor || null; + }; + + const getType = (): 'mobile' | 'desktop' => { + return device.type === 'wearable' || device.type === 'mobile' ? 'mobile' : 'desktop'; + }; + const getOs = () => (os.name && os.version ? `${os.name} ${os.version}` : os.name || null); + + const getBrowser = () => (browser.name && browser.version ? `${browser.name} ${browser.version}` : browser.name || null); + + return { + name: getName(), + type: getType(), + os: getOs(), + browser: getBrowser(), + }; +}; diff --git a/backend/src/modules/auth/helpers/oauth.ts b/backend/src/modules/auth/helpers/oauth.ts index 3eb6cd8ae..06bc1940a 100644 --- a/backend/src/modules/auth/helpers/oauth.ts +++ b/backend/src/modules/auth/helpers/oauth.ts @@ -13,7 +13,7 @@ import { logEvent } from '#/middlewares/logger/log-event'; import type { OauthProviderOptions } from '#/types/common'; import { sendVerificationEmail } from './verify-email'; -// Create a session before redirecting to the oauth provider +// Create a session before redirecting to oauth provider export const createSession = (ctx: Context, provider: string, state: string, codeVerifier?: string, redirect?: string) => { setCookie(ctx, 'oauth_state', state); @@ -23,18 +23,21 @@ export const createSession = (ctx: Context, provider: string, state: string, cod logEvent('User redirected', { strategy: provider }); }; -// Get the redirect URL from the cookie or use default +// Get redirect URL from cookie or use default export const getRedirectUrl = (ctx: Context, firstSignIn?: boolean): string => { const redirectCookie = getCookie(ctx, 'oauth_redirect'); const redirectCookieUrl = redirectCookie ? decodeURIComponent(redirectCookie) : ''; - let redirectUrl = config.frontendUrl + config.defaultRedirectPath; + let redirectPath = config.defaultRedirectPath; if (redirectCookie) { - redirectUrl = redirectCookieUrl.startsWith('http') ? decodeURIComponent(redirectCookie) : config.frontendUrl + redirectCookieUrl; + if (redirectCookieUrl.startsWith('http')) return decodeURIComponent(redirectCookie); + redirectPath = redirectCookieUrl; } - if (firstSignIn) redirectUrl = config.frontendUrl + config.firstSignInRedirectPath; - return redirectUrl; + + if (firstSignIn) redirectPath = config.firstSignInRedirectPath; + return config.frontendUrl + redirectPath; }; + // Insert oauth account into db export const insertOauthAccount = async (userId: string, providerId: OauthProviderOptions, providerUserId: string) => { await db.insert(oauthAccountsTable).values({ providerId, providerUserId, userId }); @@ -48,7 +51,7 @@ export const findOauthAccount = async (providerId: OauthProviderOptions, provide .where(and(eq(oauthAccountsTable.providerId, providerId), eq(oauthAccountsTable.providerUserId, providerUserId))); }; -// Create a slug from email +// Create slug from email export const slugFromEmail = (email: string) => { const [alias] = email.split('@'); return slugify(alias, { lower: true }); @@ -60,24 +63,19 @@ export const splitFullName = (name: string) => { return { firstName: firstName || '', lastName: lastName || '' }; }; -// Handle existing user -export const handleExistingUser = async ( - ctx: Context, - existingUser: User, - providerId: OauthProviderOptions, - { - providerUser, - isEmailVerified, - redirectUrl, - }: { - providerUser: Pick; - isEmailVerified: boolean; - redirectUrl: string; - }, -) => { +interface Params { + providerUser: Pick; + isEmailVerified: boolean; + redirectUrl: string; +} + +// Update existing user +export const updateExistingUser = async (ctx: Context, existingUser: User, providerId: OauthProviderOptions, params: Params) => { + const { providerUser, isEmailVerified, redirectUrl } = params; + await insertOauthAccount(existingUser.id, providerId, providerUser.id); - // Update user with provider data if not already present + // Update user with auth provider data if not already present await db .update(usersTable) .set({ diff --git a/backend/src/modules/auth/helpers/verify-email.ts b/backend/src/modules/auth/helpers/verify-email.ts index b6be5d43b..642bf0c3b 100644 --- a/backend/src/modules/auth/helpers/verify-email.ts +++ b/backend/src/modules/auth/helpers/verify-email.ts @@ -14,6 +14,6 @@ export const sendVerificationEmail = (email: string) => { }), }); } catch (err) { - return logEvent('Verification email sending failed'); + return logEvent('Verification email could not be sent'); } }; diff --git a/backend/src/modules/auth/index.ts b/backend/src/modules/auth/index.ts index d9eee79a0..ba9f504fc 100644 --- a/backend/src/modules/auth/index.ts +++ b/backend/src/modules/auth/index.ts @@ -14,7 +14,7 @@ import { encodeBase64 } from '@oslojs/encoding'; import slugify from 'slugify'; import { githubAuth, googleAuth, microsoftAuth } from '#/db/lucia'; -import { createSession, findOauthAccount, getRedirectUrl, handleExistingUser, slugFromEmail, splitFullName } from './helpers/oauth'; +import { createSession, findOauthAccount, getRedirectUrl, slugFromEmail, splitFullName, updateExistingUser } from './helpers/oauth'; import { getRandomValues } from 'node:crypto'; import { config } from 'config'; @@ -33,7 +33,7 @@ import { hashPasswordWithArgon, verifyPasswordWithArgon } from '#/modules/auth/h import { CustomHono, type EnabledOauthProviderOptions } from '#/types/common'; import { nanoid } from '#/utils/nanoid'; import generalRouteConfig from '../general/routes'; -import { removeSessionCookie, setCookie, setImpersonationSessionCookie, setSessionCookie } from './helpers/cookies'; +import { removeSessionCookie, setCookie, setSessionCookie } from './helpers/cookies'; import { parseAndValidatePasskeyAttestation, verifyPassKeyPublic } from './helpers/passkey'; import { handleCreateUser } from './helpers/user'; import { sendVerificationEmail } from './helpers/verify-email'; @@ -360,7 +360,7 @@ const authRoutes = app return errorResponse(ctx, 401, 'unauthorized', 'warn'); } const { targetUserId } = ctx.req.valid('query'); - await setImpersonationSessionCookie(ctx, targetUserId, user.id); + await setSessionCookie(ctx, targetUserId, user.id, 'impersonation'); return ctx.json({ success: true }, 200); }) @@ -575,7 +575,7 @@ const authRoutes = app // Check if user already exists const existingUser = await getUserBy('email', userEmail); if (existingUser) { - return await handleExistingUser(ctx, existingUser, strategy, { + return await updateExistingUser(ctx, existingUser, strategy, { providerUser: { id: String(githubUser.id), email: githubUserEmail, @@ -682,7 +682,7 @@ const authRoutes = app const existingUser = await getUserBy('email', user.email.toLowerCase()); if (existingUser) { - return await handleExistingUser(ctx, existingUser, strategy, { + return await updateExistingUser(ctx, existingUser, strategy, { providerUser: { id: user.sub, email: user.email, @@ -787,7 +787,7 @@ const authRoutes = app // Check if user already exists const existingUser = await getUserBy('email', user.email.toLowerCase()); if (existingUser) { - return await handleExistingUser(ctx, existingUser, strategy, { + return await updateExistingUser(ctx, existingUser, strategy, { providerUser: { id: user.sub, email: user.email, From 602902f3cd820134fbfdfb55be186b902dd78bf6 Mon Sep 17 00:00:00 2001 From: flipvanhaaren Date: Wed, 6 Nov 2024 09:15:15 +0100 Subject: [PATCH 07/36] only set aspect ratio for cover or avatars --- .../common/upload/image-editor-options.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/frontend/src/modules/common/upload/image-editor-options.ts b/frontend/src/modules/common/upload/image-editor-options.ts index b800ccfb9..4221ad20e 100644 --- a/frontend/src/modules/common/upload/image-editor-options.ts +++ b/frontend/src/modules/common/upload/image-editor-options.ts @@ -29,14 +29,16 @@ const baseActions = { }; export const getImageEditorOptions = (mode: 'avatar' | 'cover' | 'attachment' | undefined): ImageEditorOptions => { - const aspectRatio = mode === 'cover' ? 3 / 1 : 1; // Default to 1 for 'avatar' and undefined modes - - return { + const options: ImageEditorOptions = { quality: 0.9, actions: baseActions, - cropperOptions: { - ...baseCropperOptions, - aspectRatio, - }, + cropperOptions: baseCropperOptions, }; + + if (!options.cropperOptions) return options; + + if (mode === 'cover') options.cropperOptions.aspectRatio = 3 / 1; + if (mode === 'avatar') options.cropperOptions.aspectRatio = 1; + + return options; }; From 46499dd504ce35cf9669b018534d11274c94e074 Mon Sep 17 00:00:00 2001 From: David Hordiienko Date: Wed, 6 Nov 2024 11:03:37 +0200 Subject: [PATCH 08/36] implement: AttachmentsTable thumbnail click open carousel --- .../query-client-provider/attachments.ts | 3 +- .../attachments-table/columns.tsx | 12 +- .../organizations/attachments-table/index.tsx | 185 ++++++++++-------- 3 files changed, 112 insertions(+), 88 deletions(-) diff --git a/frontend/src/modules/common/query-client-provider/attachments.ts b/frontend/src/modules/common/query-client-provider/attachments.ts index 2ea0e37f2..feab17e4b 100644 --- a/frontend/src/modules/common/query-client-provider/attachments.ts +++ b/frontend/src/modules/common/query-client-provider/attachments.ts @@ -94,6 +94,7 @@ queryClient.setMutationDefaults(attachmentKeys.create(), { { ...previousData.pages[0], items: [newAttachment, ...previousData.pages[0].items], + total: previousData.pages[0].total + 1, }, ...previousData.pages.slice(1), ], @@ -135,7 +136,7 @@ queryClient.setMutationDefaults(attachmentKeys.delete(), { const updatedItems = items.filter((item) => !ids.includes(item.id)); return { ...oldData, - pages: [{ ...oldData.pages[0], items: updatedItems }, ...oldData.pages.slice(1)], + pages: [{ ...oldData.pages[0], items: updatedItems, total: updatedItems.length }, ...oldData.pages.slice(1)], }; }); diff --git a/frontend/src/modules/organizations/attachments-table/columns.tsx b/frontend/src/modules/organizations/attachments-table/columns.tsx index b2e341808..59f666e88 100644 --- a/frontend/src/modules/organizations/attachments-table/columns.tsx +++ b/frontend/src/modules/organizations/attachments-table/columns.tsx @@ -12,7 +12,13 @@ import { Button } from '~/modules/ui/button'; import { Input } from '~/modules/ui/input'; import { dateShort } from '~/utils/date-short'; -export const useColumns = (t: TFunction<'translation', undefined>, isMobile: boolean, isAdmin: boolean, isSheet: boolean) => { +export const useColumns = ( + t: TFunction<'translation', undefined>, + isMobile: boolean, + isAdmin: boolean, + isSheet: boolean, + openCarouselDialog: (open: boolean, slide: number) => void, +) => { const columns: ColumnOrColumnGroup[] = [ ...(isAdmin ? [CheckboxColumn] : []), { @@ -21,8 +27,8 @@ export const useColumns = (t: TFunction<'translation', undefined>, isMobile: boo visible: true, sortable: false, width: 32, - renderCell: ({ row }) => ( -
+ renderCell: ({ row, rowIdx }) => ( +
openCarouselDialog(true, rowIdx)} onKeyDown={() => {}} className="cursor-pointer w-full flex justify-center items-center"> {row.filename}
), diff --git a/frontend/src/modules/organizations/attachments-table/index.tsx b/frontend/src/modules/organizations/attachments-table/index.tsx index 5297cbf9a..f7cface34 100644 --- a/frontend/src/modules/organizations/attachments-table/index.tsx +++ b/frontend/src/modules/organizations/attachments-table/index.tsx @@ -15,6 +15,7 @@ import useMapQueryDataToRows from '~/hooks/use-map-query-data-to-rows'; import useSaveInSearchParams from '~/hooks/use-save-in-search-params'; import { queryClient } from '~/lib/router'; import { showToast } from '~/lib/toasts'; +import CarouselDialog from '~/modules/common/carousel-dialog'; import { DataTable } from '~/modules/common/data-table'; import type { ColumnOrColumnGroup } from '~/modules/common/data-table/columns-view'; import ColumnsView from '~/modules/common/data-table/columns-view'; @@ -84,6 +85,8 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro const [q, setQuery] = useState(search.q); const [sortColumns, setSortColumns] = useState(getInitialSortColumns(search)); const [totalCount, setTotalCount] = useState(0); + const [carouselOpen, setCarouselOpen] = useState(false); + const [carouselSlide, setCarouselSlide] = useState(0); // Search query options const sort = sortColumns[0]?.columnKey as AttachmentSearch['sort']; @@ -105,9 +108,14 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro }), ); + const openCarouselDialog = (open: boolean, slide: number) => { + setCarouselOpen(open); + setCarouselSlide(slide); + }; + // Build columns const [columns, setColumns] = useState[]>([]); - useMemo(() => setColumns(useColumns(t, isMobile, isAdmin, isSheet)), [isAdmin]); + useMemo(() => setColumns(useColumns(t, isMobile, isAdmin, isSheet, openCarouselDialog)), [isAdmin]); // Map (updated) query data to rows useMapQueryDataToRows({ queryResult, setSelectedRows, setRows, selectedRows, setTotalCount }); @@ -332,90 +340,99 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro }, [networkMode]); return ( -
-
- {/* Filter bar */} - - - {selected.length > 0 ? ( - <> - - - - - ) : ( - !isFiltered && - isAdmin && ( - - ) - )} - {selected.length === 0 && } - -
- - - - - - {/* Columns view */} - - - {/* Focus view */} - {!isSheet && } -
- - {/* Data table */} - - {...{ - columns: columns.filter((column) => column.visible), - rowHeight: 42, - enableVirtualization: false, - onRowsChange, - rows, - limit, - totalCount, - rowKeyGetter: (row) => row.id, - error: queryResult.error, - isLoading: queryResult.isLoading, - isFetching: queryResult.isFetching, - fetchMore: queryResult.fetchNextPage, - isFiltered, - selectedRows, - onSelectedRowsChange: setSelectedRows, - sortColumns, - onSortColumnsChange: setSortColumns, - }} + <> + ({ src: el.url }))} + carouselSlide={carouselSlide} /> -
+
+
+ {/* Filter bar */} + + + {selected.length > 0 ? ( + <> + + + + + ) : ( + !isFiltered && + isAdmin && ( + + ) + )} + {selected.length === 0 && } + +
+ + + + + + {/* Columns view */} + + + {/* Focus view */} + {!isSheet && } +
+ + {/* Data table */} + + {...{ + columns: columns.filter((column) => column.visible), + rowHeight: 42, + enableVirtualization: false, + onRowsChange, + rows, + limit, + totalCount, + rowKeyGetter: (row) => row.id, + error: queryResult.error, + isLoading: queryResult.isLoading, + isFetching: queryResult.isFetching, + fetchMore: queryResult.fetchNextPage, + isFiltered, + selectedRows, + onSelectedRowsChange: setSelectedRows, + sortColumns, + onSortColumnsChange: setSortColumns, + }} + /> +
+ ); }; From f0d6675b0da2e64cd2c3a1ac5f8bb7e187773576 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 6 Nov 2024 12:13:16 +0200 Subject: [PATCH 09/36] refactor: Optional names for columns --- backend/src/db/schema/attachments.ts | 12 ++++++------ backend/src/db/schema/memberships.ts | 10 +++++----- backend/src/db/schema/organizations.ts | 16 ++++++++-------- backend/src/db/schema/passkeys.ts | 2 +- backend/src/db/schema/requests.ts | 8 ++++---- backend/src/db/schema/sessions.ts | 6 +++--- backend/src/db/schema/tokens.ts | 8 ++++---- backend/src/db/schema/users.ts | 16 ++++++++-------- 8 files changed, 39 insertions(+), 39 deletions(-) diff --git a/backend/src/db/schema/attachments.ts b/backend/src/db/schema/attachments.ts index f204ac25c..1bf55b67b 100644 --- a/backend/src/db/schema/attachments.ts +++ b/backend/src/db/schema/attachments.ts @@ -4,15 +4,15 @@ import { organizationsTable } from './organizations'; import { usersTable } from './users'; export const attachmentsTable = pgTable('attachments', { - id: varchar('id').primaryKey().$defaultFn(nanoid), - name: varchar('name').notNull().default('attachment'), - filename: varchar('filename').notNull(), + id: varchar().primaryKey().$defaultFn(nanoid), + name: varchar().notNull().default('attachment'), + filename: varchar().notNull(), contentType: varchar('content_type').notNull(), - size: varchar('size').notNull(), - entity: varchar('entity', { enum: ['attachment'] }) + size: varchar().notNull(), + entity: varchar({ enum: ['attachment'] }) .notNull() .default('attachment'), - url: varchar('url').notNull(), + url: varchar().notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), createdBy: varchar('created_by').references(() => usersTable.id, { onDelete: 'set null', diff --git a/backend/src/db/schema/memberships.ts b/backend/src/db/schema/memberships.ts index 3405f31bb..606a06ffb 100644 --- a/backend/src/db/schema/memberships.ts +++ b/backend/src/db/schema/memberships.ts @@ -7,18 +7,18 @@ import { organizationsTable } from './organizations'; const roleEnum = config.rolesByType.entityRoles; export const membershipsTable = pgTable('memberships', { - id: varchar('id').primaryKey().$defaultFn(nanoid), - type: varchar('type', { enum: config.contextEntityTypes }).notNull(), + id: varchar().primaryKey().$defaultFn(nanoid), + type: varchar({ enum: config.contextEntityTypes }).notNull(), userId: varchar('user_id') .notNull() .references(() => usersTable.id, { onDelete: 'cascade' }), - role: varchar('role', { enum: roleEnum }).notNull().default('member'), + role: varchar({ enum: roleEnum }).notNull().default('member'), createdAt: timestamp('created_at').defaultNow().notNull(), createdBy: varchar('created_by').references(() => usersTable.id, { onDelete: 'set null' }), modifiedAt: timestamp('modified_at'), modifiedBy: varchar('modified_by').references(() => usersTable.id, { onDelete: 'set null' }), - archived: boolean('archived').default(false).notNull(), - muted: boolean('muted').default(false).notNull(), + archived: boolean().default(false).notNull(), + muted: boolean().default(false).notNull(), order: doublePrecision('sort_order').notNull(), organizationId: varchar('organization_id') .notNull() diff --git a/backend/src/db/schema/organizations.ts b/backend/src/db/schema/organizations.ts index cdb094198..01acb1cc6 100644 --- a/backend/src/db/schema/organizations.ts +++ b/backend/src/db/schema/organizations.ts @@ -10,20 +10,20 @@ const languages = config.languages.map((lang) => lang.value) as [string, ...stri export const organizationsTable = pgTable( 'organizations', { - id: varchar('id').primaryKey().$defaultFn(nanoid), - entity: varchar('entity', { enum: ['organization'] }) + id: varchar().primaryKey().$defaultFn(nanoid), + entity: varchar({ enum: ['organization'] }) .notNull() .default('organization'), - name: varchar('name').notNull(), + name: varchar().notNull(), shortName: varchar('short_name'), - slug: varchar('slug').unique().notNull(), - country: varchar('country'), - timezone: varchar('timezone'), + slug: varchar().unique().notNull(), + country: varchar(), + timezone: varchar(), defaultLanguage: varchar('default_language', { enum: languages }).notNull().default(config.defaultLanguage), - languages: json('languages').$type().notNull().default([config.defaultLanguage]), + languages: json().$type().notNull().default([config.defaultLanguage]), notificationEmail: varchar('notification_email'), emailDomains: json('email_domains').$type().notNull().default([]), - color: varchar('color'), + color: varchar(), thumbnailUrl: varchar('thumbnail_url'), bannerUrl: varchar('banner_url'), logoUrl: varchar('logo_url'), diff --git a/backend/src/db/schema/passkeys.ts b/backend/src/db/schema/passkeys.ts index b2f17ecd0..e0b070611 100644 --- a/backend/src/db/schema/passkeys.ts +++ b/backend/src/db/schema/passkeys.ts @@ -3,7 +3,7 @@ import { usersTable } from '#/db/schema/users'; import { nanoid } from '#/utils/nanoid'; export const passkeysTable = pgTable('passkeys', { - id: varchar('id').primaryKey().$defaultFn(nanoid), + id: varchar().primaryKey().$defaultFn(nanoid), userEmail: varchar('user_email') .notNull() .references(() => usersTable.email, { onDelete: 'cascade' }), diff --git a/backend/src/db/schema/requests.ts b/backend/src/db/schema/requests.ts index f51627406..f7c0a329d 100644 --- a/backend/src/db/schema/requests.ts +++ b/backend/src/db/schema/requests.ts @@ -7,10 +7,10 @@ export type RequestType = (typeof requestTypeEnum)[number]; export const requestsTable = pgTable( 'requests', { - id: varchar('id').primaryKey().$defaultFn(nanoid), - message: varchar('message'), - email: varchar('email').notNull(), - type: varchar('type', { enum: requestTypeEnum }).notNull(), + id: varchar().primaryKey().$defaultFn(nanoid), + message: varchar(), + email: varchar().notNull(), + type: varchar({ enum: requestTypeEnum }).notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), }, (table) => { diff --git a/backend/src/db/schema/sessions.ts b/backend/src/db/schema/sessions.ts index 6128297e7..cd0059933 100644 --- a/backend/src/db/schema/sessions.ts +++ b/backend/src/db/schema/sessions.ts @@ -4,7 +4,7 @@ import { usersTable } from '#/db/schema/users'; export const sessionsTable = pgTable( 'sessions', { - id: varchar('id').primaryKey(), + id: varchar().primaryKey(), userId: varchar('user_id') .notNull() .references(() => usersTable.id, { onDelete: 'cascade' }), @@ -13,11 +13,11 @@ export const sessionsTable = pgTable( .notNull() .default('desktop'), deviceOs: varchar('device_os'), - browser: varchar('browser'), + browser: varchar(), authStrategy: varchar('auth_strategy', { enum: ['github', 'google', 'microsoft', 'password', 'passkey'], }), - type: varchar('type', { + type: varchar({ enum: ['regular', 'impersonation'], }) .notNull() diff --git a/backend/src/db/schema/tokens.ts b/backend/src/db/schema/tokens.ts index 2a9752684..105b417e7 100644 --- a/backend/src/db/schema/tokens.ts +++ b/backend/src/db/schema/tokens.ts @@ -7,10 +7,10 @@ const tokenTypeEnum = ['email_verification', 'password_reset', 'system_invitatio const roleEnum = config.rolesByType.allRoles; export const tokensTable = pgTable('tokens', { - id: varchar('id').primaryKey(), - type: varchar('type', { enum: tokenTypeEnum }).notNull(), - email: varchar('email'), - role: varchar('role', { enum: roleEnum }), + id: varchar().primaryKey(), + type: varchar({ enum: tokenTypeEnum }).notNull(), + email: varchar(), + role: varchar({ enum: roleEnum }), userId: varchar('user_id').references(() => usersTable.id, { onDelete: 'cascade' }), organizationId: varchar('organization_id').references(() => organizationsTable.id, { onDelete: 'cascade' }), createdAt: timestamp('created_at').defaultNow().notNull(), diff --git a/backend/src/db/schema/users.ts b/backend/src/db/schema/users.ts index 1611580c9..1753ab353 100644 --- a/backend/src/db/schema/users.ts +++ b/backend/src/db/schema/users.ts @@ -7,27 +7,27 @@ const roleEnum = config.rolesByType.systemRoles; export const usersTable = pgTable( 'users', { - id: varchar('id').primaryKey(), - entity: varchar('entity', { enum: ['user'] }) + id: varchar().primaryKey(), + entity: varchar({ enum: ['user'] }) .notNull() .default('user'), hashedPassword: varchar('hashed_password'), - slug: varchar('slug').unique().notNull(), + slug: varchar().unique().notNull(), unsubscribeToken: varchar('unsubscribe_token').unique().notNull(), - name: varchar('name').notNull(), + name: varchar().notNull(), firstName: varchar('first_name'), lastName: varchar('last_name'), - email: varchar('email').notNull().unique(), + email: varchar().notNull().unique(), emailVerified: boolean('email_verified').notNull().default(false), - bio: varchar('bio'), - language: varchar('language', { + bio: varchar(), + language: varchar({ enum: ['en', 'nl'], }) .notNull() .default(config.defaultLanguage), bannerUrl: varchar('banner_url'), thumbnailUrl: varchar('thumbnail_url'), - newsletter: boolean('newsletter').notNull().default(false), + newsletter: boolean().notNull().default(false), lastSeenAt: timestamp('last_seen_at'), // last time any GET request has been made lastStartedAt: timestamp('last_started_at'), // last time GET me lastSignInAt: timestamp('last_sign_in_at'), // last time user went through authentication flow From 7bbee205b7f680f163c4b068d64c95f8ed8b462c Mon Sep 17 00:00:00 2001 From: David Hordiienko Date: Wed, 6 Nov 2024 14:43:55 +0200 Subject: [PATCH 10/36] improve: attachment carousel & attachment item preview --- .npmrc | 1 + frontend/package.json | 3 + frontend/src/index.css | 4 + .../common/attachments/audio-preview.tsx | 8 + .../src/modules/common/attachments/index.tsx | 38 +++ .../common/attachments/pdf-preview.tsx | 20 ++ .../common/attachments/video-preview.tsx | 8 + .../src/modules/common/carousel-dialog.tsx | 2 +- frontend/src/modules/common/carousel.tsx | 26 +- .../modules/organizations/attachment-page.tsx | 8 +- .../attachments-table/attachment-preview.tsx | 34 +++ .../attachments-table/columns.tsx | 7 +- .../organizations/attachments-table/index.tsx | 4 +- locales/en/common.json | 1 + pnpm-lock.yaml | 276 +++++++++++++++++- 15 files changed, 419 insertions(+), 21 deletions(-) create mode 100644 frontend/src/modules/common/attachments/audio-preview.tsx create mode 100644 frontend/src/modules/common/attachments/index.tsx create mode 100644 frontend/src/modules/common/attachments/pdf-preview.tsx create mode 100644 frontend/src/modules/common/attachments/video-preview.tsx create mode 100644 frontend/src/modules/organizations/attachments-table/attachment-preview.tsx diff --git a/.npmrc b/.npmrc index e27836004..1a6740025 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ package-import-method=clone-or-copy +public-hoist-pattern[]=pdfjs-dist \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index eaf97f53e..dbcfe575a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -95,6 +95,8 @@ "locales": "workspace:*", "lucide-react": "^0.447.0", "nanoid": "^5.0.8", + "pdfjs-dist": "4.4.168", + "player.style": "^0.0.12", "react": "^18.3.1", "react-confetti-explosion": "^2.1.2", "react-data-grid": "7.0.0-beta.47", @@ -105,6 +107,7 @@ "react-i18next": "^15.1.0", "react-intersection-observer": "^9.13.1", "react-lazy-with-preload": "^2.2.1", + "react-pdf": "^9.1.1", "react-resizable-panels": "^2.1.6", "recharts": "^2.13.2", "slugify": "1.6.6", diff --git a/frontend/src/index.css b/frontend/src/index.css index bfbc41cd7..6ab5105f7 100755 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -9,6 +9,9 @@ @layer base { .bn-container.bn-shadcn, :root { + --base: 14px; + + --media-secondary-color: 240 5% 92%; --bloknote-placeholder: 240 6% 92%; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; @@ -41,6 +44,7 @@ .bn-container.bn-shadcn.dark, .dark { + --media-secondary-color: 240 3.7% 15%; --bloknote-placeholder: 240 3.7% 20%; --chart-1: 220 70% 50%; --chart-2: 160 60% 45%; diff --git a/frontend/src/modules/common/attachments/audio-preview.tsx b/frontend/src/modules/common/attachments/audio-preview.tsx new file mode 100644 index 000000000..0df3afe49 --- /dev/null +++ b/frontend/src/modules/common/attachments/audio-preview.tsx @@ -0,0 +1,8 @@ +import MediaThemeSutroAudio from 'player.style/sutro-audio/react'; + +export const AudioPreview = ({ src }: { src: string }) => ( + + {/* biome-ignore lint/a11y/useMediaCaption: by author */} + +); diff --git a/frontend/src/modules/common/attachments/index.tsx b/frontend/src/modules/common/attachments/index.tsx new file mode 100644 index 000000000..8a1e8628b --- /dev/null +++ b/frontend/src/modules/common/attachments/index.tsx @@ -0,0 +1,38 @@ +import { AudioPreview } from '~/modules/common/attachments/audio-preview'; +import PreviewPDF from '~/modules/common/attachments/pdf-preview'; +import { VideoPreview } from '~/modules/common/attachments/video-preview'; +import ReactPanZoom from '~/modules/common/image-viewer'; + +interface AttachmentItemProps { + type: string; + source: string; + altName?: string; + imagePanZoom?: boolean; + showButtons?: boolean; + itemClassName?: string; + containerClassName?: string; +} + +export const AttachmentItem = ({ + source, + type, + altName, + showButtons, + imagePanZoom = false, + itemClassName, + containerClassName, +}: AttachmentItemProps) => { + return ( +
+ {type.includes('image') && + (imagePanZoom ? ( + + ) : ( + {altName} + ))} + {type.includes('audio') && } + {type.includes('video') && } + {type.includes('pdf') && } +
+ ); +}; diff --git a/frontend/src/modules/common/attachments/pdf-preview.tsx b/frontend/src/modules/common/attachments/pdf-preview.tsx new file mode 100644 index 000000000..fcd7a3b6f --- /dev/null +++ b/frontend/src/modules/common/attachments/pdf-preview.tsx @@ -0,0 +1,20 @@ +import { Document, pdfjs } from 'react-pdf'; +import 'react-pdf/dist/esm/Page/TextLayer.css'; + +pdfjs.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString(); + +const options = { cMapUrl: '/cmaps/' }; + +type PDFFile = string | File | null; + +export default function PreviewPDF({ file, className }: { file: PDFFile; className?: string }) { + // const [numPages, setNumPages] = useState(); + + return ( + + {/* {Array.from(new Array(numPages), (_el, index) => ( + + ))} */} + + ); +} diff --git a/frontend/src/modules/common/attachments/video-preview.tsx b/frontend/src/modules/common/attachments/video-preview.tsx new file mode 100644 index 000000000..e882079dc --- /dev/null +++ b/frontend/src/modules/common/attachments/video-preview.tsx @@ -0,0 +1,8 @@ +import MediaThemeSutro from 'player.style/sutro/react'; + +export const VideoPreview = ({ src }: { src: string }) => ( + + {/* biome-ignore lint/a11y/useMediaCaption: by author */} + +); diff --git a/frontend/src/modules/common/carousel-dialog.tsx b/frontend/src/modules/common/carousel-dialog.tsx index 45818722d..4429d47c4 100644 --- a/frontend/src/modules/common/carousel-dialog.tsx +++ b/frontend/src/modules/common/carousel-dialog.tsx @@ -7,7 +7,7 @@ interface CarouselDialogProps { isOpen: boolean; title: string; carouselSlide: number; - slides?: { src: string }[]; + slides?: { src: string; fileType?: string }[]; onOpenChange: (open: boolean) => void; } diff --git a/frontend/src/modules/common/carousel.tsx b/frontend/src/modules/common/carousel.tsx index 8a92866b7..6ddf629b5 100644 --- a/frontend/src/modules/common/carousel.tsx +++ b/frontend/src/modules/common/carousel.tsx @@ -1,18 +1,18 @@ import Autoplay from 'embla-carousel-autoplay'; import { useState } from 'react'; -import ReactPanZoom from '~/modules/common/image-viewer'; import { Carousel as BaseCarousel, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious } from '~/modules/ui/carousel'; +import { AttachmentItem } from './attachments'; interface CarouselProps { slide?: number; - slides?: { src: string }[]; + slides?: { src: string; fileType?: string }[]; isDialog?: boolean; onOpenChange: (open: boolean, slide?: number) => void; } const Carousel = ({ slides, onOpenChange, isDialog = false, slide = 0 }: CarouselProps) => { const [current, setCurrent] = useState(0); - const imageClass = isDialog ? 'object-contain' : ''; + const itemClass = isDialog ? 'object-contain' : ''; const autoplay = Autoplay({ delay: 4000, stopOnInteraction: true, stopOnMouseEnter: true }); return ( @@ -28,16 +28,18 @@ const Carousel = ({ slides, onOpenChange, isDialog = false, slide = 0 }: Carouse }} > - {slides?.map((slide, idx) => { + {slides?.map(({ src, fileType = 'image' }, idx) => { return ( - onOpenChange(true, idx)}> -
- {isDialog ? ( - - ) : ( - {`Slide - )} -
+ onOpenChange(true, idx)}> + ); })} diff --git a/frontend/src/modules/organizations/attachment-page.tsx b/frontend/src/modules/organizations/attachment-page.tsx index e077d530d..011d35094 100644 --- a/frontend/src/modules/organizations/attachment-page.tsx +++ b/frontend/src/modules/organizations/attachment-page.tsx @@ -1,6 +1,7 @@ import { queryOptions, useSuspenseQuery } from '@tanstack/react-query'; import { useParams } from '@tanstack/react-router'; import { getAttachment } from '~/api/attachments'; +import { AttachmentItem } from '~/modules/common/attachments'; import { AttachmentRoute } from '~/routes/attachments'; export const attachmentQueryOptions = (orgIdOrSlug: string, id: string) => @@ -16,7 +17,12 @@ const AttachmentPage = () => { return (

{attachment.filename}

- {attachment.filename} +
); }; diff --git a/frontend/src/modules/organizations/attachments-table/attachment-preview.tsx b/frontend/src/modules/organizations/attachments-table/attachment-preview.tsx new file mode 100644 index 000000000..414380c7f --- /dev/null +++ b/frontend/src/modules/organizations/attachments-table/attachment-preview.tsx @@ -0,0 +1,34 @@ +import { File, FileAudio, FileText, FileVideo } from 'lucide-react'; +import type React from 'react'; + +interface AttachmentPreviewIconProps { + url: string; + contentType: string; + name: string; + openCarouselDialog: () => void; +} + +const AttachmentPreviewIcon = ({ url, contentType, name, openCarouselDialog }: AttachmentPreviewIconProps) => { + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key !== 'Enter') return; + openCarouselDialog(); + }; + + const renderAttachmentPreviewIcon = (iconSize = 20) => { + if (contentType.includes('image')) return {name}; + + if (contentType.includes('video')) return ; + if (contentType.includes('pdf')) return ; + if (contentType.includes('audio')) return ; + + return ; + }; + + return ( +
+ {renderAttachmentPreviewIcon()} +
+ ); +}; + +export default AttachmentPreviewIcon; diff --git a/frontend/src/modules/organizations/attachments-table/columns.tsx b/frontend/src/modules/organizations/attachments-table/columns.tsx index 59f666e88..4c5edf602 100644 --- a/frontend/src/modules/organizations/attachments-table/columns.tsx +++ b/frontend/src/modules/organizations/attachments-table/columns.tsx @@ -8,6 +8,7 @@ import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard'; import CheckboxColumn from '~/modules/common/data-table/checkbox-column'; import type { ColumnOrColumnGroup } from '~/modules/common/data-table/columns-view'; import HeaderCell from '~/modules/common/data-table/header-cell'; +import AttachmentPreviewIcon from '~/modules/organizations/attachments-table/attachment-preview'; import { Button } from '~/modules/ui/button'; import { Input } from '~/modules/ui/input'; import { dateShort } from '~/utils/date-short'; @@ -27,10 +28,8 @@ export const useColumns = ( visible: true, sortable: false, width: 32, - renderCell: ({ row, rowIdx }) => ( -
openCarouselDialog(true, rowIdx)} onKeyDown={() => {}} className="cursor-pointer w-full flex justify-center items-center"> - {row.filename} -
+ renderCell: ({ row: { url, filename, contentType }, rowIdx }) => ( + openCarouselDialog(true, rowIdx)} contentType={contentType} /> ), }, { diff --git a/frontend/src/modules/organizations/attachments-table/index.tsx b/frontend/src/modules/organizations/attachments-table/index.tsx index f7cface34..f80028097 100644 --- a/frontend/src/modules/organizations/attachments-table/index.tsx +++ b/frontend/src/modules/organizations/attachments-table/index.tsx @@ -342,10 +342,10 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro return ( <> ({ src: el.url }))} + slides={rows.map((el) => ({ src: el.url, fileType: el.contentType }))} carouselSlide={carouselSlide} />
diff --git a/locales/en/common.json b/locales/en/common.json index 48eddaefb..1b0350efd 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -432,6 +432,7 @@ "verify_my_email": "Verify my email address", "view": "View", "view_attachment": "View attachment", + "view_attachment_of": "View attachments of {{name}}", "view_options": "View options", "view_profile": "View profile", "view_screenshot": "View screenshot", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb66028f3..9e1608b95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -531,6 +531,12 @@ importers: nanoid: specifier: ^5.0.8 version: 5.0.8 + pdfjs-dist: + specifier: 4.4.168 + version: 4.4.168 + player.style: + specifier: ^0.0.12 + version: 0.0.12 react: specifier: ^18.3.1 version: 18.3.1 @@ -561,6 +567,9 @@ importers: react-lazy-with-preload: specifier: ^2.2.1 version: 2.2.1 + react-pdf: + specifier: ^9.1.1 + version: 9.1.1(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-resizable-panels: specifier: ^2.1.6 version: 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2800,7 +2809,6 @@ packages: '@evilmartians/lefthook@1.8.2': resolution: {integrity: sha512-SZdQk3W9q7tcJwnSwEMUubQqVIK7SHxv52hEAnV7o3nPI+xKcmd+rN0hZIJg07wjBaJRAjzdvoQySKQQYPW5Qw==} - cpu: [x64, arm64, ia32] os: [darwin, linux, win32] hasBin: true @@ -3030,6 +3038,10 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + '@microsoft/tsdoc@0.15.0': resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} @@ -6008,6 +6020,9 @@ packages: '@xstate/fsm@1.6.5': resolution: {integrity: sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==} + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -6063,9 +6078,17 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + arctic@2.2.1: resolution: {integrity: sha512-ZaHREgEur4APnlhm7uQwIRFVMjr+z1YPD2O8qpqndPt7m1AW/BeT23nNn+xfoo2iAYx57A8ehq7ACiDQmgIGSw==} + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -6326,6 +6349,10 @@ packages: caniuse-lite@1.0.30001676: resolution: {integrity: sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==} + canvas@2.11.2: + resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} + engines: {node: '>=6'} + canvg@3.0.10: resolution: {integrity: sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==} engines: {node: '>=10.0.0'} @@ -6456,6 +6483,10 @@ packages: color-string@1.9.1: resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + color@4.2.3: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} @@ -6527,6 +6558,9 @@ packages: console-browserify@1.2.0: resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + constants-browserify@1.0.0: resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} @@ -6716,6 +6750,10 @@ packages: decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + decompress-response@4.2.1: + resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} + engines: {node: '>=8'} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -6747,6 +6785,9 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -7321,6 +7362,11 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + generic-pool@3.9.0: resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} engines: {node: '>= 4'} @@ -7458,6 +7504,9 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + has-yarn@2.1.0: resolution: {integrity: sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==} engines: {node: '>=8'} @@ -8254,6 +8303,9 @@ packages: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} + make-cancellable-promise@1.3.2: + resolution: {integrity: sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==} + make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -8261,6 +8313,9 @@ packages: make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + make-event-props@1.6.2: + resolution: {integrity: sha512-iDwf7mA03WPiR8QxvcVHmVWEPfMY1RZXerDVNCRYW7dUr2ppH3J58Rwb39/WG39yTZdRSxr3x+2v22tvI0VEvA==} + markdown-it@14.1.0: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true @@ -8326,6 +8381,9 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + media-chrome@4.2.3: + resolution: {integrity: sha512-gzwFy2b+RLsEtnPzUzqzf2L5XkaTLQr8POOyLOcoebWSAWg31cPy2vfXNiUnd93sc5IxwJ8OAwkKxnaJNZ8Gjg==} + memfs-browser@3.5.10302: resolution: {integrity: sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==} @@ -8336,6 +8394,14 @@ packages: memoize-one@6.0.0: resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + merge-refs@1.3.0: + resolution: {integrity: sha512-nqXPXbso+1dcKDpPCXvwZyJILz+vSLqGGOnDrYHQYE+B8n9JTCekVLC65AfCpR4ggVyA/45Y0iR9LDyS2iI+zA==} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -8469,6 +8535,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + mimic-response@2.1.0: + resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} + engines: {node: '>=8'} + mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -8566,6 +8636,9 @@ packages: namespace-emitter@2.0.1: resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==} + nan@2.22.0: + resolution: {integrity: sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -8621,6 +8694,11 @@ packages: resolution: {integrity: sha512-dZezG3D88Lg22DwyjsDuUs7cCT/XGr8WwJgg/S3ZnkcWuPet2Tt/W1d2Eytb1Z73JpZv+XVCDI5TWv6UMRq0Gg==} engines: {node: '>=10'} + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + nopt@7.2.1: resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -8642,6 +8720,10 @@ packages: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -8825,6 +8907,10 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + path2d@0.2.1: + resolution: {integrity: sha512-Fl2z/BHvkTNvkuBzYTpTuirHZg6wW9z8+4SND/3mDTEcYbbNKWAy21dz9D3ePNNwrrK8pqZO5vLPZ1hLF6T7XA==} + engines: {node: '>=6'} + pathe@0.2.0: resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} @@ -8835,6 +8921,10 @@ packages: resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} engines: {node: '>=0.12'} + pdfjs-dist@4.4.168: + resolution: {integrity: sha512-MbkAjpwka/dMHaCfQ75RY1FXX3IewBVu6NGZOcxerRFlaBiIkZmUoR0jotX5VUzYZEXAGzSFtknWs5xRKliXPA==} + engines: {node: '>=18'} + peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} @@ -8918,6 +9008,9 @@ packages: pkg-types@1.2.0: resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} + player.style@0.0.12: + resolution: {integrity: sha512-vYOa9LJGkaEhlq9648f9FFhvQP8UwzmoBWnazKpxgqGjEyH4HSSKPc3Nx2+k+cSX6AXKtjAuN08dKjP3YC1Htg==} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -9476,6 +9569,16 @@ packages: react-lazy-with-preload@2.2.1: resolution: {integrity: sha512-ONSb8gizLE5jFpdHAclZ6EAAKuFX2JydnFXPPPjoUImZlLjGtKzyBS8SJgJq7CpLgsGKh9QCZdugJyEEOVC16Q==} + react-pdf@9.1.1: + resolution: {integrity: sha512-Cn3RTJZMqVOOCgLMRXDamLk4LPGfyB2Np3OwQAUjmHIh47EpuGW1OpAA1Z1GVDLoHx4d5duEDo/YbUkDbr4QFQ==} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -9778,6 +9881,11 @@ packages: resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==} engines: {node: '>= 0.8.15'} + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rimraf@6.0.1: resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} engines: {node: 20 || >=22} @@ -9891,6 +9999,9 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -9951,6 +10062,9 @@ packages: simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + simple-get@3.1.1: + resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} + simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} @@ -10784,6 +10898,9 @@ packages: w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -10824,6 +10941,9 @@ packages: engines: {node: '>= 8'} hasBin: true + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + widest-line@3.1.0: resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} engines: {node: '>=8'} @@ -13631,6 +13751,22 @@ snapshots: '@lukeed/csprng@1.1.0': {} + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.6.3 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + '@microsoft/tsdoc@0.15.0': {} '@million/install@1.0.11': @@ -17034,6 +17170,9 @@ snapshots: '@xstate/fsm@1.6.5': {} + abbrev@1.1.1: + optional: true + abbrev@2.0.0: {} acorn-import-attributes@1.9.5(acorn@8.14.0): @@ -17085,12 +17224,21 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + aproba@2.0.0: + optional: true + arctic@2.2.1: dependencies: '@oslojs/crypto': 1.0.1 '@oslojs/encoding': 1.1.0 '@oslojs/jwt': 0.2.0 + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + optional: true + arg@4.1.3: optional: true @@ -17413,6 +17561,16 @@ snapshots: caniuse-lite@1.0.30001676: {} + canvas@2.11.2: + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + nan: 2.22.0 + simple-get: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + canvg@3.0.10: dependencies: '@babel/runtime': 7.26.0 @@ -17556,6 +17714,9 @@ snapshots: simple-swizzle: 0.2.2 optional: true + color-support@1.1.3: + optional: true + color@4.2.3: dependencies: color-convert: 2.0.1 @@ -17619,6 +17780,9 @@ snapshots: console-browserify@1.2.0: {} + console-control-strings@1.1.0: + optional: true + constants-browserify@1.0.0: {} convert-source-map@2.0.0: {} @@ -17828,6 +17992,11 @@ snapshots: dependencies: character-entities: 2.0.2 + decompress-response@4.2.1: + dependencies: + mimic-response: 2.1.0 + optional: true + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -17855,6 +18024,9 @@ snapshots: delayed-stream@1.0.0: {} + delegates@1.0.0: + optional: true + dequal@2.0.3: {} des.js@1.1.0: @@ -18559,6 +18731,19 @@ snapshots: functions-have-names@1.2.3: {} + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + optional: true + generic-pool@3.9.0: optional: true @@ -18725,6 +18910,9 @@ snapshots: dependencies: has-symbols: 1.0.3 + has-unicode@2.0.1: + optional: true + has-yarn@2.1.0: {} hash-base@3.0.4: @@ -19725,6 +19913,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + make-cancellable-promise@1.3.2: {} + make-dir@3.1.0: dependencies: semver: 6.3.1 @@ -19732,6 +19922,8 @@ snapshots: make-error@1.3.6: optional: true + make-event-props@1.6.2: {} + markdown-it@14.1.0: dependencies: argparse: 2.0.1 @@ -19875,6 +20067,8 @@ snapshots: mdurl@2.0.0: {} + media-chrome@4.2.3: {} + memfs-browser@3.5.10302: dependencies: memfs: 3.5.3 @@ -19887,6 +20081,10 @@ snapshots: memoize-one@6.0.0: {} + merge-refs@1.3.0(@types/react@18.3.12): + optionalDependencies: + '@types/react': 18.3.12 + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -20123,6 +20321,9 @@ snapshots: mimic-fn@4.0.0: {} + mimic-response@2.1.0: + optional: true + mimic-response@3.1.0: optional: true @@ -20208,6 +20409,9 @@ snapshots: namespace-emitter@2.0.1: {} + nan@2.22.0: + optional: true + nanoid@3.3.7: {} nanoid@5.0.8: {} @@ -20279,6 +20483,11 @@ snapshots: util: 0.12.5 vm-browserify: 1.1.2 + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + optional: true + nopt@7.2.1: dependencies: abbrev: 2.0.0 @@ -20295,6 +20504,14 @@ snapshots: dependencies: path-key: 4.0.0 + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + optional: true + nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -20470,6 +20687,9 @@ snapshots: path-type@4.0.0: {} + path2d@0.2.1: + optional: true + pathe@0.2.0: {} pathe@1.1.2: {} @@ -20482,6 +20702,14 @@ snapshots: safe-buffer: 5.2.1 sha.js: 2.4.11 + pdfjs-dist@4.4.168: + optionalDependencies: + canvas: 2.11.2 + path2d: 0.2.1 + transitivePeerDependencies: + - encoding + - supports-color + peberminta@0.9.0: {} performance-now@2.1.0: @@ -20562,6 +20790,10 @@ snapshots: mlly: 1.7.1 pathe: 1.1.2 + player.style@0.0.12: + dependencies: + media-chrome: 4.2.3 + possible-typed-array-names@1.0.0: {} postcss-attribute-case-insensitive@7.0.1(postcss@8.4.47): @@ -21220,6 +21452,24 @@ snapshots: react-lazy-with-preload@2.2.1: {} + react-pdf@9.1.1(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 2.1.1 + dequal: 2.0.3 + make-cancellable-promise: 1.3.2 + make-event-props: 1.6.2 + merge-refs: 1.3.0(@types/react@18.3.12) + pdfjs-dist: 4.4.168 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tiny-invariant: 1.3.3 + warning: 4.0.3 + optionalDependencies: + '@types/react': 18.3.12 + transitivePeerDependencies: + - encoding + - supports-color + react-refresh@0.14.2: {} react-remove-scroll-bar@2.3.6(@types/react@18.3.12)(react@18.3.1): @@ -21640,6 +21890,11 @@ snapshots: rgbcolor@1.0.1: optional: true + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + optional: true + rimraf@6.0.1: dependencies: glob: 11.0.0 @@ -21798,6 +22053,9 @@ snapshots: dependencies: randombytes: 2.1.0 + set-blocking@2.0.0: + optional: true + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -21879,6 +22137,13 @@ snapshots: simple-concat@1.0.1: optional: true + simple-get@3.1.1: + dependencies: + decompress-response: 4.2.1 + once: 1.4.0 + simple-concat: 1.0.1 + optional: true + simple-get@4.0.1: dependencies: decompress-response: 6.0.0 @@ -22913,6 +23178,10 @@ snapshots: w3c-keyname@2.2.8: {} + warning@4.0.3: + dependencies: + loose-envify: 1.4.0 + web-namespaces@2.0.1: {} webidl-conversions@3.0.1: {} @@ -22958,6 +23227,11 @@ snapshots: dependencies: isexe: 2.0.0 + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + optional: true + widest-line@3.1.0: dependencies: string-width: 4.2.3 From bab3d8090430aba240a5491f1eed1aeaad2bb2c7 Mon Sep 17 00:00:00 2001 From: David Hordiienko Date: Wed, 6 Nov 2024 14:54:09 +0200 Subject: [PATCH 11/36] improve: AttachmentPreview --- .../attachments-table/attachment-preview.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/src/modules/organizations/attachments-table/attachment-preview.tsx b/frontend/src/modules/organizations/attachments-table/attachment-preview.tsx index 414380c7f..f5e87c4e0 100644 --- a/frontend/src/modules/organizations/attachments-table/attachment-preview.tsx +++ b/frontend/src/modules/organizations/attachments-table/attachment-preview.tsx @@ -1,21 +1,21 @@ import { File, FileAudio, FileText, FileVideo } from 'lucide-react'; import type React from 'react'; -interface AttachmentPreviewIconProps { +interface AttachmentPreviewProps { url: string; contentType: string; name: string; openCarouselDialog: () => void; } -const AttachmentPreviewIcon = ({ url, contentType, name, openCarouselDialog }: AttachmentPreviewIconProps) => { +const AttachmentPreview: React.FC = ({ url, contentType, name, openCarouselDialog }) => { const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key !== 'Enter') return; - openCarouselDialog(); + if (e.key === 'Enter') openCarouselDialog(); }; - const renderAttachmentPreviewIcon = (iconSize = 20) => { - if (contentType.includes('image')) return {name}; + const renderIcon = (iconSize = 24) => { + if (contentType.includes('image')) + return {name}; if (contentType.includes('video')) return ; if (contentType.includes('pdf')) return ; @@ -25,10 +25,10 @@ const AttachmentPreviewIcon = ({ url, contentType, name, openCarouselDialog }: A }; return ( -
- {renderAttachmentPreviewIcon()} +
+ {renderIcon()}
); }; -export default AttachmentPreviewIcon; +export default AttachmentPreview; From 454f516176af60cca7411ae361b346df192eb4f5 Mon Sep 17 00:00:00 2001 From: David Hordiienko Date: Wed, 6 Nov 2024 17:21:21 +0200 Subject: [PATCH 12/36] add: support for multiple attachment --- backend/src/modules/attachments/index.ts | 25 ++++---- backend/src/modules/attachments/routes.ts | 2 +- backend/src/modules/attachments/schema.ts | 18 +++--- frontend/src/api/attachments.ts | 10 ++- .../query-client-provider/attachments.ts | 64 ++++++++++++------- .../organizations/attachments-table/index.tsx | 23 +++---- locales/en/common.json | 1 + 7 files changed, 84 insertions(+), 59 deletions(-) diff --git a/backend/src/modules/attachments/index.ts b/backend/src/modules/attachments/index.ts index d59b32c76..cf2446540 100644 --- a/backend/src/modules/attachments/index.ts +++ b/backend/src/modules/attachments/index.ts @@ -21,27 +21,24 @@ const attachmentsRoutes = app * Create attachment */ .openapi(attachmentsRoutesConfig.createAttachment, async (ctx) => { - const newAttachment = ctx.req.valid('json'); + const newAttachments = ctx.req.valid('json'); const organization = getOrganization(); const user = getContextUser(); - // Get the name of the attachment without the extension - const name = newAttachment.filename.split('.').slice(0, -1).join('.'); + const fixedNewAttachments = newAttachments.map((el) => ({ + ...el, + name: el.filename.split('.').slice(0, -1).join('.'), + createdBy: user.id, + organizationId: organization.id, + })); - const [createdAttachment] = await db - .insert(attachmentsTable) - .values({ - ...newAttachment, - name, - createdBy: user.id, - organizationId: organization.id, - }) - .returning(); + const createdAttachments = await db.insert(attachmentsTable).values(fixedNewAttachments).returning(); - logEvent('Attachment created', { attachment: createdAttachment.id }); + logEvent(`${createdAttachments.length} attachments have been created`); + // Store the created attachment - return ctx.json({ success: true, data: createdAttachment }, 200); + return ctx.json({ success: true, data: createdAttachments }, 200); }) /* * Get attachments diff --git a/backend/src/modules/attachments/routes.ts b/backend/src/modules/attachments/routes.ts index a163d42bc..bffff213a 100644 --- a/backend/src/modules/attachments/routes.ts +++ b/backend/src/modules/attachments/routes.ts @@ -31,7 +31,7 @@ class AttachmentRoutesConfig { description: 'Attachment', content: { 'application/json': { - schema: successWithDataSchema(attachmentSchema), + schema: successWithDataSchema(z.array(attachmentSchema)), }, }, }, diff --git a/backend/src/modules/attachments/schema.ts b/backend/src/modules/attachments/schema.ts index fe61b7e64..8c0d134d3 100644 --- a/backend/src/modules/attachments/schema.ts +++ b/backend/src/modules/attachments/schema.ts @@ -3,14 +3,16 @@ import { z } from 'zod'; import { idsQuerySchema, nameSchema, paginationQuerySchema } from '#/utils/schema/common-schemas'; import { attachmentsTable } from '../../db/schema/attachments'; -export const createAttachmentSchema = createInsertSchema(attachmentsTable).omit({ - name: true, - entity: true, - modifiedAt: true, - modifiedBy: true, - createdAt: true, - createdBy: true, -}); +export const createAttachmentSchema = z.array( + createInsertSchema(attachmentsTable).omit({ + name: true, + entity: true, + modifiedAt: true, + modifiedBy: true, + createdAt: true, + createdBy: true, + }), +); export const deleteAttachmentsQuerySchema = idsQuerySchema; diff --git a/frontend/src/api/attachments.ts b/frontend/src/api/attachments.ts index f0ec8cf17..ed115ced3 100644 --- a/frontend/src/api/attachments.ts +++ b/frontend/src/api/attachments.ts @@ -8,8 +8,14 @@ export const client = attachmentsHc(config.backendUrl, clientConfig); type CreateAttachmentParams = Parameters<(typeof client.index)['$post']>['0']['json']; // Create a new attachment -export const createAttachment = async (task: CreateAttachmentParams) => { - const response = await client.index.$post({ param: { orgIdOrSlug: task.organizationId }, json: task }); +export const createAttachment = async ({ + attachments, + organizationId, +}: { + attachments: CreateAttachmentParams; + organizationId: string; +}) => { + const response = await client.index.$post({ param: { orgIdOrSlug: organizationId }, json: attachments }); const json = await handleResponse(response); return json.data; }; diff --git a/frontend/src/modules/common/query-client-provider/attachments.ts b/frontend/src/modules/common/query-client-provider/attachments.ts index feab17e4b..bdce5abe9 100644 --- a/frontend/src/modules/common/query-client-provider/attachments.ts +++ b/frontend/src/modules/common/query-client-provider/attachments.ts @@ -27,7 +27,7 @@ export const attachmentKeys = { export type AttachmentsCreateMutationQueryFnVariables = Parameters[0]; export const useAttachmentCreateMutation = () => { - return useMutation({ + return useMutation({ mutationKey: attachmentKeys.create(), mutationFn: createAttachment, }); @@ -68,56 +68,74 @@ const onError = ( queryClient.setMutationDefaults(attachmentKeys.create(), { mutationFn: createAttachment, onMutate: async (variables) => { - const { id, organizationId } = variables; - - const optimisticId = id || nanoid(); - const newAttachment: Attachment = { - ...variables, - name: variables.filename.split('.').slice(0, -1).join('.'), - id: optimisticId, - entity: 'attachment', - createdAt: new Date().toISOString(), - createdBy: null, - modifiedAt: new Date().toISOString(), - modifiedBy: null, - }; + const { attachments, organizationId } = variables; + + const newAttachments: Attachment[] = []; + const optimisticIds: string[] = []; + + for (const attachment of attachments) { + const optimisticId = attachment.id || nanoid(); + const newAttachment: Attachment = { + ...attachment, + name: attachment.filename.split('.').slice(0, -1).join('.'), + id: optimisticId, + entity: 'attachment', + createdAt: new Date().toISOString(), + createdBy: null, + modifiedAt: new Date().toISOString(), + modifiedBy: null, + }; + + newAttachments.push(newAttachment); + optimisticIds.push(optimisticId); + } const queryKey = attachmentKeys.list({ orgIdOrSlug: organizationId }); - const previousData = await getPreviousData(queryKey); + // Get previous query data for optimistic update + const previousData = queryClient.getQueryData(queryKey); - // Optimistically update to the new value + // Optimistically update the list if (previousData) { queryClient.setQueryData(queryKey, { ...previousData, pages: [ { ...previousData.pages[0], - items: [newAttachment, ...previousData.pages[0].items], - total: previousData.pages[0].total + 1, + items: [...newAttachments, ...previousData.pages[0].items], // Add new attachments + total: previousData.pages[0].total + newAttachments.length, // Increment total by the number of new attachments }, ...previousData.pages.slice(1), ], }); } - return { previousData, optimisticId }; + return { previousData, optimisticIds }; }, - onSuccess: (createdAttachment, { organizationId }, { optimisticId }) => { + + onSuccess: (createdAttachments, { organizationId }, { optimisticIds }) => { const queryKey = attachmentKeys.list({ orgIdOrSlug: organizationId }); queryClient.setQueryData(queryKey, (oldData) => { if (!oldData) return oldData; + + const optimisticAttachments = createdAttachments.filter((el) => optimisticIds.includes(el.id)); + const items = oldData.pages[0].items; - const updatedItems = items.map((item) => (item.id === optimisticId ? createdAttachment : item)); + const updatedItems = items.map((item) => { + const createdItem = optimisticAttachments.find((created) => created.id === item.id); + return createdItem ? { ...item, ...createdItem } : item; + }); + return { ...oldData, - pages: [{ ...oldData.pages[0], items: updatedItems }, ...oldData.pages.slice(1)], + pages: [{ ...oldData.pages[0], items: updatedItems, total: updatedItems.length }, ...oldData.pages.slice(1)], }; }); - toast.success(t('common:success.create_resource', { resource: t('common:attachment') })); + toast.success(t('common:success.create_resources_with_number', { number: createdAttachments.length, resource: t('common:attachment') })); }, + onError, }); diff --git a/frontend/src/modules/organizations/attachments-table/index.tsx b/frontend/src/modules/organizations/attachments-table/index.tsx index f80028097..761cd70aa 100644 --- a/frontend/src/modules/organizations/attachments-table/index.tsx +++ b/frontend/src/modules/organizations/attachments-table/index.tsx @@ -41,6 +41,8 @@ import RemoveAttachmentsForm from './remove-attachments-form'; const LIMIT = config.requestLimits.attachments; +const maxAttachmentsUpload = 20; + type AttachmentSearch = z.infer; type RawAttachment = { @@ -178,10 +180,10 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro uppyOptions={{ restrictions: { maxFileSize: 10 * 1024 * 1024, // 10MB - maxNumberOfFiles: 1, + maxNumberOfFiles: maxAttachmentsUpload, allowedFileTypes: ['*/*'], minFileSize: null, - maxTotalFileSize: 10 * 1024 * 1024, // 10MB + maxTotalFileSize: 10 * 1024 * 1024 * maxAttachmentsUpload, // for maxAttachmentsUpload files at 10MB max each minNumberOfFiles: null, requiredMetaFields: [], }, @@ -189,15 +191,14 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro plugins={['webcam', 'image-editor', 'screen-capture', 'audio']} imageMode="attachment" callback={(result) => { - for (const res of result) { - createAttachment({ - url: res.url, - size: String(res.file.size || 0), - contentType: res.file.type, - filename: res.file.name || 'unknown', - organizationId: organization.id, - }); - } + const attachments = result.map((a) => ({ + url: a.url, + size: String(a.file.size || 0), + contentType: a.file.type, + filename: a.file.name || 'unknown', + organizationId: organization.id, + })); + createAttachment({ attachments, organizationId: organization.id }); dialog.remove(true, 'upload-attachment'); }} /> diff --git a/locales/en/common.json b/locales/en/common.json index 1b0350efd..a26ce6303 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -362,6 +362,7 @@ "success.archived_resource": "{{resource}} has been archived.", "success.create_newsletter": "Newsletter has been sent.", "success.create_resource": "{{resource}} has been created.", + "success.create_resources_with_number": "{{number}} of {{resource}} has been created.", "success.delete_attachments": "Attachments have been removed.", "success.delete_members": "Members have been removed.", "success.delete_resource": "{{resource}} has been deleted.", From 8226c17ba3e9f020b633495b97242000e8527d9f Mon Sep 17 00:00:00 2001 From: flipvanhaaren Date: Wed, 6 Nov 2024 17:26:48 +0100 Subject: [PATCH 13/36] imado fix --- backend/package.json | 2 +- backend/src/lib/imado-url.ts | 49 +- frontend/package.json | 32 +- frontend/src/lib/imado.ts | 9 +- pnpm-lock.yaml | 1676 ++++++++++++++++++++-------------- tus/package.json | 7 +- tus/src/imado-tus.ts | 125 +++ tus/src/index.ts | 2 +- 8 files changed, 1189 insertions(+), 713 deletions(-) create mode 100644 tus/src/imado-tus.ts diff --git a/backend/package.json b/backend/package.json index 45c7f510b..223fca226 100644 --- a/backend/package.json +++ b/backend/package.json @@ -29,7 +29,7 @@ "emails:preview": "email preview ./emails" }, "dependencies": { - "@cellajs/imado": "^0.1.3", + "@aws-sdk/cloudfront-signer": "^3.679.0", "@cellajs/permission-manager": "^0.1.0", "@commander-js/extra-typings": "^12.1.0", "@electric-sql/pglite": "^0.2.12", diff --git a/backend/src/lib/imado-url.ts b/backend/src/lib/imado-url.ts index 36d6988a2..1fdefba14 100644 --- a/backend/src/lib/imado-url.ts +++ b/backend/src/lib/imado-url.ts @@ -1,7 +1,54 @@ -import { ImadoUrl } from '@cellajs/imado'; import { config } from 'config'; import { env } from '../../env'; +import { getSignedUrl as cloudfrontGetSignedUrl } from '@aws-sdk/cloudfront-signer'; + +export type ImadoUrlParams = { + width?: number; + height?: number; + quality?: number; + format?: 'auto' | 'jpeg' | 'webp' | 'avif' | 'png' | 'gif' | 'pdf'; +}; + +interface ImadoUrlConfig { + signUrl: string; + cloudfrontKeyId: string; + cloudfrontPrivateKey: string; +} + +export class ImadoUrl { + private config: ImadoUrlConfig; + + constructor(config: ImadoUrlConfig) { + this.config = config; + } + + generate(path: string | null, params?: ImadoUrlParams) { + if (!path) return path; + + const url = new URL(path); + + if (params) { + for (const [key, value] of Object.entries(params)) { + url.searchParams.append(key, value.toString()); + } + } + + if (this.config.signUrl && path.includes(this.config.signUrl) && this.config.cloudfrontKeyId && this.config.cloudfrontPrivateKey) { + const signed = cloudfrontGetSignedUrl({ + url: url.toString(), + keyPairId: this.config.cloudfrontKeyId, + privateKey: this.config.cloudfrontPrivateKey, + dateLessThan: new Date(Date.now() + 24 * 60 * 60 * 1000).toString(), + }); + + return signed; + } + + return url.toString(); + } +} + export const getImadoUrl = new ImadoUrl({ signUrl: config.privateCDNUrl, cloudfrontKeyId: env.AWS_CLOUDFRONT_KEY_ID, diff --git a/frontend/package.json b/frontend/package.json index dbcfe575a..428eb2550 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -26,7 +26,7 @@ "@github/mini-throttle": "^2.1.1", "@hookform/resolvers": "^3.9.1", "@oslojs/encoding": "^1.1.0", - "@paddle/paddle-js": "^1.3.1", + "@paddle/paddle-js": "^1.3.2", "@radix-ui/react-accordion": "^1.2.1", "@radix-ui/react-alert-dialog": "^1.1.2", "@radix-ui/react-avatar": "^1.1.1", @@ -50,15 +50,15 @@ "@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.3", "@radix-ui/react-visually-hidden": "^1.1.0", - "@sentry/react": "^8.36.0", + "@sentry/react": "^8.37.1", "@t3-oss/env-core": "^0.11.1", "@tailwindcss/typography": "^0.5.15", - "@tanstack/query-sync-storage-persister": "^5.59.16", - "@tanstack/react-query": "^5.59.16", - "@tanstack/react-query-devtools": "^5.59.16", - "@tanstack/react-query-persist-client": "^5.59.16", - "@tanstack/react-router": "^1.78.0", - "@tanstack/router-devtools": "^1.78.0", + "@tanstack/query-sync-storage-persister": "^5.59.20", + "@tanstack/react-query": "^5.59.20", + "@tanstack/react-query-devtools": "^5.59.20", + "@tanstack/react-query-persist-client": "^5.59.20", + "@tanstack/react-router": "^1.79.0", + "@tanstack/router-devtools": "^1.79.0", "@uppy/audio": "^2.0.1", "@uppy/core": "^4.2.3", "@uppy/dashboard": "^4.1.2", @@ -75,7 +75,7 @@ "backend": "workspace:*", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", - "cmdk": "^1.0.3", + "cmdk": "^1.0.4", "config": "workspace:*", "dayjs": "^1.11.13", "dompurify": "^3.1.7", @@ -100,7 +100,7 @@ "react": "^18.3.1", "react-confetti-explosion": "^2.1.2", "react-data-grid": "7.0.0-beta.47", - "react-day-picker": "^9.2.1", + "react-day-picker": "^9.3.0", "react-dom": "^18.3.1", "react-error-boundary": "^4.1.2", "react-hook-form": "^7.53.1", @@ -109,9 +109,9 @@ "react-lazy-with-preload": "^2.2.1", "react-pdf": "^9.1.1", "react-resizable-panels": "^2.1.6", - "recharts": "^2.13.2", + "recharts": "^2.13.3", "slugify": "1.6.6", - "sonner": "^1.5.0", + "sonner": "^1.7.0", "tailwind-merge": "^2.5.4", "tailwindcss-animate": "^1.0.7", "use-count-up": "^3.0.1", @@ -122,15 +122,15 @@ "zxcvbn": "^4.4.2" }, "devDependencies": { - "@faker-js/faker": "^9.1.0", + "@faker-js/faker": "^9.2.0", "@million/lint": "^1.0.11", "@redux-devtools/extension": "^3.3.0", "@rollup/plugin-terser": "^0.4.4", "@sentry/vite-plugin": "^2.22.6", - "@tanstack/router-plugin": "^1.78.0", + "@tanstack/router-plugin": "^1.79.0", "@types/dompurify": "^3.0.5", "@types/lodash.clonedeep": "^4.5.9", - "@types/node": "^22.8.6", + "@types/node": "^22.9.0", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@types/zxcvbn": "^4.4.5", @@ -139,7 +139,7 @@ "autoprefixer": "^10.4.20", "postcss": "^8.4.47", "postcss-import": "^16.1.0", - "postcss-preset-env": "^10.0.8", + "postcss-preset-env": "^10.0.9", "postgres": "^3.4.5", "rollup-plugin-visualizer": "^5.12.0", "tailwindcss": "^3.4.14", diff --git a/frontend/src/lib/imado.ts b/frontend/src/lib/imado.ts index 7adc22bbe..a72eebbc9 100644 --- a/frontend/src/lib/imado.ts +++ b/frontend/src/lib/imado.ts @@ -6,7 +6,7 @@ import type { UploadParams, UploadType } from '~/types/common'; import '@uppy/core/dist/style.min.css'; -export type UppyMeta = { public?: boolean }; +export type UppyMeta = { public?: boolean; contentType?: string }; // biome-ignore lint/complexity/noBannedTypes: no other way to define this type export type UppyBody = {}; @@ -39,6 +39,13 @@ export async function ImadoUppy( meta: { public: isPublic, }, + onBeforeFileAdded: (file) => { + file.meta = { + ...file.meta, + contentType: file.type, + }; + return file; + }, }) .use(Tus, { endpoint: config.tusUrl, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e1608b95..aa9c4b5b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,9 +33,9 @@ importers: backend: dependencies: - '@cellajs/imado': - specifier: ^0.1.3 - version: 0.1.3(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2)(yaml@2.6.0) + '@aws-sdk/cloudfront-signer': + specifier: ^3.679.0 + version: 3.679.0 '@cellajs/permission-manager': specifier: ^0.1.0 version: 0.1.0(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2)(yaml@2.6.0) @@ -140,7 +140,7 @@ importers: version: 9.0.2 jsx-email: specifier: ^2.1.2 - version: 2.1.2(@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.3)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))(@jsx-email/plugin-inline@1.0.1)(@jsx-email/plugin-minify@1.0.1)(@jsx-email/plugin-pretty@1.0.0)(@types/node@22.8.6)(@types/react@18.3.12)(react@18.3.1)(terser@5.36.0) + version: 2.1.2(@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.4)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))(@jsx-email/plugin-inline@1.0.1)(@jsx-email/plugin-minify@1.0.1)(@jsx-email/plugin-pretty@1.0.0)(@types/node@22.8.6)(@types/react@18.3.12)(react@18.3.1)(terser@5.36.0) locales: specifier: workspace:* version: link:../locales @@ -308,7 +308,7 @@ importers: version: 0.17.1(@tiptap/pm@2.9.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@blocknote/shadcn': specifier: ^0.17.1 - version: 0.17.1(@tiptap/pm@2.9.1)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)) + version: 0.17.1(@tiptap/pm@2.9.1)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)) '@electric-sql/client': specifier: ^0.6.5 version: 0.6.5 @@ -325,8 +325,8 @@ importers: specifier: ^1.1.0 version: 1.1.0 '@paddle/paddle-js': - specifier: ^1.3.1 - version: 1.3.1 + specifier: ^1.3.2 + version: 1.3.2 '@radix-ui/react-accordion': specifier: ^1.2.1 version: 1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -397,32 +397,32 @@ importers: specifier: ^1.1.0 version: 1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@sentry/react': - specifier: ^8.36.0 - version: 8.36.0(react@18.3.1) + specifier: ^8.37.1 + version: 8.37.1(react@18.3.1) '@t3-oss/env-core': specifier: ^0.11.1 version: 0.11.1(typescript@5.6.3)(zod@3.23.8) '@tailwindcss/typography': specifier: ^0.5.15 - version: 0.5.15(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))) + version: 0.5.15(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3))) '@tanstack/query-sync-storage-persister': - specifier: ^5.59.16 - version: 5.59.16 + specifier: ^5.59.20 + version: 5.59.20 '@tanstack/react-query': - specifier: ^5.59.16 - version: 5.59.16(react@18.3.1) + specifier: ^5.59.20 + version: 5.59.20(react@18.3.1) '@tanstack/react-query-devtools': - specifier: ^5.59.16 - version: 5.59.16(@tanstack/react-query@5.59.16(react@18.3.1))(react@18.3.1) + specifier: ^5.59.20 + version: 5.59.20(@tanstack/react-query@5.59.20(react@18.3.1))(react@18.3.1) '@tanstack/react-query-persist-client': - specifier: ^5.59.16 - version: 5.59.16(@tanstack/react-query@5.59.16(react@18.3.1))(react@18.3.1) + specifier: ^5.59.20 + version: 5.59.20(@tanstack/react-query@5.59.20(react@18.3.1))(react@18.3.1) '@tanstack/react-router': - specifier: ^1.78.0 - version: 1.78.0(@tanstack/router-generator@1.78.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.79.0 + version: 1.79.0(@tanstack/router-generator@1.79.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/router-devtools': - specifier: ^1.78.0 - version: 1.78.0(@tanstack/react-router@1.78.0(@tanstack/router-generator@1.78.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.79.0 + version: 1.79.0(@tanstack/react-router@1.79.0(@tanstack/router-generator@1.79.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@uppy/audio': specifier: ^2.0.1 version: 2.0.1(@uppy/core@4.2.3) @@ -472,8 +472,8 @@ importers: specifier: ^2.1.1 version: 2.1.1 cmdk: - specifier: ^1.0.3 - version: 1.0.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) config: specifier: workspace:* version: link:../config @@ -491,7 +491,7 @@ importers: version: 8.3.1(react@18.3.1) emblor: specifier: ^1.4.6 - version: 1.4.6(@swc/core@1.7.26)(@types/react-dom@18.3.1)(@types/react@18.3.12)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3) + version: 1.4.6(@swc/core@1.7.26)(@types/react-dom@18.3.1)(@types/react@18.3.12)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3))(typescript@5.6.3) framer-motion: specifier: ^11.11.11 version: 11.11.11(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -547,8 +547,8 @@ importers: specifier: 7.0.0-beta.47 version: 7.0.0-beta.47(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-day-picker: - specifier: ^9.2.1 - version: 9.2.1(react@18.3.1) + specifier: ^9.3.0 + version: 9.3.0(react@18.3.1) react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) @@ -574,20 +574,20 @@ importers: specifier: ^2.1.6 version: 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) recharts: - specifier: ^2.13.2 - version: 2.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^2.13.3 + version: 2.13.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) slugify: specifier: 1.6.6 version: 1.6.6 sonner: - specifier: ^1.5.0 - version: 1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.7.0 + version: 1.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) tailwind-merge: specifier: ^2.5.4 version: 2.5.4 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))) + version: 1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3))) use-count-up: specifier: ^3.0.1 version: 3.0.1(react@18.3.1) @@ -608,8 +608,8 @@ importers: version: 4.4.2 devDependencies: '@faker-js/faker': - specifier: ^9.1.0 - version: 9.1.0 + specifier: ^9.2.0 + version: 9.2.0 '@million/lint': specifier: ^1.0.11 version: 1.0.11(rollup@2.79.2)(webpack-sources@3.2.3) @@ -623,8 +623,8 @@ importers: specifier: ^2.22.6 version: 2.22.6 '@tanstack/router-plugin': - specifier: ^1.78.0 - version: 1.78.0(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0))(webpack-sources@3.2.3) + specifier: ^1.79.0 + version: 1.79.0(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0))(webpack-sources@3.2.3) '@types/dompurify': specifier: ^3.0.5 version: 3.0.5 @@ -632,8 +632,8 @@ importers: specifier: ^4.5.9 version: 4.5.9 '@types/node': - specifier: ^22.8.6 - version: 22.8.6 + specifier: ^22.9.0 + version: 22.9.0 '@types/react': specifier: ^18.3.12 version: 18.3.12 @@ -645,10 +645,10 @@ importers: version: 4.4.5 '@vitejs/plugin-basic-ssl': specifier: ^1.1.0 - version: 1.1.0(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0)) + version: 1.1.0(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0)) '@vitejs/plugin-react': specifier: ^4.3.3 - version: 4.3.3(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0)) + version: 4.3.3(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0)) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.47) @@ -659,8 +659,8 @@ importers: specifier: ^16.1.0 version: 16.1.0(postcss@8.4.47) postcss-preset-env: - specifier: ^10.0.8 - version: 10.0.8(postcss@8.4.47) + specifier: ^10.0.9 + version: 10.0.9(postcss@8.4.47) postgres: specifier: ^3.4.5 version: 3.4.5 @@ -669,7 +669,7 @@ importers: version: 5.12.0(rollup@2.79.2) tailwindcss: specifier: ^3.4.14 - version: 3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)) + version: 3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -678,33 +678,45 @@ importers: version: 5.6.3 vite: specifier: ^5.4.10 - version: 5.4.10(@types/node@22.8.6)(terser@5.36.0) + version: 5.4.10(@types/node@22.9.0)(terser@5.36.0) vite-plugin-html: specifier: ^3.2.2 - version: 3.2.2(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0)) + version: 3.2.2(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0)) vite-plugin-pwa: specifier: ^0.20.5 - version: 0.20.5(@vite-pwa/assets-generator@0.2.6)(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0) + version: 0.20.5(@vite-pwa/assets-generator@0.2.6)(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0) vite-plugin-static-copy: specifier: ^1.0.6 - version: 1.0.6(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0)) + version: 1.0.6(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0)) locales: {} tus: dependencies: - '@cellajs/imado': - specifier: ^0.1.3 - version: 0.1.3(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(yaml@2.6.0) + '@aws-sdk/client-s3': + specifier: ^3.685.0 + version: 3.685.0 '@t3-oss/env-core': specifier: ^0.11.0 version: 0.11.1(typescript@5.6.2)(zod@3.23.8) + '@tus/file-store': + specifier: ^1.5.0 + version: 1.5.0 + '@tus/s3-store': + specifier: ^1.6.0 + version: 1.6.0 + '@tus/server': + specifier: ^1.9.0 + version: 1.9.0 config: specifier: workspace:* version: link:../config dotenv: specifier: ^16.4.5 version: 16.4.5 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 tsx: specifier: ^4.17.0 version: 4.19.1 @@ -712,6 +724,9 @@ importers: specifier: ^3.23.8 version: 3.23.8 devDependencies: + '@types/jsonwebtoken': + specifier: ^9.0.7 + version: 9.0.7 tsup: specifier: ^8.2.4 version: 8.3.0(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.2)(yaml@2.6.0) @@ -782,139 +797,139 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.678.0': - resolution: {integrity: sha512-2N+cGerOtcijYVRThakA1wwaXjdb7bNX8fMnmNzfqsRu1kASCPNvefhPTAiNl//Hf2l2d+H8TdI3wtLw0KurBQ==} + '@aws-sdk/client-s3@3.685.0': + resolution: {integrity: sha512-ClvMeQHbLhWkpxnVymo4qWS5/yZcPXjorDbSday3joCWYWCSHTO409nWd+jx6eA4MKT/EY/uJ6ZBJRFfByKLuA==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-sso-oidc@3.678.0': - resolution: {integrity: sha512-sgj9Y4zGiwLePLDjqhGoghoZgseh88JkKkwWH558IIte/cf/ix7ezOvptnA0WUlI5Z/329LtkN6O8TRqSJ7MWw==} + '@aws-sdk/client-sso-oidc@3.682.0': + resolution: {integrity: sha512-ZPZ7Y/r/w3nx/xpPzGSqSQsB090Xk5aZZOH+WBhTDn/pBEuim09BYXCLzvvxb7R7NnuoQdrTJiwimdJAhHl7ZQ==} engines: {node: '>=16.0.0'} peerDependencies: - '@aws-sdk/client-sts': ^3.678.0 + '@aws-sdk/client-sts': ^3.682.0 - '@aws-sdk/client-sso@3.678.0': - resolution: {integrity: sha512-5Fg2BkR1En8iBbiZ18STvLDGPK9Re5MyCmX+hfIhQzPsEf1FRkAkOluEXX79aBva8iWn2oCD/xKBUku4x3eusw==} + '@aws-sdk/client-sso@3.682.0': + resolution: {integrity: sha512-PYH9RFUMYLFl66HSBq4tIx6fHViMLkhJHTYJoJONpBs+Td+NwVJ895AdLtDsBIhMS0YseCbPpuyjUCJgsUrwUw==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-sts@3.678.0': - resolution: {integrity: sha512-oRtDnbqIuTbBq0xd7XlaugDA41EqRFzWLpPNr4uwkH8L7xwtIByfJG/qXx2OtOiFFasAhMWJLu/DDqWZyp819A==} + '@aws-sdk/client-sts@3.682.0': + resolution: {integrity: sha512-xKuo4HksZ+F8m9DOfx/ZuWNhaPuqZFPwwy0xqcBT6sWH7OAuBjv/fnpOTzyQhpVTWddlf+ECtMAMrxjxuOExGQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/cloudfront-signer@3.678.0': - resolution: {integrity: sha512-kn0wjxKn80vy9FRexsqb0MAz/QQ4egGdx1otLcA/kNuekupbodIhv8S6y5aoW6Tvz2kJNaRNePp7rYqo4nRkVQ==} + '@aws-sdk/cloudfront-signer@3.679.0': + resolution: {integrity: sha512-aq1zgo8YwVlz/lAsuKuHKx+ETH4D6+e0J3aTCPrWdX3EdtLYGe9cDpgCbS/2vgMQVqnM0d0dJ+9UPtSXxeHXuw==} engines: {node: '>=16.0.0'} - '@aws-sdk/core@3.678.0': - resolution: {integrity: sha512-ZTzybFZqSaPQymgRkTl08vk6xilaxr8LnJOc0h3KhcHLK4TJmdOcxqPpa6QxrBKcn2rmxzGiPRbAHLGI+BIxBw==} + '@aws-sdk/core@3.679.0': + resolution: {integrity: sha512-CS6PWGX8l4v/xyvX8RtXnBisdCa5+URzKd0L6GvHChype9qKUVxO/Gg6N/y43Hvg7MNWJt9FBPNWIxUB+byJwg==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-env@3.678.0': - resolution: {integrity: sha512-29uhXAB7uJqHtvJ2U3pi1YkMfv0WefW9EmSMoFAunjudXXBVktwTlWg0lyCM+KHrGKLkQyfs5UF/A9IelS8tdQ==} + '@aws-sdk/credential-provider-env@3.679.0': + resolution: {integrity: sha512-EdlTYbzMm3G7VUNAMxr9S1nC1qUNqhKlAxFU8E7cKsAe8Bp29CD5HAs3POc56AVo9GC4yRIS+/mtlZSmrckzUA==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-http@3.678.0': - resolution: {integrity: sha512-EvpmP0nc7ddRp0qwJOSu0uBXa+MMk4+OLlyEJcdaHnZI4/BoyVWr5fJUD5eQYZk11LZPZSvnsliYXWwLyVNXHQ==} + '@aws-sdk/credential-provider-http@3.679.0': + resolution: {integrity: sha512-ZoKLubW5DqqV1/2a3TSn+9sSKg0T8SsYMt1JeirnuLJF0mCoYFUaWMyvxxKuxPoqvUsaycxKru4GkpJ10ltNBw==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-ini@3.678.0': - resolution: {integrity: sha512-8kHy7V5rRO73EpBCUclykP9T/QIBVi0SkQsc88ZRxpdh59/JY2N6DT5khMTzrz9+Vvlw3FDMJN4AI/qWjJHhdw==} + '@aws-sdk/credential-provider-ini@3.682.0': + resolution: {integrity: sha512-6eqWeHdK6EegAxqDdiCi215nT3QZPwukgWAYuVxNfJ/5m0/P7fAzF+D5kKVgByUvGJEbq/FEL8Fw7OBe64AA+g==} engines: {node: '>=16.0.0'} peerDependencies: - '@aws-sdk/client-sts': ^3.678.0 + '@aws-sdk/client-sts': ^3.682.0 - '@aws-sdk/credential-provider-node@3.678.0': - resolution: {integrity: sha512-KGRBVD/oNr/aD+Wy5zc5AjfeSv5b4ahAu5eAUbOz+eGjGpGgrMtjY+R2rDY/3i3wFj9/DvOIfFGeZQMwtDzIuA==} + '@aws-sdk/credential-provider-node@3.682.0': + resolution: {integrity: sha512-HSmDqZcBVZrTctHCT9m++vdlDfJ1ARI218qmZa+TZzzOFNpKWy6QyHMEra45GB9GnkkMmV6unoDSPMuN0AqcMg==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-process@3.678.0': - resolution: {integrity: sha512-5TpzzHKwPOvUJig0bvTt+brtXfLPaSVLwea9re+XGrS5T6Hz65IaX2RL6uY1GQ0UVOqgwQ5nAti1WOfBoSJ5BA==} + '@aws-sdk/credential-provider-process@3.679.0': + resolution: {integrity: sha512-u/p4TV8kQ0zJWDdZD4+vdQFTMhkDEJFws040Gm113VHa/Xo1SYOjbpvqeuFoz6VmM0bLvoOWjxB9MxnSQbwKpQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-sso@3.678.0': - resolution: {integrity: sha512-PXydLUsLYd1rkhZ7zwf0613u5sofxIEhh7C1QGP1MSY3L1jt8bu7pZIcMzubfvmaGZI5k84aHhhjQEiAJUxIMg==} + '@aws-sdk/credential-provider-sso@3.682.0': + resolution: {integrity: sha512-h7IH1VsWgV6YAJSWWV6y8uaRjGqLY3iBpGZlXuTH/c236NMLaNv+WqCBLeBxkFGUb2WeQ+FUPEJDCD69rgLIkg==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-web-identity@3.678.0': - resolution: {integrity: sha512-fcYZjTTFcef99l+BhcEAhHS4tEK1kE6Xj5Zz5lT4tFA07BkQt3d6kUKRVVfJnsbcHH4RDBUCnLhU8HPfc/kvjA==} + '@aws-sdk/credential-provider-web-identity@3.679.0': + resolution: {integrity: sha512-a74tLccVznXCaBefWPSysUcLXYJiSkeUmQGtalNgJ1vGkE36W5l/8czFiiowdWdKWz7+x6xf0w+Kjkjlj42Ung==} engines: {node: '>=16.0.0'} peerDependencies: - '@aws-sdk/client-sts': ^3.678.0 + '@aws-sdk/client-sts': ^3.679.0 - '@aws-sdk/middleware-bucket-endpoint@3.667.0': - resolution: {integrity: sha512-XGz4jMAkDoTyFdtLz7ZF+C05IAhCTC1PllpvTBaj821z/L0ilhbqVhrT/f2Buw8Id/K5A390csGXgusXyrFFjA==} + '@aws-sdk/middleware-bucket-endpoint@3.679.0': + resolution: {integrity: sha512-5EpiPhhGgnF+uJR4DzWUk6Lx3pOn9oM6JGXxeHsiynfoBfq7vHMleq+uABHHSQS+y7XzbyZ7x8tXNQlliMwOsg==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-expect-continue@3.667.0': - resolution: {integrity: sha512-0TiSL9S5DSG95NHGIz6qTMuV7GDKVn8tvvGSrSSZu/wXO3JaYSH0AElVpYfc4PtPRqVpEyNA7nnc7W56mMCLWQ==} + '@aws-sdk/middleware-expect-continue@3.679.0': + resolution: {integrity: sha512-nYsh9PdWrF4EahTRdXHGlNud82RPc508CNGdh1lAGfPU3tNveGfMBX3PcGBtPOse3p9ebNKRWVmUc9eXSjGvHA==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.678.0': - resolution: {integrity: sha512-IyWWXVvG4IJ9vkagTF8wkNtybKU5SWYIQ1BRDiCmoDyLPOpogNOBVnn10RX9FW7J7BMAUFgtx6N1uMQ8MitDiA==} + '@aws-sdk/middleware-flexible-checksums@3.682.0': + resolution: {integrity: sha512-5u1STth6iZUtAvPDO0NJVYKUX2EYKU7v84MYYaZ3O27HphRjFqDos0keL2KTnHn/KmMD68rM3yiUareWR8hnAQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-host-header@3.667.0': - resolution: {integrity: sha512-Z7fIAMQnPegs7JjAQvlOeWXwpMRfegh5eCoIP6VLJIeR6DLfYKbP35JBtt98R6DXslrN2RsbTogjbxPEDQfw1w==} + '@aws-sdk/middleware-host-header@3.679.0': + resolution: {integrity: sha512-y176HuQ8JRY3hGX8rQzHDSbCl9P5Ny9l16z4xmaiLo+Qfte7ee4Yr3yaAKd7GFoJ3/Mhud2XZ37fR015MfYl2w==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-location-constraint@3.667.0': - resolution: {integrity: sha512-ob85H3HhT3/u5O+x0o557xGZ78vSNeSSwMaSitxdsfs2hOuoUl1uk+OeLpi1hkuJnL41FPpokV7TVII2XrFfmg==} + '@aws-sdk/middleware-location-constraint@3.679.0': + resolution: {integrity: sha512-SA1C1D3XgoKTGxyNsOqd016ONpk46xJLWDgJUd00Zb21Ox5wYCoY6aDRKiaMRW+1VfCJdezs1Do3XLyIU9KxyA==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-logger@3.667.0': - resolution: {integrity: sha512-PtTRNpNm/5c746jRgZCNg4X9xEJIwggkGJrF0GP9AB1ANg4pc/sF2Fvn1NtqPe9wtQ2stunJprnm5WkCHN7QiA==} + '@aws-sdk/middleware-logger@3.679.0': + resolution: {integrity: sha512-0vet8InEj7nvIvGKk+ch7bEF5SyZ7Us9U7YTEgXPrBNStKeRUsgwRm0ijPWWd0a3oz2okaEwXsFl7G/vI0XiEA==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-recursion-detection@3.667.0': - resolution: {integrity: sha512-U5glWD3ehFohzpUpopLtmqAlDurGWo2wRGPNgi4SwhWU7UDt6LS7E/UvJjqC0CUrjlzOw+my2A+Ncf+fisMhxQ==} + '@aws-sdk/middleware-recursion-detection@3.679.0': + resolution: {integrity: sha512-sQoAZFsQiW/LL3DfKMYwBoGjYDEnMbA9WslWN8xneCmBAwKo6IcSksvYs23PP8XMIoBGe2I2J9BSr654XWygTQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-sdk-s3@3.678.0': - resolution: {integrity: sha512-AT4oKh4kPGWG+Ews9M/KYB/TdSvRJLxhVvrVXFxStm9OgeNksxsHH02gnyEOfmGX08ouNRyeaIsqG9RsvXz6Gg==} + '@aws-sdk/middleware-sdk-s3@3.685.0': + resolution: {integrity: sha512-C4w92b3A99NbghrA2Ssw6y1RbDF3I3Bgzi2Izh0pXgyIoDiX0xs9bUs/FGYLK4uepYr78DAZY8DwEpzjWIXkSA==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-ssec@3.667.0': - resolution: {integrity: sha512-1wuAUZIkmZIvOmGg5qNQU821CGFHhkuKioxXgNh0DpUxZ9+AeiV7yorJr+bqkb2KBFv1i1TnzGRecvKf/KvZIQ==} + '@aws-sdk/middleware-ssec@3.679.0': + resolution: {integrity: sha512-4GNUxXbs1M71uFHRiCAZtN0/g23ogI9YjMe5isAuYMHXwDB3MhqF7usKf954mBP6tplvN44vYlbJ84faaLrTtg==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-user-agent@3.678.0': - resolution: {integrity: sha512-tg9cC5COgGP0cznD2ys9kxPtVeKUygPZshDWXLAfA/cH/4m2ZUBvoEVv1SxkIbvOjnPwa976rdPLQUwRZvsL0g==} + '@aws-sdk/middleware-user-agent@3.682.0': + resolution: {integrity: sha512-7TyvYR9HdGH1/Nq0eeApUTM4izB6rExiw87khVYuJwZHr6FmvIL1FsOVFro/4WlXa0lg4LiYOm/8H8dHv+fXTg==} engines: {node: '>=16.0.0'} - '@aws-sdk/region-config-resolver@3.667.0': - resolution: {integrity: sha512-iNr+JhhA902JMKHG9IwT9YdaEx6KGl6vjAL5BRNeOjfj4cZYMog6Lz/IlfOAltMtT0w88DAHDEFrBd2uO0l2eg==} + '@aws-sdk/region-config-resolver@3.679.0': + resolution: {integrity: sha512-Ybx54P8Tg6KKq5ck7uwdjiKif7n/8g1x+V0V9uTjBjRWqaIgiqzXwKWoPj6NCNkE7tJNtqI4JrNxp/3S3HvmRw==} engines: {node: '>=16.0.0'} - '@aws-sdk/signature-v4-multi-region@3.678.0': - resolution: {integrity: sha512-IPredemaXQ09SsRAry0x0o60V3eKXqjvhg4/rD5QcSxZXNkHQPZzbfEpB6J68NN8IXTv8n9uFwAKBAtbIrnnfg==} + '@aws-sdk/signature-v4-multi-region@3.685.0': + resolution: {integrity: sha512-IHLwuAZGqfUWVrNqw0ugnBa7iL8uBP4x6A7bfBDXRXWCWjUCed/1/D//0lKDHwpFkV74fGW6KoBacnWSUlXmwA==} engines: {node: '>=16.0.0'} - '@aws-sdk/token-providers@3.667.0': - resolution: {integrity: sha512-ZecJlG8p6D4UTYlBHwOWX6nknVtw/OBJ3yPXTSajBjhUlj9lE2xvejI8gl4rqkyLXk7z3bki+KR4tATbMaM9yg==} + '@aws-sdk/token-providers@3.679.0': + resolution: {integrity: sha512-1/+Zso/x2jqgutKixYFQEGli0FELTgah6bm7aB+m2FAWH4Hz7+iMUsazg6nSWm714sG9G3h5u42Dmpvi9X6/hA==} engines: {node: '>=16.0.0'} peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.667.0 + '@aws-sdk/client-sso-oidc': ^3.679.0 - '@aws-sdk/types@3.667.0': - resolution: {integrity: sha512-gYq0xCsqFfQaSL/yT1Gl1vIUjtsg7d7RhnUfsXaHt8xTxOKRTdH9GjbesBjXOzgOvB0W0vfssfreSNGFlOOMJg==} + '@aws-sdk/types@3.679.0': + resolution: {integrity: sha512-NwVq8YvInxQdJ47+zz4fH3BRRLC6lL+WLkvr242PVBbUOLRyK/lkwHlfiKUoeVIMyK5NF+up6TRg71t/8Bny6Q==} engines: {node: '>=16.0.0'} - '@aws-sdk/util-arn-parser@3.568.0': - resolution: {integrity: sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==} + '@aws-sdk/util-arn-parser@3.679.0': + resolution: {integrity: sha512-CwzEbU8R8rq9bqUFryO50RFBlkfufV9UfMArHPWlo+lmsC+NlSluHQALoj6Jkq3zf5ppn1CN0c1DDLrEqdQUXg==} engines: {node: '>=16.0.0'} - '@aws-sdk/util-endpoints@3.667.0': - resolution: {integrity: sha512-X22SYDAuQJWnkF1/q17pkX3nGw5XMD9YEUbmt87vUnRq7iyJ3JOpl6UKOBeUBaL838wA5yzdbinmCITJ/VZ1QA==} + '@aws-sdk/util-endpoints@3.679.0': + resolution: {integrity: sha512-YL6s4Y/1zC45OvddvgE139fjeWSKKPgLlnfrvhVL7alNyY9n7beR4uhoDpNrt5mI6sn9qiBF17790o+xLAXjjg==} engines: {node: '>=16.0.0'} '@aws-sdk/util-locate-window@3.568.0': resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} engines: {node: '>=16.0.0'} - '@aws-sdk/util-user-agent-browser@3.675.0': - resolution: {integrity: sha512-HW4vGfRiX54RLcsYjLuAhcBBJ6lRVEZd7njfGpAwBB9s7BH8t48vrpYbyA5XbbqbTvXfYBnugQCUw9HWjEa1ww==} + '@aws-sdk/util-user-agent-browser@3.679.0': + resolution: {integrity: sha512-CusSm2bTBG1kFypcsqU8COhnYc6zltobsqs3nRrvYqYaOqtMnuE46K4XTWpnzKgwDejgZGOE+WYyprtAxrPvmQ==} - '@aws-sdk/util-user-agent-node@3.678.0': - resolution: {integrity: sha512-bKRemCdHMPAlEYE9KuQiMQG9/b4n8C+9DlJAL/X00Q7Zvm9Gv6h0+i5EZ+Xx8sbHq5oUv9a4W4tb+nkUZ0ltpw==} + '@aws-sdk/util-user-agent-node@3.682.0': + resolution: {integrity: sha512-so5s+j0gPoTS0HM4HPL+G0ajk0T6cQAg8JXzRgvyiQAxqie+zGCZAV3VuVeMNWMVbzsgZl0pYZaatPFTLG/AxA==} engines: {node: '>=16.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -922,8 +937,8 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.662.0': - resolution: {integrity: sha512-ikLkXn0igUpnJu2mCZjklvmcDGWT9OaLRv3JyC/cRkTaaSrblPjPM7KKsltxdMTLQ+v7fjCN0TsJpxphMfaOPA==} + '@aws-sdk/xml-builder@3.679.0': + resolution: {integrity: sha512-nPmhVZb39ty5bcQ7mAwtjezBcsBqTYZ9A2D9v/lE92KCLdu5RhSkPH7O71ZqbZx1mUSg9fAOxHPiG79U5VlpLQ==} engines: {node: '>=16.0.0'} '@axiomhq/js@1.0.0-rc.3': @@ -1526,9 +1541,6 @@ packages: '@canvas/image-data@1.0.0': resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==} - '@cellajs/imado@0.1.3': - resolution: {integrity: sha512-WDUVh44nfYT/HOoKlZIBH54Gh7Qt4bR6IbM04nLPFdYBWqo9fVtxROl2yEmutux85zmjG4t+HztH3U7iRGEePQ==} - '@cellajs/permission-manager@0.1.0': resolution: {integrity: sha512-+vGHLdGrR5Jez+dp6yHsgij8hIuZbdU88IU4JAV8nO4FzH1RDNkkj++O9RhMf3CKKDslYn7QiwI8D16u379+0A==} engines: {node: '>=20.9.0'} @@ -1550,47 +1562,47 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@csstools/cascade-layer-name-parser@2.0.3': - resolution: {integrity: sha512-KUcKk2oe7666aaeY+yxhy5TB0AN5x2Pi/ZJ23fbO8A0TEcLpA+VhVIw9s+6hTsAQHr8Fqc8p4RClsxxsmuIn1A==} + '@csstools/cascade-layer-name-parser@2.0.4': + resolution: {integrity: sha512-7DFHlPuIxviKYZrOiwVU/PiHLm3lLUR23OMuEEtfEOQTOp9hzQ2JjdY6X5H18RVuUPJqSCI+qNnD5iOLMVE0bA==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.3 - '@csstools/css-tokenizer': ^3.0.2 + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 '@csstools/color-helpers@5.0.1': resolution: {integrity: sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==} engines: {node: '>=18'} - '@csstools/css-calc@2.0.3': - resolution: {integrity: sha512-UAhqOt43s8e4MfLAnIS1OmB/lDN32t03YObodmFyy60+1i6ZsT2rlwBEdajH6zDFS/TGogsvgMamV5GzZt2muA==} + '@csstools/css-calc@2.0.4': + resolution: {integrity: sha512-8/iCd8lH10gKNsq5detnbGWiFd6PXK2wB8wjE6fHNNhtqvshyMrIJgffwRcw6yl/gzGTH+N1i+KRhjqHxqYTmg==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.3 - '@csstools/css-tokenizer': ^3.0.2 + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 - '@csstools/css-color-parser@3.0.4': - resolution: {integrity: sha512-kXviLfsxXmx2YcUPd478vuJd/s21EFTmxcgjC3danRhLa2zqfqZMTRonwRRSckezmgn7nlOCXpk3tZAKbFeihQ==} + '@csstools/css-color-parser@3.0.5': + resolution: {integrity: sha512-4Wo8raj9YF3PnZ5iGrAl+BSsk2MYBOEUS/X4k1HL9mInhyCVftEG02MywdvelXlwZGUF2XTQ0qj9Jd398mhqrw==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.3 - '@csstools/css-tokenizer': ^3.0.2 + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 - '@csstools/css-parser-algorithms@3.0.3': - resolution: {integrity: sha512-15WQTALDyxAwSgAvLt7BksAssiSrNNhTv4zM7qX9U6R7FtpNskVVakzWQlYODlwPwXhGpKPmB10LM943pxMe7w==} + '@csstools/css-parser-algorithms@3.0.4': + resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-tokenizer': ^3.0.2 + '@csstools/css-tokenizer': ^3.0.3 '@csstools/css-tokenizer@3.0.3': resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} engines: {node: '>=18'} - '@csstools/media-query-list-parser@4.0.1': - resolution: {integrity: sha512-dMr9PcN2B0TzxBFk6r+08Ln39aCti7SJeXB671JcXB1ZTPHqs4hpheRpL2vPPGRyXiQwW/UexvOej7Nw0Janxg==} + '@csstools/media-query-list-parser@4.0.2': + resolution: {integrity: sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.3 - '@csstools/css-tokenizer': ^3.0.2 + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 '@csstools/postcss-cascade-layers@5.0.1': resolution: {integrity: sha512-XOfhI7GShVcKiKwmPAnWSqd2tBR0uxt+runAxttbSp/LY2U16yAVPmAf7e9q4JJ0d+xMNmpwNDLBXnmRCl3HMQ==} @@ -1598,26 +1610,26 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-color-function@4.0.4': - resolution: {integrity: sha512-lL+ITQgwmAZd0/yBWkNIKzud2jQXeetFH9PtmQ/tWcD+FfQUjCGWZ8u6y6Pta64PbGPm1qn7+WgSNop+TC6pMQ==} + '@csstools/postcss-color-function@4.0.5': + resolution: {integrity: sha512-6dHr2NDsBMiZCPkGDi2qMfIbzV2kWV8Dh7SVb1FZGnN/r2TI4TSAkVF8rCG5L70yQZHMcQGB84yp8Zm+RGhoHA==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-color-mix-function@3.0.4': - resolution: {integrity: sha512-Jp6hI6T7Iq0+7VzEn5CbUymvo8W3x8xAJLVNRIQ/nn8iXsSprUtDo6DznDa7Uajz9qq70AwNK4Js1gmnZGKs3Q==} + '@csstools/postcss-color-mix-function@3.0.5': + resolution: {integrity: sha512-jgq0oGbit7TxWYP8y2hWWfV64xzcAgJk54PBYZ2fDrRgEDy1l5YMCrFawnn+5JETh/E1jjXPDFhFEYhwr3vA3g==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-content-alt-text@2.0.3': - resolution: {integrity: sha512-7fY4hfR77UezWoEu2NBMc550FL2NKr+FbcMdZLDIF5qkbn9rwW3l0+RXI7g6GmUPXeEwtVApp39xa55Cx1WKgw==} + '@csstools/postcss-content-alt-text@2.0.4': + resolution: {integrity: sha512-YItlZUOuZJCBlRaCf8Aucc1lgN41qYGALMly0qQllrxYJhiyzlI6RxOTMUvtWk+KhS8GphMDsDhKQ7KTPfEMSw==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-exponential-functions@2.0.3': - resolution: {integrity: sha512-7d626jcY3Za5uXoG3FQ4laZ9zjIpp2fzpqfAQO902n2p9nguaoCgfcM6cu9Ot+av2OEhf6YeaG69L0rhv2GfNg==} + '@csstools/postcss-exponential-functions@2.0.4': + resolution: {integrity: sha512-xmzFCGTkkLDs7q9vVaRGlnu8s51lRRJzHsaJ/nXmkQuyg0q7gh7rTbJ0bY5sSVet+KB7MTIxRXRUCl2tm7RODA==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -1628,20 +1640,20 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-gamut-mapping@2.0.4': - resolution: {integrity: sha512-3VidlUzT5VNKhxLSUS79B7EWk+KlF4cRdZPyg/T7q/QYI544a3o3/KoraEDw/np3Px1/9rljBJCgS5uNsRFBtQ==} + '@csstools/postcss-gamut-mapping@2.0.5': + resolution: {integrity: sha512-VQDayRhC/Mg1fuo8/4F43La5aROgvVyqtCqdNyGvRKi6L1+zXfwQ583nImi7k/gn2GNJH82Bf9mutTuT1GtXzA==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-gradients-interpolation-method@5.0.4': - resolution: {integrity: sha512-t2GrRZ/pnR7FJHvUoDl3gspwWGj2RCE7h9erAqs6eLp5oNh6qf7OzL6HwV6RcfGUjx49sliBmXxoDrReBuzncw==} + '@csstools/postcss-gradients-interpolation-method@5.0.5': + resolution: {integrity: sha512-l3ShDdAt/szbyBh3Jz27MRFt5WPAbnVCMsU7Vs7EbBxJQNgVDrcu1APBB2nPagDJOyhI6/IahuW7nb6grWVTpA==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-hwb-function@4.0.4': - resolution: {integrity: sha512-1kDydqBP16urjshTYdB28zSnWZXoTJyeToGhMkVEPDm4Mw9+JPe+PO2DZhqHXz2LzAMiHMAgOwp3oCBN2MRwoQ==} + '@csstools/postcss-hwb-function@4.0.5': + resolution: {integrity: sha512-bPn/SQyiiYjWkwK2ykc7O9LliMR50YfUGukd6jQI2okHzB7NxNt/IS45tS1Muk7Hhf3B9Lbmg1Ofq36tBmM92Q==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -1664,8 +1676,8 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-light-dark-function@2.0.6': - resolution: {integrity: sha512-eo9WPWkFGEfbhOgfHrIFTZlK8goW/rLYRfM2r8Rghl1NTvXnQ8qpMEmd67iXwMdfoKl6nMWs5sTTVLflpa2+EA==} + '@csstools/postcss-light-dark-function@2.0.7': + resolution: {integrity: sha512-ZZ0rwlanYKOHekyIPaU+sVm3BEHCe+Ha0/px+bmHe62n0Uc1lL34vbwrLYn6ote8PHlsqzKeTQdIejQCJ05tfw==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -1694,20 +1706,20 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-logical-viewport-units@3.0.2': - resolution: {integrity: sha512-oog7VobKvrS34oyUKslI6wCphtJxx0ldiA8RToPQ0HXPWNiXXSM7IbgwOTImJKTIUjo3eL7o5uuPxeu5MsnkvA==} + '@csstools/postcss-logical-viewport-units@3.0.3': + resolution: {integrity: sha512-OC1IlG/yoGJdi0Y+7duz/kU/beCwO+Gua01sD6GtOtLi7ByQUpcIqs7UE/xuRPay4cHgOMatWdnDdsIDjnWpPw==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-media-minmax@2.0.3': - resolution: {integrity: sha512-+Vr5eQ/ZSL0hdARb/1sohoYtYnYxGi94HuzgmzjZ7jnruEDYJaWux6UtS2gXY/cWrsx/lmJCJNFJO87/5hcgCQ==} + '@csstools/postcss-media-minmax@2.0.4': + resolution: {integrity: sha512-zgdBOCI9aKoy5GK9tb/3ve0pl7vH0HJg7rfQEWT3TZiIKh7XEWucDSTSwnwgdgtgz50UxrOfbK+C59M+u2fE2Q==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 - '@csstools/postcss-media-queries-aspect-ratio-number-values@3.0.3': - resolution: {integrity: sha512-kyLO69jXq/BIkOJeCi7++uzarm9qb5La1K1cL36e+QUnV6wto7UtFuzjelT3PEuCnIikj9JCbDCYDfGzCmkhQw==} + '@csstools/postcss-media-queries-aspect-ratio-number-values@3.0.4': + resolution: {integrity: sha512-AnGjVslHMm5xw9keusQYvjVWvuS7KWK+OJagaG0+m9QnIjZsrysD2kJP/tr/UJIyYtMCtu8OkUd+Rajb4DqtIQ==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -1724,8 +1736,8 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-oklab-function@4.0.4': - resolution: {integrity: sha512-IDPtqifrFjIjdMBphc8ebbq7YdMReEBjkoEZOVrm1I+ZfclgMim9HAE7+V0zCFaP4WyKhVSodKAWWh5Uj4cDLA==} + '@csstools/postcss-oklab-function@4.0.5': + resolution: {integrity: sha512-19bsJQFyJNSEhpaVq0Mq1E0HDXfx8qMHa/bR1MaHr1UD4DWvM2/J6YXb9OVGS7eFl92Y3c84Yggn9uFv13vsiQ==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -1736,8 +1748,8 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-relative-color-syntax@3.0.4': - resolution: {integrity: sha512-vfjMNPHTZ3SZbTuZ30tNvplQuxEaubUugd4P6PeXfxSKcAMUUH1weVTMaY75MsT5RpHw0m7GRyLDNwwAKXGm1g==} + '@csstools/postcss-relative-color-syntax@3.0.5': + resolution: {integrity: sha512-5VrE4hAwv/ZpuL1Yo0ZGGFi1QPpIikp/rzz7LnpQ31ACQVRIA5/M9qZmJbRlZVsJ4bUFSQ3dq6kHSHrCt2uM6Q==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -1748,8 +1760,8 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-stepped-value-functions@4.0.3': - resolution: {integrity: sha512-xy/cT/a51xecPw0T2GIwuCTc4IwIB5woznFAbhOHaJvBi6cdUJyQPeUjwgpOQkA31JEl11T0oGRP0MBDEdLOrg==} + '@csstools/postcss-stepped-value-functions@4.0.4': + resolution: {integrity: sha512-JjShuWZkmIOT8EfI7lYjl7V5qM29LNDdnnSo5O7v/InJJHfeiQjtxyAaZzKGXzpkghPrCAcgLfJ+IyqTdXo7IA==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -1760,8 +1772,8 @@ packages: peerDependencies: postcss: ^8.4 - '@csstools/postcss-trigonometric-functions@4.0.3': - resolution: {integrity: sha512-OTtGIJglcGqSMyZo6yYrt7c+eOqI7N38oh3IWfpqrDnjFtqvR7n2fDSSYPrkR9KjT4alCXNPV9cC7ExXFCG6Uw==} + '@csstools/postcss-trigonometric-functions@4.0.4': + resolution: {integrity: sha512-nn+gWTZZlSnwbyUtGQCnvBXIx1TX+HVStvIm3221dWGQvp47bB5giMBbuAK4a/UJGBbfDQhGKEbYq++WWM1i1A==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -2816,6 +2828,10 @@ packages: resolution: {integrity: sha512-GJvX9iM9PBtKScJVlXQ0tWpihK3i0pha/XAhzQa1hPK/ILLa1Wq3I63Ij7lRtqTwmdTxRCyrUhLC5Sly9SLbug==} engines: {node: '>=18.0.0', npm: '>=9.0.0'} + '@faker-js/faker@9.2.0': + resolution: {integrity: sha512-ulqQu4KMr1/sTFIYvqSdegHT8NIkt66tFAkugGnHA+1WAfEn6hMzNR+svjXGFRVLnapxvej67Z/LwchFrnLBUg==} + engines: {node: '>=18.0.0', npm: '>=9.0.0'} + '@floating-ui/core@1.6.8': resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} @@ -2855,6 +2871,12 @@ packages: peerDependencies: hono: ^4 + '@hono/node-server@1.13.5': + resolution: {integrity: sha512-lSo+CFlLqAFB4fX7ePqI9nauEn64wOfJHAfc9duYFTvAG3o416pC0nTGeNjuLHchLedH+XyWda5v79CVx1PIjg==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@hono/sentry@1.2.0': resolution: {integrity: sha512-9mS8GrkGtR4YxM1CViL4Ft8LFQ9YhCoXeqKnUA1AUmrvA5PhUU/V+xoo8Autw0nVriin3liX5/lPrwWz3muwiw==} peerDependencies: @@ -3634,8 +3656,8 @@ packages: '@oslojs/webauthn@1.0.0': resolution: {integrity: sha512-2ZRpbt3msNURwvjmavzq9vrNlxUnWFBGMYqbC1kO3fYBLskL7r4DiLJT1wbtLoI+hclFwjhl48YhRFBl6RWg1A==} - '@paddle/paddle-js@1.3.1': - resolution: {integrity: sha512-q2b2aO0dlEPnqiF3dovFIL3QIOwYNbTTPARU9qds9BAnVeUFC9YaKWobfPJaMlKHknPViT0SrAcw+KzA44FNtg==} + '@paddle/paddle-js@1.3.2': + resolution: {integrity: sha512-N0/Vd3mmnk0zk1+xY+61xBD7bDW0gFAZjyMXvZvF6UDDAKIuNNkKYsImGvMSCG9zKvCMRGAw4fJBwe2mG01Bbg==} '@paddle/paddle-node-sdk@1.9.1': resolution: {integrity: sha512-MB3jf0lpYr7xbBLNzsAecqxv2dirOsgPfp3YAINLaNYdNCoyFjZjDRJ/a6KJucd3sYQJEozaIqUnCrP89wcCNQ==} @@ -4831,6 +4853,11 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.24.4': + resolution: {integrity: sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.24.0': resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} cpu: [arm64] @@ -4841,6 +4868,11 @@ packages: cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.24.4': + resolution: {integrity: sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.24.0': resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} cpu: [arm64] @@ -4851,6 +4883,11 @@ packages: cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.24.4': + resolution: {integrity: sha512-GmU/QgGtBTeraKyldC7cDVVvAJEOr3dFLKneez/n7BvX57UdhOqDsVwzU7UOnYA7AAOt+Xb26lk79PldDHgMIQ==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.24.0': resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} cpu: [x64] @@ -4861,16 +4898,31 @@ packages: cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.24.4': + resolution: {integrity: sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.24.3': resolution: {integrity: sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.24.4': + resolution: {integrity: sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.24.3': resolution: {integrity: sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.24.4': + resolution: {integrity: sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} cpu: [arm] @@ -4881,6 +4933,11 @@ packages: cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.24.4': + resolution: {integrity: sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.24.0': resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} cpu: [arm] @@ -4891,6 +4948,11 @@ packages: cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.24.4': + resolution: {integrity: sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.24.0': resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} cpu: [arm64] @@ -4901,6 +4963,11 @@ packages: cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.24.4': + resolution: {integrity: sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.24.0': resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} cpu: [arm64] @@ -4911,6 +4978,11 @@ packages: cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.24.4': + resolution: {integrity: sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} cpu: [ppc64] @@ -4921,6 +4993,11 @@ packages: cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.24.4': + resolution: {integrity: sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.24.0': resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} cpu: [riscv64] @@ -4931,6 +5008,11 @@ packages: cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.24.4': + resolution: {integrity: sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.24.0': resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} cpu: [s390x] @@ -4941,6 +5023,11 @@ packages: cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.24.4': + resolution: {integrity: sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.24.0': resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} cpu: [x64] @@ -4951,6 +5038,11 @@ packages: cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.24.4': + resolution: {integrity: sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.24.0': resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} cpu: [x64] @@ -4961,6 +5053,11 @@ packages: cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.24.4': + resolution: {integrity: sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q==} + cpu: [x64] + os: [linux] + '@rollup/rollup-win32-arm64-msvc@4.24.0': resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} cpu: [arm64] @@ -4971,6 +5068,11 @@ packages: cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.24.4': + resolution: {integrity: sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.24.0': resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} cpu: [ia32] @@ -4981,6 +5083,11 @@ packages: cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.24.4': + resolution: {integrity: sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.24.0': resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} cpu: [x64] @@ -4991,6 +5098,11 @@ packages: cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.24.4': + resolution: {integrity: sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==} + cpu: [x64] + os: [win32] + '@rrweb/types@2.0.0-alpha.16': resolution: {integrity: sha512-E6cACNVsm+NUhn7dzocQoKyXI7BHrHRRm5Ab23yrAzEQ2caWocCEYJhqDlc4KRVJBkQfXZfyWm8+2d0uggFuZg==} @@ -5023,28 +5135,28 @@ packages: resolution: {integrity: sha512-MUpIZykD9ARie8LElYCqbcBhGGMaA/E6I7fEcG7Hc2An26QJyLtwOaKQ3taGp8xO8BICPJrSKuYV4bDeAJKFGQ==} engines: {node: '>=12.*'} - '@sentry-internal/browser-utils@8.36.0': - resolution: {integrity: sha512-AVJ9GmQW7jYxaal6hjQnnktsDNype01ajVC4q1RyOn1SfzSnXg6mXwj4xm4ovuJV+aBI7fAZJ55vEX5ASuP0ZA==} + '@sentry-internal/browser-utils@8.37.1': + resolution: {integrity: sha512-OSR/V5GCsSCG7iapWtXCT/y22uo3HlawdEgfM1NIKk1mkP15UyGQtGEzZDdih2H+SNuX1mp9jQLTjr5FFp1A5w==} engines: {node: '>=14.18'} - '@sentry-internal/feedback@8.36.0': - resolution: {integrity: sha512-aAMTm3uDBj8Ta7FwoohpLmJOpWzpWXvvtTbtmSgkeCtPJLUS8DZDCTZ9uCILUkpuYrv2savRUHsdPkxNjgL8FA==} + '@sentry-internal/feedback@8.37.1': + resolution: {integrity: sha512-Se25NXbSapgS2S+JssR5YZ48b3OY4UGmAuBOafgnMW91LXMxRNWRbehZuNUmjjHwuywABMxjgu+Yp5uJDATX+g==} engines: {node: '>=14.18'} - '@sentry-internal/replay-canvas@8.36.0': - resolution: {integrity: sha512-KJPLf+qYdrQdmouoAqIPZ2KeapIBlHWbzNdQqNxJFWLHFFjpLUtt0b+87ruvbA/q3NYy2fDwD7EB0tGS1RHBaA==} + '@sentry-internal/replay-canvas@8.37.1': + resolution: {integrity: sha512-1JLAaPtn1VL5vblB0BMELFV0D+KUm/iMGsrl4/JpRm0Ws5ESzQl33DhXVv1IX/ZAbx9i14EjR7MG9+Hj70tieQ==} engines: {node: '>=14.18'} - '@sentry-internal/replay@8.36.0': - resolution: {integrity: sha512-lbic98GsSkDeinQDix54tBFEgHUlmBtO+HjXECk9jIE0vOzR4As20/s5ta46t1rKMLlnxOtJuT5jKXeUYogBUw==} + '@sentry-internal/replay@8.37.1': + resolution: {integrity: sha512-E/Plhisk/pXJjOdOU12sg8m/APTXTA21iEniidP6jW3/+O0tD/H/UovEqa4odNTqxPMa798xHQSQNt5loYiaLA==} engines: {node: '>=14.18'} '@sentry/babel-plugin-component-annotate@2.22.6': resolution: {integrity: sha512-V2g1Y1I5eSe7dtUVMBvAJr8BaLRr4CLrgNgtPaZyMT4Rnps82SrZ5zqmEkLXPumlXhLUWR6qzoMNN2u+RXVXfQ==} engines: {node: '>= 14'} - '@sentry/browser@8.36.0': - resolution: {integrity: sha512-bLrQNe+wD4DkCfB8OD5TF3Rr8KA2+aTo5wF3t3Bf6KVn8//iX1ia1hhtptYiRnbRkG/0AEPxlqL6XfPZYVPQ5A==} + '@sentry/browser@8.37.1': + resolution: {integrity: sha512-5ym+iGiIpjIKKpMWi9S3/tXh9xneS+jqxwRTJqed3cb8i4ydfMAAP8sM3U8xMCWWABpWyIUW+fpewC0tkhE1aQ==} engines: {node: '>=14.18'} '@sentry/bundler-plugin-core@2.22.6': @@ -5056,71 +5168,117 @@ packages: engines: {node: '>=10'} os: [darwin] + '@sentry/cli-darwin@2.38.2': + resolution: {integrity: sha512-21ywIcJCCFrCTyiF1o1PaT7rbelFC2fWmayKYgFElnQ55IzNYkcn8BYhbh/QknE0l1NBRaeWMXwTTdeoqETCCg==} + engines: {node: '>=10'} + os: [darwin] + '@sentry/cli-linux-arm64@2.38.1': resolution: {integrity: sha512-3bj5DS4wDusL0YHwG5qeI+O19kz4N4KDDmnWqIew56MmSSAEM5B0qKk5Hivu1vRU5vPKFwVn8BVjLnKXu9idjg==} engines: {node: '>=10'} cpu: [arm64] os: [linux, freebsd] + '@sentry/cli-linux-arm64@2.38.2': + resolution: {integrity: sha512-4Fp/jjQpNZj4Th+ZckMQvldAuuP0ZcyJ9tJCP1CCOn5poIKPYtY6zcbTP036R7Te14PS4ALOcDNX3VNKfpsifA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux, freebsd] + '@sentry/cli-linux-arm@2.38.1': resolution: {integrity: sha512-xyf4f56O4/eeirol8t1tTQw0cwF34b3v69zn6wHtKfx2lW5IEBGO+agVNdOdosnCx6j3UadgdRXUJlSyM9kx/w==} engines: {node: '>=10'} cpu: [arm] os: [linux, freebsd] + '@sentry/cli-linux-arm@2.38.2': + resolution: {integrity: sha512-+AiKDBQKIdQe4NhBiHSHGl0KR+b//HHTrnfK1SaTrOm9HtM4ELXAkjkRF3bmbpSzSQCS5WzcbIxxCJOeaUaO0A==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux, freebsd] + '@sentry/cli-linux-i686@2.38.1': resolution: {integrity: sha512-VygJO2oTc6GfiqqmPYNpO2bW1hzszuNyn37SSmeRuuhq1/kRwD+ZQj4OmXYEASjSLg+8mDPoWOurPjHEPKNtNw==} engines: {node: '>=10'} cpu: [x86, ia32] os: [linux, freebsd] + '@sentry/cli-linux-i686@2.38.2': + resolution: {integrity: sha512-6zVJN10dHIn4R1v+fxuzlblzVBhIVwsaN/S7aBED6Vn1HhAyAcNG2tIzeCLGeDfieYjXlE2sCI82sZkQBCbAGw==} + engines: {node: '>=10'} + cpu: [x86, ia32] + os: [linux, freebsd] + '@sentry/cli-linux-x64@2.38.1': resolution: {integrity: sha512-9SaPJK5yAGR7qGsDubTT9O7VpNQG9KIolCOov4xJU7scbmjGaFyYBm9c7ZIqbq6B+56YchPbtD0RewZC6CiF2w==} engines: {node: '>=10'} cpu: [x64] os: [linux, freebsd] + '@sentry/cli-linux-x64@2.38.2': + resolution: {integrity: sha512-4UiLu9zdVtqPeltELR5MDGKcuqAdQY9xz3emISuA6bm+MXGbt2W1WgX+XY3GElwjZbmH8qpyLUEd34sw6sdcbQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux, freebsd] + '@sentry/cli-win32-i686@2.38.1': resolution: {integrity: sha512-BVUM5y+ZDBK/LqyVvt0C7oolmg8aq7PI/u04/Pp6FLRExySqwyQim0vNyL2FRjIeX1yhbk7x4Z79UjEKqJBltA==} engines: {node: '>=10'} cpu: [x86, ia32] os: [win32] + '@sentry/cli-win32-i686@2.38.2': + resolution: {integrity: sha512-DYfSvd5qLPerLpIxj3Xu2rRe3CIlpGOOfGSNI6xvJ5D8j6hqbOHlCzvfC4oBWYVYGtxnwQLMeDGJ7o7RMYulig==} + engines: {node: '>=10'} + cpu: [x86, ia32] + os: [win32] + '@sentry/cli-win32-x64@2.38.1': resolution: {integrity: sha512-+HgsdM3LFSzUNlDpicPRdTKfr5u+nJ2C5p4aDYPb2G+qoYW+66FI4NxgWSyzJsj3nVQ8lW5/6AoMP6U5z/e/0A==} engines: {node: '>=10'} cpu: [x64] os: [win32] + '@sentry/cli-win32-x64@2.38.2': + resolution: {integrity: sha512-W5UX58PKY1hNUHo9YJxWNhGvgvv2uOYHI27KchRiUvFYBIqlUUcIdPZDfyzetDfd8qBCxlAsFnkL2VJSNdpA9A==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + '@sentry/cli@2.38.1': resolution: {integrity: sha512-XFO04nP7cn0tboMQ4ALR81QRF/6xoWAFzNld7Io6jHbaFzihqewjxAqy7pSvVPaieepUjqe7m/Ippt00kKOACg==} engines: {node: '>= 10'} hasBin: true - '@sentry/core@8.36.0': - resolution: {integrity: sha512-cbq1WQyRqc/+YpPhjwQxfniUM3ZxmO3Pm1oisTB8dw6mlbgQfGD6aznEIjXWWJY6k6acewJlMUx09N7DnprtBw==} + '@sentry/cli@2.38.2': + resolution: {integrity: sha512-CR0oujpAnhegK2pBAv6ZReMqbFTuNJLDZLvoD1B+syrKZX+R+oxkgT2e1htsBbht+wGxAsluVWsIAydSws1GAA==} + engines: {node: '>= 10'} + hasBin: true + + '@sentry/core@8.37.1': + resolution: {integrity: sha512-82csXby589iDupM3VgCHJeWZagUyEEaDnbFcoZ/Z91QX2Sjq8FcF5OsforoXjw09i0XTFqlkFAnQVpDBmMXcpQ==} engines: {node: '>=14.18'} '@sentry/core@8.9.2': resolution: {integrity: sha512-ixm8NISFlPlEo3FjSaqmq4nnd13BRHoafwJ5MG+okCz6BKGZ1SexEggP42/QpGvDprUUHnfncG6WUMgcarr1zA==} engines: {node: '>=14.18'} - '@sentry/react@8.36.0': - resolution: {integrity: sha512-YIJZUx7Q5aulK034cRri0p/7MeP3tdLfdP6vMJMwrVlqoWQI9gKZXikmLIqHUQegZdMRYX5tr03gTWJu3dhYwg==} + '@sentry/react@8.37.1': + resolution: {integrity: sha512-HanDqBFTgIUhUsYztAHhSti+sEhQ8YopAymXgnpqkJ7j1PLHXZgQAre6M4Uvixu28WS5MDHC1onnAIBDgYRDYw==} engines: {node: '>=14.18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/types@8.36.0': - resolution: {integrity: sha512-K1pVFfdGHw115RzGHpwSOqoEPeayn4N1F9IfM0kxrYpQSbFT1X29eak88GBfC8gPiLEF0iFGlSaQ4ERmF7oRcA==} + '@sentry/types@8.37.1': + resolution: {integrity: sha512-ryMOTROLSLINKFEbHWvi7GigNrsQhsaScw2NddybJGztJQ5UhxIGESnxGxWCufBmWFDwd7+5u0jDPCVUJybp7w==} engines: {node: '>=14.18'} '@sentry/types@8.9.2': resolution: {integrity: sha512-+LFOyQGl+zk5SZRGZD2MEURf7i5RHgP/mt3s85Rza+vz8M211WJ0YsjkIGUJFSY842nged5QLx4JysLaBlLymg==} engines: {node: '>=14.18'} - '@sentry/utils@8.36.0': - resolution: {integrity: sha512-oJ3EDPj0I00z+AwC3EWBpSidXYUoKW0Id8MfMQP5Hflniz3gif7UEReblT+FJgPEVo6+6uNzAncY0MuNMxmDKQ==} + '@sentry/utils@8.37.1': + resolution: {integrity: sha512-Qtn2IfpII12K17txG/ZtTci35XYjYi4CxbQ3j7nXY7toGv/+MqPXwV5q2i9g94XaSXlE5Wy9/hoCZoZpZs/djA==} engines: {node: '>=14.18'} '@sentry/utils@8.9.2': @@ -5455,40 +5613,40 @@ packages: resolution: {integrity: sha512-2CqERleeqO3hkhJmyJm37tiL3LYgeOpmo8szqdjgtnnG0z7ZpvzkZz6HkfOr9Ca/ha7mhAiouSvLYuLkM37AMg==} engines: {node: '>=12'} - '@tanstack/query-core@5.59.16': - resolution: {integrity: sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==} + '@tanstack/query-core@5.59.20': + resolution: {integrity: sha512-e8vw0lf7KwfGe1if4uPFhvZRWULqHjFcz3K8AebtieXvnMOz5FSzlZe3mTLlPuUBcydCnBRqYs2YJ5ys68wwLg==} - '@tanstack/query-devtools@5.58.0': - resolution: {integrity: sha512-iFdQEFXaYYxqgrv63ots+65FGI+tNp5ZS5PdMU1DWisxk3fez5HG3FyVlbUva+RdYS5hSLbxZ9aw3yEs97GNTw==} + '@tanstack/query-devtools@5.59.20': + resolution: {integrity: sha512-vxhuQ+8VV4YWQSFxQLsuM+dnEKRY7VeRzpNabFXdhEwsBYLrjXlF1pM38A8WyKNLqZy8JjyRO8oP4Wd/oKHwuQ==} - '@tanstack/query-persist-client-core@5.59.16': - resolution: {integrity: sha512-UXu9GEOXAlH4pROhct+zBuMeTXUIvHh9ECfKs8Ux2RgBFBs4hwUqlhUNjb/ezji4lxF+F0Dqtg48O60ii3DArg==} + '@tanstack/query-persist-client-core@5.59.20': + resolution: {integrity: sha512-RUaDys2zyhCw8MGcp0tirbpp8IjU7zrtdMaEYQ6WetrNvn/IOg9Y2Zpk55P7gjBq8fEyFlmuRM3cHVNn/Usg8w==} - '@tanstack/query-sync-storage-persister@5.59.16': - resolution: {integrity: sha512-5GRazdusJaH1lKTQmfQxXmhP9do+2smPazrZfYTvZBSabGdp3ZEf7J67IIG+ZrkYDW5jJj9lVulVHU8COvt47A==} + '@tanstack/query-sync-storage-persister@5.59.20': + resolution: {integrity: sha512-uM4vxU2jSRE3lPOysi7GWpSJsUz9ZT6mZKX2dONfzIVng/7baW3Q3L/cyTUyLQtvERiuvbbhgCCTa8w8ZOeyaA==} - '@tanstack/react-query-devtools@5.59.16': - resolution: {integrity: sha512-Dejo39QBXmDqXZ3vdrk7vHDvs7TvL573/AX2NveMBmRAufAPYuE3oWSKP/gGqkDfEqyr4CmldOj+v9cKskUchQ==} + '@tanstack/react-query-devtools@5.59.20': + resolution: {integrity: sha512-AL/eQS1NFZhwwzq2Bq9Gd8wTTH+XhPNOJlDFpzPMu9NC5CQVgA0J8lWrte/sXpdWNo5KA4hgHnEdImZsF4h6Lw==} peerDependencies: - '@tanstack/react-query': ^5.59.16 + '@tanstack/react-query': ^5.59.20 react: ^18 || ^19 - '@tanstack/react-query-persist-client@5.59.16': - resolution: {integrity: sha512-JtcCHtGyRmwuSf/wzWfRqbTbq3C+fffaOZsSSWm/AXybql/Q+iA6TCqSS+vXX8s40o2yN4aKstr+W8pfn2qfsg==} + '@tanstack/react-query-persist-client@5.59.20': + resolution: {integrity: sha512-7dV9wGs9f8IvGysfmsmLcQztHjULOMB30HmERo8yjwVHRKhoQvdtKTvQ9aX2lSP5zzP1Qd1/IHF2hBB11Q/rwA==} peerDependencies: - '@tanstack/react-query': ^5.59.16 + '@tanstack/react-query': ^5.59.20 react: ^18 || ^19 - '@tanstack/react-query@5.59.16': - resolution: {integrity: sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==} + '@tanstack/react-query@5.59.20': + resolution: {integrity: sha512-Zly0egsK0tFdfSbh5/mapSa+Zfc3Et0Zkar7Wo5sQkFzWyB3p3uZWOHR2wrlAEEV2L953eLuDBtbgFvMYiLvUw==} peerDependencies: react: ^18 || ^19 - '@tanstack/react-router@1.78.0': - resolution: {integrity: sha512-yVIi6EJ3HzQxfdeWzfytSYBEELaHgvVL8xUoKWHeShcey4ChegGF0oSXhZ6dTiu8ODu0CkHKInTjgzjrlMO14Q==} + '@tanstack/react-router@1.79.0': + resolution: {integrity: sha512-v+0+Y3mCsd6waphnG0dloU9cDtSbB5k/LwbkhqXd2uGhwZyI1/nvvOcSOnuTB8CDEvC6WFNBt+fKOPVqNZ3ezw==} engines: {node: '>=12'} peerDependencies: - '@tanstack/router-generator': 1.78.0 + '@tanstack/router-generator': 1.79.0 react: '>=18' react-dom: '>=18' peerDependenciesMeta: @@ -5501,20 +5659,20 @@ packages: react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - '@tanstack/router-devtools@1.78.0': - resolution: {integrity: sha512-9NTBXKL3jqHEMPTyy/f4ibDxKL5aEyGMt+SxE4fJUeaG8cZeko8cZwcRuV+a2/5OBrvxzEBMa4enmKY8PvW19Q==} + '@tanstack/router-devtools@1.79.0': + resolution: {integrity: sha512-VOMED13hTuJzXmR1csKuREbWkRy57sDHWz0nkdF5sEusQGr1J156GQUpNXShgBfAaz9cvH0qAH+5y6aRAQtILw==} engines: {node: '>=12'} peerDependencies: - '@tanstack/react-router': ^1.78.0 + '@tanstack/react-router': ^1.79.0 react: '>=18' react-dom: '>=18' - '@tanstack/router-generator@1.78.0': - resolution: {integrity: sha512-hrP3M/oh+Lvli6oVH2bItXFHsWKHkL758efPKIyfmvp7Fk9Lm+eL0AVcxF8w9KzS+jtlzw0aPYVV7KH7Vu0URA==} + '@tanstack/router-generator@1.79.0': + resolution: {integrity: sha512-HJxmYs7GAA1AJQzyfy4Hiygmg93qCCDiAxQ//zCRMbzVntwpqtZ96o9UGOPjT3Lw0SxbyzbKgpo3zqCdwlv8Ew==} engines: {node: '>=12'} - '@tanstack/router-plugin@1.78.0': - resolution: {integrity: sha512-ep3FYZzH/DYxZE/Pg4VyiYUqNfvoNXzmvw0hdHliTOxgsJdOs71LKcJokzeLdNijQOgud+CDurTWTknrRBOgeA==} + '@tanstack/router-plugin@1.79.0': + resolution: {integrity: sha512-dY81YyKxON9KhZQlrkkuxsl688pGpZ4HAF5w40ZkJa+nwmEJdg0b2td+MPXWbtmSd1t1cbYlFvc68k+PUSHN/A==} engines: {node: '>=12'} peerDependencies: '@rsbuild/core': '>=1.0.2' @@ -5687,6 +5845,10 @@ packages: resolution: {integrity: sha512-QHWO5uhCtpDPQoM5ZFnTpDoa1qI2qNNKx9rNQmcvRQYWxsK5lNR44+EZVgMafYz26iFe9Myi54xU93wo/NzFoA==} engines: {node: '>=16'} + '@tus/server@1.9.0': + resolution: {integrity: sha512-4eSEKqTjndVRq3is1r99ubN37BaI+u/76Xkcw19joh35yf/pGfOg4rv8QjGmTZgTyzFJvT5P/ny9hYD4ILJL2Q==} + engines: {node: '>=16'} + '@tus/utils@0.4.0': resolution: {integrity: sha512-Lc+t/D4IyB/iV70DNALN851yPcqP/FYp1prA3jQHM2vn0OicA7qpFjMaM+1/XRDkeF2b75gDGHRfw8S4sVjs8g==} engines: {node: '>=16'} @@ -5796,6 +5958,9 @@ packages: '@types/node@22.8.6': resolution: {integrity: sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==} + '@types/node@22.9.0': + resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} + '@types/papaparse@5.3.15': resolution: {integrity: sha512-JHe6vF6x/8Z85nCX4yFdDslN11d+1pr12E526X8WAfhadOeaOTx5AuIkvDKIBopfvlzpzkdMx4YyvSKCM9oqtw==} @@ -6346,8 +6511,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001676: - resolution: {integrity: sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==} + caniuse-lite@1.0.30001677: + resolution: {integrity: sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==} canvas@2.11.2: resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} @@ -6464,8 +6629,8 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 - cmdk@1.0.3: - resolution: {integrity: sha512-2c3uTjwT4YeHj60q2k8S1B0WHSoGR6t5CPnec6PMFD2QF4gwid0t1VSPNeEmL02EwBwNky/A3gwPCOViKTtoPA==} + cmdk@1.0.4: + resolution: {integrity: sha512-AnsjfHyHpQ/EFeAnG216WY7A5LiYCoZzCSygiLvfXC3H3LFGCprErteUcszaVluGOhuOTbJS3jWHrSDYPBBygg==} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc @@ -7001,16 +7166,16 @@ packages: engines: {node: '>=14'} hasBin: true - effect@3.10.8: - resolution: {integrity: sha512-vCKKp7EL6j7x1xfBbmndcQvooNE5FEDxkVro2yqEGBR2Ogn4I4XVK5A4eLVlDxS9egvX7EFaBwDRfm7k6IM6oA==} + effect@3.10.12: + resolution: {integrity: sha512-+IqqTQS5+jM13qZHzfAX+RpCGOl0oBWIR7nec/zq76HyN/hdGQYnfr/LLLOVxUt8uIbckBea8rkwBKrQ0Hb/aA==} ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.50: - resolution: {integrity: sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==} + electron-to-chromium@1.5.52: + resolution: {integrity: sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==} elliptic@6.6.0: resolution: {integrity: sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==} @@ -7202,8 +7367,8 @@ packages: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} - fast-check@3.22.0: - resolution: {integrity: sha512-8HKz3qXqnHYp/VCNn2qfjHdAdcI8zcSqOyX64GOMukp7SL2bfzfeDKjSd+UyECtejccaZv3LcvZTm9YDD22iCQ==} + fast-check@3.23.1: + resolution: {integrity: sha512-u/MudsoQEgBUZgR5N1v87vEgybeVYus9VnDVaIkxkkGP2jt54naghQ3PCQHJiogS8U/GavZCUPFfx3Xkp+NaHw==} engines: {node: '>=8.0.0'} fast-deep-equal@3.1.3: @@ -9027,8 +9192,8 @@ packages: peerDependencies: postcss: ^8.4.6 - postcss-color-functional-notation@7.0.4: - resolution: {integrity: sha512-bK5EYM9f/F8zqbVT+Etky6sZBR3XedXRasF0cFxi2uX3JOKrkEw+YfRFaVLAYA934RuypGZiqTgDXVpVPnaoDQ==} + postcss-color-functional-notation@7.0.5: + resolution: {integrity: sha512-zW97tq5t2sSSSZQcIS4y6NDZj79zVv8hrBIJ4PSFZFmMBcjYqCt8sRXFGIYZohCpfFHmimMNqJje2Qd3qqMNdg==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -9045,20 +9210,20 @@ packages: peerDependencies: postcss: ^8.4 - postcss-custom-media@11.0.4: - resolution: {integrity: sha512-fz6+8rikAQZHsDwy2EEdeE0JlOaYRz1O0WNyrENkC21nEQfp2etnLcP4V1igieGG5mKokfLmH6lLrBR8kMRUfA==} + postcss-custom-media@11.0.5: + resolution: {integrity: sha512-SQHhayVNgDvSAdX9NQ/ygcDQGEY+aSF4b/96z7QUX6mqL5yl/JgG/DywcF6fW9XbnCRE+aVYk+9/nqGuzOPWeQ==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 - postcss-custom-properties@14.0.3: - resolution: {integrity: sha512-zCc5y6cilcZXld3RK0glb5OR9p6i/54ro7Dul2drDI7kLCIZC1uiblHGociomp2fwBet3kRFf9DpG4lJtz5yhw==} + postcss-custom-properties@14.0.4: + resolution: {integrity: sha512-QnW8FCCK6q+4ierwjnmXF9Y9KF8q0JkbgVfvQEMa93x1GT8FvOiUevWCN2YLaOWyByeDX8S6VFbZEeWoAoXs2A==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 - postcss-custom-selectors@8.0.3: - resolution: {integrity: sha512-VozjI6h5AxtMWtsI7IdP/LYpioe2Ha0Cg0JwHiifIyIM/HIoRGcRPnbbrywbbG6uPagJH/l2xIOyVddAIqB/KA==} + postcss-custom-selectors@8.0.4: + resolution: {integrity: sha512-ASOXqNvDCE0dAJ/5qixxPeL1aOVGHGW2JwSy7HyjWNbnWTQCl+fDc968HY1jCmZI0+BaYT5CxsOiUhavpG/7eg==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -9122,8 +9287,8 @@ packages: peerDependencies: postcss: ^8.4.21 - postcss-lab-function@7.0.4: - resolution: {integrity: sha512-BkNIkLVZDPJo5EYTfdri/tllk1y83zZET9Imn6gbt8YmeK4SnOiLN8Tfr3DSFk4sIHYbuuQp5UmPXsb9J2mNBQ==} + postcss-lab-function@7.0.5: + resolution: {integrity: sha512-q2M8CfQbjHxbwv1GPAny05EVuj0WByUgq/OWKgpfbTHnMchtUqsVQgaW1mztjSZ4UPufwuTLB14fmFGsoTE/VQ==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -9211,8 +9376,8 @@ packages: peerDependencies: postcss: ^8.4 - postcss-preset-env@10.0.8: - resolution: {integrity: sha512-rN7wmrc4GDvsCR8o1J0c0lexJI7x7ibCoSJ6Xoz/lAyzXzJhq6MYtfQGby5hMU0eqQTQc8JDEcREJaA7kYy7aQ==} + postcss-preset-env@10.0.9: + resolution: {integrity: sha512-mpfJWMAW6szov+ifW9HpNUUZE3BoXoHc4CDzNQHdH2I4CwsqulQ3bpFNUR6zh4tg0BUcqM7UUAbzG4UTel8QYw==} engines: {node: '>=18'} peerDependencies: postcss: ^8.4 @@ -9406,8 +9571,8 @@ packages: prosemirror-transform@1.10.2: resolution: {integrity: sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==} - prosemirror-view@1.35.0: - resolution: {integrity: sha512-Umtbh22fmUlpZpRTiOVXA0PpdRZeYEeXQsLp51VfnMhjkJrqJ0n8APinIZrRAD5Jr3UxH8FnOaUqRylSuMsqHA==} + prosemirror-view@1.36.0: + resolution: {integrity: sha512-U0GQd5yFvV5qUtT41X1zCQfbw14vkbbKwLlQXhdylEmgpYVHkefXYcC4HHwWOfZa3x6Y8wxDLUBv7dxN5XQ3nA==} proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -9491,8 +9656,8 @@ packages: react: ^18.0 || ^19.0 react-dom: ^18.0 || ^19.0 - react-day-picker@9.2.1: - resolution: {integrity: sha512-rCoK4oJi9NBXt1nNdQFSa7gBG+hWsqVCtoLFLxvMzkVxDp+rSqsuUQ0LccJyLigwp/hX8XnAokTsT03+5lbjyA==} + react-day-picker@9.3.0: + resolution: {integrity: sha512-xXgZISTXlwQ1Igt4cBttXF+aK1Xvd00azcGVY74PNCAe8PxtULFVWGT1UfdavFiVScF04dyV8QcybKZAw570QQ==} engines: {node: '>=18'} peerDependencies: react: '>=16.8.0' @@ -9693,8 +9858,8 @@ packages: recharts-scale@0.4.5: resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} - recharts@2.13.2: - resolution: {integrity: sha512-UDLGFmnsBluDIPpQb9uty0ejb+jiVI71vkki8vVsR6ZCJdgjBfKQoQfft4re99CKlTy9qjQApxCLG6TrxJkeAg==} + recharts@2.13.3: + resolution: {integrity: sha512-YDZ9dOfK9t3ycwxgKbrnDlRC4BHdjlY73fet3a0C1+qGMjXVZe6+VXmpOIIhzkje5MMEL8AN4hLIe4AMskBzlA==} engines: {node: '>=14'} peerDependencies: react: ^16.0.0 || ^17.0.0 || ^18.0.0 @@ -9928,6 +10093,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.24.4: + resolution: {integrity: sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rope-sequence@1.3.4: resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} @@ -10093,11 +10263,11 @@ packages: resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} engines: {node: '>=10.0.0'} - sonner@1.5.0: - resolution: {integrity: sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA==} + sonner@1.7.0: + resolution: {integrity: sha512-W6dH7m5MujEPyug3lpI2l3TC3Pp1+LTgK0Efg+IHDrBbtEjyCmCHHo6yfNBOsf1tFZ6zf+jceWwB38baC8yO9g==} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} @@ -10219,8 +10389,8 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - sugar-high@0.7.4: - resolution: {integrity: sha512-dG0B/JbpFVW3D7BiaNLUi1NTYN7+gzYMPBOJJDsfwDWbe2S9g+ftfKBmooeIdNCMb1O19YSqFeVUXl3ykuHKYA==} + sugar-high@0.7.5: + resolution: {integrity: sha512-lfGxo0il0Mx4WLdXEt0WsJ8V3QkQWssXnolj5xBurzlGJW07LuwmJWKtS0B2WJ5XWz1439RHngXAmzsnLD0rFA==} superstruct@1.0.4: resolution: {integrity: sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==} @@ -11202,20 +11372,20 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -11225,7 +11395,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -11233,7 +11403,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -11242,36 +11412,36 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.678.0': + '@aws-sdk/client-s3@3.685.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.678.0(@aws-sdk/client-sts@3.678.0) - '@aws-sdk/client-sts': 3.678.0 - '@aws-sdk/core': 3.678.0 - '@aws-sdk/credential-provider-node': 3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0))(@aws-sdk/client-sts@3.678.0) - '@aws-sdk/middleware-bucket-endpoint': 3.667.0 - '@aws-sdk/middleware-expect-continue': 3.667.0 - '@aws-sdk/middleware-flexible-checksums': 3.678.0 - '@aws-sdk/middleware-host-header': 3.667.0 - '@aws-sdk/middleware-location-constraint': 3.667.0 - '@aws-sdk/middleware-logger': 3.667.0 - '@aws-sdk/middleware-recursion-detection': 3.667.0 - '@aws-sdk/middleware-sdk-s3': 3.678.0 - '@aws-sdk/middleware-ssec': 3.667.0 - '@aws-sdk/middleware-user-agent': 3.678.0 - '@aws-sdk/region-config-resolver': 3.667.0 - '@aws-sdk/signature-v4-multi-region': 3.678.0 - '@aws-sdk/types': 3.667.0 - '@aws-sdk/util-endpoints': 3.667.0 - '@aws-sdk/util-user-agent-browser': 3.675.0 - '@aws-sdk/util-user-agent-node': 3.678.0 - '@aws-sdk/xml-builder': 3.662.0 + '@aws-sdk/client-sso-oidc': 3.682.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/client-sts': 3.682.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/credential-provider-node': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/middleware-bucket-endpoint': 3.679.0 + '@aws-sdk/middleware-expect-continue': 3.679.0 + '@aws-sdk/middleware-flexible-checksums': 3.682.0 + '@aws-sdk/middleware-host-header': 3.679.0 + '@aws-sdk/middleware-location-constraint': 3.679.0 + '@aws-sdk/middleware-logger': 3.679.0 + '@aws-sdk/middleware-recursion-detection': 3.679.0 + '@aws-sdk/middleware-sdk-s3': 3.685.0 + '@aws-sdk/middleware-ssec': 3.679.0 + '@aws-sdk/middleware-user-agent': 3.682.0 + '@aws-sdk/region-config-resolver': 3.679.0 + '@aws-sdk/signature-v4-multi-region': 3.685.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-endpoints': 3.679.0 + '@aws-sdk/util-user-agent-browser': 3.679.0 + '@aws-sdk/util-user-agent-node': 3.682.0 + '@aws-sdk/xml-builder': 3.679.0 '@smithy/config-resolver': 3.0.10 '@smithy/core': 2.5.1 '@smithy/eventstream-serde-browser': 3.0.11 @@ -11309,22 +11479,22 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0)': + '@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.678.0 - '@aws-sdk/core': 3.678.0 - '@aws-sdk/credential-provider-node': 3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0))(@aws-sdk/client-sts@3.678.0) - '@aws-sdk/middleware-host-header': 3.667.0 - '@aws-sdk/middleware-logger': 3.667.0 - '@aws-sdk/middleware-recursion-detection': 3.667.0 - '@aws-sdk/middleware-user-agent': 3.678.0 - '@aws-sdk/region-config-resolver': 3.667.0 - '@aws-sdk/types': 3.667.0 - '@aws-sdk/util-endpoints': 3.667.0 - '@aws-sdk/util-user-agent-browser': 3.675.0 - '@aws-sdk/util-user-agent-node': 3.678.0 + '@aws-sdk/client-sts': 3.682.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/credential-provider-node': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/middleware-host-header': 3.679.0 + '@aws-sdk/middleware-logger': 3.679.0 + '@aws-sdk/middleware-recursion-detection': 3.679.0 + '@aws-sdk/middleware-user-agent': 3.682.0 + '@aws-sdk/region-config-resolver': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-endpoints': 3.679.0 + '@aws-sdk/util-user-agent-browser': 3.679.0 + '@aws-sdk/util-user-agent-node': 3.682.0 '@smithy/config-resolver': 3.0.10 '@smithy/core': 2.5.1 '@smithy/fetch-http-handler': 3.2.9 @@ -11354,20 +11524,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.678.0': + '@aws-sdk/client-sso@3.682.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.678.0 - '@aws-sdk/middleware-host-header': 3.667.0 - '@aws-sdk/middleware-logger': 3.667.0 - '@aws-sdk/middleware-recursion-detection': 3.667.0 - '@aws-sdk/middleware-user-agent': 3.678.0 - '@aws-sdk/region-config-resolver': 3.667.0 - '@aws-sdk/types': 3.667.0 - '@aws-sdk/util-endpoints': 3.667.0 - '@aws-sdk/util-user-agent-browser': 3.675.0 - '@aws-sdk/util-user-agent-node': 3.678.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/middleware-host-header': 3.679.0 + '@aws-sdk/middleware-logger': 3.679.0 + '@aws-sdk/middleware-recursion-detection': 3.679.0 + '@aws-sdk/middleware-user-agent': 3.682.0 + '@aws-sdk/region-config-resolver': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-endpoints': 3.679.0 + '@aws-sdk/util-user-agent-browser': 3.679.0 + '@aws-sdk/util-user-agent-node': 3.682.0 '@smithy/config-resolver': 3.0.10 '@smithy/core': 2.5.1 '@smithy/fetch-http-handler': 3.2.9 @@ -11397,22 +11567,22 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.678.0': + '@aws-sdk/client-sts@3.682.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.678.0(@aws-sdk/client-sts@3.678.0) - '@aws-sdk/core': 3.678.0 - '@aws-sdk/credential-provider-node': 3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0))(@aws-sdk/client-sts@3.678.0) - '@aws-sdk/middleware-host-header': 3.667.0 - '@aws-sdk/middleware-logger': 3.667.0 - '@aws-sdk/middleware-recursion-detection': 3.667.0 - '@aws-sdk/middleware-user-agent': 3.678.0 - '@aws-sdk/region-config-resolver': 3.667.0 - '@aws-sdk/types': 3.667.0 - '@aws-sdk/util-endpoints': 3.667.0 - '@aws-sdk/util-user-agent-browser': 3.675.0 - '@aws-sdk/util-user-agent-node': 3.678.0 + '@aws-sdk/client-sso-oidc': 3.682.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/core': 3.679.0 + '@aws-sdk/credential-provider-node': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/middleware-host-header': 3.679.0 + '@aws-sdk/middleware-logger': 3.679.0 + '@aws-sdk/middleware-recursion-detection': 3.679.0 + '@aws-sdk/middleware-user-agent': 3.682.0 + '@aws-sdk/region-config-resolver': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-endpoints': 3.679.0 + '@aws-sdk/util-user-agent-browser': 3.679.0 + '@aws-sdk/util-user-agent-node': 3.682.0 '@smithy/config-resolver': 3.0.10 '@smithy/core': 2.5.1 '@smithy/fetch-http-handler': 3.2.9 @@ -11442,14 +11612,14 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/cloudfront-signer@3.678.0': + '@aws-sdk/cloudfront-signer@3.679.0': dependencies: '@smithy/url-parser': 3.0.8 tslib: 2.8.1 - '@aws-sdk/core@3.678.0': + '@aws-sdk/core@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/core': 2.5.1 '@smithy/node-config-provider': 3.1.9 '@smithy/property-provider': 3.1.8 @@ -11461,18 +11631,18 @@ snapshots: fast-xml-parser: 4.4.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.678.0': + '@aws-sdk/credential-provider-env@3.679.0': dependencies: - '@aws-sdk/core': 3.678.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 '@smithy/property-provider': 3.1.8 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.678.0': + '@aws-sdk/credential-provider-http@3.679.0': dependencies: - '@aws-sdk/core': 3.678.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 '@smithy/fetch-http-handler': 3.2.9 '@smithy/node-http-handler': 3.2.5 '@smithy/property-provider': 3.1.8 @@ -11482,16 +11652,16 @@ snapshots: '@smithy/util-stream': 3.2.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0))(@aws-sdk/client-sts@3.678.0)': + '@aws-sdk/credential-provider-ini@3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0)': dependencies: - '@aws-sdk/client-sts': 3.678.0 - '@aws-sdk/core': 3.678.0 - '@aws-sdk/credential-provider-env': 3.678.0 - '@aws-sdk/credential-provider-http': 3.678.0 - '@aws-sdk/credential-provider-process': 3.678.0 - '@aws-sdk/credential-provider-sso': 3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0)) - '@aws-sdk/credential-provider-web-identity': 3.678.0(@aws-sdk/client-sts@3.678.0) - '@aws-sdk/types': 3.667.0 + '@aws-sdk/client-sts': 3.682.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/credential-provider-env': 3.679.0 + '@aws-sdk/credential-provider-http': 3.679.0 + '@aws-sdk/credential-provider-process': 3.679.0 + '@aws-sdk/credential-provider-sso': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0)) + '@aws-sdk/credential-provider-web-identity': 3.679.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/types': 3.679.0 '@smithy/credential-provider-imds': 3.2.5 '@smithy/property-provider': 3.1.8 '@smithy/shared-ini-file-loader': 3.1.9 @@ -11501,15 +11671,15 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-node@3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0))(@aws-sdk/client-sts@3.678.0)': + '@aws-sdk/credential-provider-node@3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0)': dependencies: - '@aws-sdk/credential-provider-env': 3.678.0 - '@aws-sdk/credential-provider-http': 3.678.0 - '@aws-sdk/credential-provider-ini': 3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0))(@aws-sdk/client-sts@3.678.0) - '@aws-sdk/credential-provider-process': 3.678.0 - '@aws-sdk/credential-provider-sso': 3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0)) - '@aws-sdk/credential-provider-web-identity': 3.678.0(@aws-sdk/client-sts@3.678.0) - '@aws-sdk/types': 3.667.0 + '@aws-sdk/credential-provider-env': 3.679.0 + '@aws-sdk/credential-provider-http': 3.679.0 + '@aws-sdk/credential-provider-ini': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/credential-provider-process': 3.679.0 + '@aws-sdk/credential-provider-sso': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0)) + '@aws-sdk/credential-provider-web-identity': 3.679.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/types': 3.679.0 '@smithy/credential-provider-imds': 3.2.5 '@smithy/property-provider': 3.1.8 '@smithy/shared-ini-file-loader': 3.1.9 @@ -11520,21 +11690,21 @@ snapshots: - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-process@3.678.0': + '@aws-sdk/credential-provider-process@3.679.0': dependencies: - '@aws-sdk/core': 3.678.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 '@smithy/property-provider': 3.1.8 '@smithy/shared-ini-file-loader': 3.1.9 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0))': + '@aws-sdk/credential-provider-sso@3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))': dependencies: - '@aws-sdk/client-sso': 3.678.0 - '@aws-sdk/core': 3.678.0 - '@aws-sdk/token-providers': 3.667.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0)) - '@aws-sdk/types': 3.667.0 + '@aws-sdk/client-sso': 3.682.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/token-providers': 3.679.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0)) + '@aws-sdk/types': 3.679.0 '@smithy/property-provider': 3.1.8 '@smithy/shared-ini-file-loader': 3.1.9 '@smithy/types': 3.6.0 @@ -11543,38 +11713,38 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-web-identity@3.678.0(@aws-sdk/client-sts@3.678.0)': + '@aws-sdk/credential-provider-web-identity@3.679.0(@aws-sdk/client-sts@3.682.0)': dependencies: - '@aws-sdk/client-sts': 3.678.0 - '@aws-sdk/core': 3.678.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/client-sts': 3.682.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 '@smithy/property-provider': 3.1.8 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/middleware-bucket-endpoint@3.667.0': + '@aws-sdk/middleware-bucket-endpoint@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 - '@aws-sdk/util-arn-parser': 3.568.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-arn-parser': 3.679.0 '@smithy/node-config-provider': 3.1.9 '@smithy/protocol-http': 4.1.5 '@smithy/types': 3.6.0 '@smithy/util-config-provider': 3.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.667.0': + '@aws-sdk/middleware-expect-continue@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/protocol-http': 4.1.5 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.678.0': + '@aws-sdk/middleware-flexible-checksums@3.682.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 - '@aws-sdk/core': 3.678.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 '@smithy/is-array-buffer': 3.0.0 '@smithy/node-config-provider': 3.1.9 '@smithy/protocol-http': 4.1.5 @@ -11583,37 +11753,37 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.667.0': + '@aws-sdk/middleware-host-header@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/protocol-http': 4.1.5 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.667.0': + '@aws-sdk/middleware-location-constraint@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.667.0': + '@aws-sdk/middleware-logger@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.667.0': + '@aws-sdk/middleware-recursion-detection@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/protocol-http': 4.1.5 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.678.0': + '@aws-sdk/middleware-sdk-s3@3.685.0': dependencies: - '@aws-sdk/core': 3.678.0 - '@aws-sdk/types': 3.667.0 - '@aws-sdk/util-arn-parser': 3.568.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-arn-parser': 3.679.0 '@smithy/core': 2.5.1 '@smithy/node-config-provider': 3.1.9 '@smithy/protocol-http': 4.1.5 @@ -11626,61 +11796,61 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.667.0': + '@aws-sdk/middleware-ssec@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.678.0': + '@aws-sdk/middleware-user-agent@3.682.0': dependencies: - '@aws-sdk/core': 3.678.0 - '@aws-sdk/types': 3.667.0 - '@aws-sdk/util-endpoints': 3.667.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-endpoints': 3.679.0 '@smithy/core': 2.5.1 '@smithy/protocol-http': 4.1.5 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/region-config-resolver@3.667.0': + '@aws-sdk/region-config-resolver@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/node-config-provider': 3.1.9 '@smithy/types': 3.6.0 '@smithy/util-config-provider': 3.0.0 '@smithy/util-middleware': 3.0.8 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.678.0': + '@aws-sdk/signature-v4-multi-region@3.685.0': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.678.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/middleware-sdk-s3': 3.685.0 + '@aws-sdk/types': 3.679.0 '@smithy/protocol-http': 4.1.5 '@smithy/signature-v4': 4.2.1 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.667.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0))': + '@aws-sdk/token-providers@3.679.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))': dependencies: - '@aws-sdk/client-sso-oidc': 3.678.0(@aws-sdk/client-sts@3.678.0) - '@aws-sdk/types': 3.667.0 + '@aws-sdk/client-sso-oidc': 3.682.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/types': 3.679.0 '@smithy/property-provider': 3.1.8 '@smithy/shared-ini-file-loader': 3.1.9 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/types@3.667.0': + '@aws-sdk/types@3.679.0': dependencies: '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/util-arn-parser@3.568.0': + '@aws-sdk/util-arn-parser@3.679.0': dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.667.0': + '@aws-sdk/util-endpoints@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/types': 3.6.0 '@smithy/util-endpoints': 2.1.4 tslib: 2.8.1 @@ -11689,22 +11859,22 @@ snapshots: dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.675.0': + '@aws-sdk/util-user-agent-browser@3.679.0': dependencies: - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.679.0 '@smithy/types': 3.6.0 bowser: 2.11.0 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.678.0': + '@aws-sdk/util-user-agent-node@3.682.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.678.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/middleware-user-agent': 3.682.0 + '@aws-sdk/types': 3.679.0 '@smithy/node-config-provider': 3.1.9 '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.662.0': + '@aws-sdk/xml-builder@3.679.0': dependencies: '@smithy/types': 3.6.0 tslib: 2.8.1 @@ -12467,8 +12637,8 @@ snapshots: '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1) '@tiptap/extension-bold': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1)) '@tiptap/extension-code': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1)) - '@tiptap/extension-collaboration': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)) - '@tiptap/extension-collaboration-cursor': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)) + '@tiptap/extension-collaboration': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.36.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)) + '@tiptap/extension-collaboration-cursor': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.36.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)) '@tiptap/extension-dropcursor': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1) '@tiptap/extension-gapcursor': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1) '@tiptap/extension-hard-break': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1)) @@ -12490,7 +12660,7 @@ snapshots: prosemirror-state: 1.4.3 prosemirror-tables: 1.6.1 prosemirror-transform: 1.10.2 - prosemirror-view: 1.35.0 + prosemirror-view: 1.36.0 rehype-format: 5.0.1 rehype-parse: 8.0.5 rehype-remark: 9.1.2 @@ -12501,7 +12671,7 @@ snapshots: remark-stringify: 10.0.3 unified: 10.1.2 uuid: 8.3.2 - y-prosemirror: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20) + y-prosemirror: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.36.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20) y-protocols: 1.0.6(yjs@13.6.20) yjs: 13.6.20 transitivePeerDependencies: @@ -12521,7 +12691,7 @@ snapshots: - '@tiptap/pm' - supports-color - '@blocknote/shadcn@0.17.1(@tiptap/pm@2.9.1)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))': + '@blocknote/shadcn@0.17.1(@tiptap/pm@2.9.1)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3))': dependencies: '@blocknote/core': 0.17.1 '@blocknote/react': 0.17.1(@tiptap/pm@2.9.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -12543,8 +12713,8 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.53.1(react@18.3.1) tailwind-merge: 2.5.4 - tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)) - tailwindcss-animate: 1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))) + tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)) + tailwindcss-animate: 1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3))) zod: 3.23.8 transitivePeerDependencies: - '@tiptap/pm' @@ -12556,44 +12726,6 @@ snapshots: '@canvas/image-data@1.0.0': optional: true - '@cellajs/imado@0.1.3(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(yaml@2.6.0)': - dependencies: - '@aws-sdk/client-s3': 3.678.0 - '@aws-sdk/cloudfront-signer': 3.678.0 - '@tus/file-store': 1.5.0 - '@tus/s3-store': 1.6.0 - jsonwebtoken: 9.0.2 - tsup: 8.3.5(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.6.0) - typescript: 5.6.3 - transitivePeerDependencies: - - '@microsoft/api-extractor' - - '@swc/core' - - aws-crt - - jiti - - postcss - - supports-color - - tsx - - yaml - - '@cellajs/imado@0.1.3(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2)(yaml@2.6.0)': - dependencies: - '@aws-sdk/client-s3': 3.678.0 - '@aws-sdk/cloudfront-signer': 3.678.0 - '@tus/file-store': 1.5.0 - '@tus/s3-store': 1.6.0 - jsonwebtoken: 9.0.2 - tsup: 8.3.5(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.6.0) - typescript: 5.6.3 - transitivePeerDependencies: - - '@microsoft/api-extractor' - - '@swc/core' - - aws-crt - - jiti - - postcss - - supports-color - - tsx - - yaml - '@cellajs/permission-manager@0.1.0(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2)(yaml@2.6.0)': dependencies: tsup: 8.3.5(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.6.0) @@ -12627,34 +12759,34 @@ snapshots: '@jridgewell/trace-mapping': 0.3.9 optional: true - '@csstools/cascade-layer-name-parser@2.0.3(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + '@csstools/cascade-layer-name-parser@2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': dependencies: - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/color-helpers@5.0.1': {} - '@csstools/css-calc@2.0.3(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + '@csstools/css-calc@2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': dependencies: - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 - '@csstools/css-color-parser@3.0.4(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + '@csstools/css-color-parser@3.0.5(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': dependencies: '@csstools/color-helpers': 5.0.1 - '@csstools/css-calc': 2.0.3(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-calc': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 - '@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3)': + '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': dependencies: '@csstools/css-tokenizer': 3.0.3 '@csstools/css-tokenizer@3.0.3': {} - '@csstools/media-query-list-parser@4.0.1(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + '@csstools/media-query-list-parser@4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': dependencies: - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-cascade-layers@5.0.1(postcss@8.4.47)': @@ -12663,36 +12795,36 @@ snapshots: postcss: 8.4.47 postcss-selector-parser: 7.0.0 - '@csstools/postcss-color-function@4.0.4(postcss@8.4.47)': + '@csstools/postcss-color-function@4.0.5(postcss@8.4.47)': dependencies: - '@csstools/css-color-parser': 3.0.4(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.5(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) '@csstools/utilities': 2.0.0(postcss@8.4.47) postcss: 8.4.47 - '@csstools/postcss-color-mix-function@3.0.4(postcss@8.4.47)': + '@csstools/postcss-color-mix-function@3.0.5(postcss@8.4.47)': dependencies: - '@csstools/css-color-parser': 3.0.4(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.5(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) '@csstools/utilities': 2.0.0(postcss@8.4.47) postcss: 8.4.47 - '@csstools/postcss-content-alt-text@2.0.3(postcss@8.4.47)': + '@csstools/postcss-content-alt-text@2.0.4(postcss@8.4.47)': dependencies: - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) '@csstools/utilities': 2.0.0(postcss@8.4.47) postcss: 8.4.47 - '@csstools/postcss-exponential-functions@2.0.3(postcss@8.4.47)': + '@csstools/postcss-exponential-functions@2.0.4(postcss@8.4.47)': dependencies: - '@csstools/css-calc': 2.0.3(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-calc': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 postcss: 8.4.47 @@ -12702,26 +12834,26 @@ snapshots: postcss: 8.4.47 postcss-value-parser: 4.2.0 - '@csstools/postcss-gamut-mapping@2.0.4(postcss@8.4.47)': + '@csstools/postcss-gamut-mapping@2.0.5(postcss@8.4.47)': dependencies: - '@csstools/css-color-parser': 3.0.4(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.5(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 postcss: 8.4.47 - '@csstools/postcss-gradients-interpolation-method@5.0.4(postcss@8.4.47)': + '@csstools/postcss-gradients-interpolation-method@5.0.5(postcss@8.4.47)': dependencies: - '@csstools/css-color-parser': 3.0.4(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.5(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) '@csstools/utilities': 2.0.0(postcss@8.4.47) postcss: 8.4.47 - '@csstools/postcss-hwb-function@4.0.4(postcss@8.4.47)': + '@csstools/postcss-hwb-function@4.0.5(postcss@8.4.47)': dependencies: - '@csstools/css-color-parser': 3.0.4(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.5(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) '@csstools/utilities': 2.0.0(postcss@8.4.47) @@ -12744,9 +12876,9 @@ snapshots: postcss: 8.4.47 postcss-selector-parser: 7.0.0 - '@csstools/postcss-light-dark-function@2.0.6(postcss@8.4.47)': + '@csstools/postcss-light-dark-function@2.0.7(postcss@8.4.47)': dependencies: - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) '@csstools/utilities': 2.0.0(postcss@8.4.47) @@ -12769,25 +12901,25 @@ snapshots: postcss: 8.4.47 postcss-value-parser: 4.2.0 - '@csstools/postcss-logical-viewport-units@3.0.2(postcss@8.4.47)': + '@csstools/postcss-logical-viewport-units@3.0.3(postcss@8.4.47)': dependencies: '@csstools/css-tokenizer': 3.0.3 '@csstools/utilities': 2.0.0(postcss@8.4.47) postcss: 8.4.47 - '@csstools/postcss-media-minmax@2.0.3(postcss@8.4.47)': + '@csstools/postcss-media-minmax@2.0.4(postcss@8.4.47)': dependencies: - '@csstools/css-calc': 2.0.3(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-calc': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 - '@csstools/media-query-list-parser': 4.0.1(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/media-query-list-parser': 4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) postcss: 8.4.47 - '@csstools/postcss-media-queries-aspect-ratio-number-values@3.0.3(postcss@8.4.47)': + '@csstools/postcss-media-queries-aspect-ratio-number-values@3.0.4(postcss@8.4.47)': dependencies: - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 - '@csstools/media-query-list-parser': 4.0.1(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/media-query-list-parser': 4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) postcss: 8.4.47 '@csstools/postcss-nested-calc@4.0.0(postcss@8.4.47)': @@ -12801,10 +12933,10 @@ snapshots: postcss: 8.4.47 postcss-value-parser: 4.2.0 - '@csstools/postcss-oklab-function@4.0.4(postcss@8.4.47)': + '@csstools/postcss-oklab-function@4.0.5(postcss@8.4.47)': dependencies: - '@csstools/css-color-parser': 3.0.4(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.5(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) '@csstools/utilities': 2.0.0(postcss@8.4.47) @@ -12815,10 +12947,10 @@ snapshots: postcss: 8.4.47 postcss-value-parser: 4.2.0 - '@csstools/postcss-relative-color-syntax@3.0.4(postcss@8.4.47)': + '@csstools/postcss-relative-color-syntax@3.0.5(postcss@8.4.47)': dependencies: - '@csstools/css-color-parser': 3.0.4(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.5(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) '@csstools/utilities': 2.0.0(postcss@8.4.47) @@ -12829,10 +12961,10 @@ snapshots: postcss: 8.4.47 postcss-selector-parser: 7.0.0 - '@csstools/postcss-stepped-value-functions@4.0.3(postcss@8.4.47)': + '@csstools/postcss-stepped-value-functions@4.0.4(postcss@8.4.47)': dependencies: - '@csstools/css-calc': 2.0.3(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-calc': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 postcss: 8.4.47 @@ -12842,10 +12974,10 @@ snapshots: postcss: 8.4.47 postcss-value-parser: 4.2.0 - '@csstools/postcss-trigonometric-functions@4.0.3(postcss@8.4.47)': + '@csstools/postcss-trigonometric-functions@4.0.4(postcss@8.4.47)': dependencies: - '@csstools/css-calc': 2.0.3(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-calc': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 postcss: 8.4.47 @@ -12877,7 +13009,7 @@ snapshots: '@electric-sql/client@0.6.5': optionalDependencies: - '@rollup/rollup-darwin-arm64': 4.24.3 + '@rollup/rollup-darwin-arm64': 4.24.4 '@electric-sql/pglite@0.2.12': {} @@ -13412,6 +13544,8 @@ snapshots: '@faker-js/faker@9.1.0': {} + '@faker-js/faker@9.2.0': {} + '@floating-ui/core@1.6.8': dependencies: '@floating-ui/utils': 0.2.8 @@ -13461,6 +13595,10 @@ snapshots: dependencies: hono: 4.6.8 + '@hono/node-server@1.13.5(hono@4.6.8)': + dependencies: + hono: 4.6.8 + '@hono/sentry@1.2.0(hono@4.6.8)': dependencies: hono: 4.6.8 @@ -13500,7 +13638,7 @@ snapshots: '@inquirer/figures': 1.0.6 '@inquirer/type': 2.0.0 '@types/mute-stream': 0.0.4 - '@types/node': 22.8.6 + '@types/node': 22.9.0 '@types/wrap-ansi': 3.0.0 ansi-escapes: 4.3.2 cli-width: 4.1.0 @@ -13617,7 +13755,7 @@ snapshots: '@js-sdsl/ordered-map@4.4.2': {} - '@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.3)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))': + '@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.4)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))': dependencies: '@radix-ui/colors': 3.0.0 '@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.2.0(react@18.3.1))(react@18.3.1) @@ -13637,7 +13775,7 @@ snapshots: tailwindcss: 3.4.0(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)) titleize: 4.0.0 vite: 4.5.5(@types/node@22.8.6)(terser@5.36.0) - vite-plugin-node-polyfills: 0.16.0(rollup@4.24.3)(vite@4.5.5(@types/node@22.8.6)(terser@5.36.0)) + vite-plugin-node-polyfills: 0.16.0(rollup@4.24.4)(vite@4.5.5(@types/node@22.8.6)(terser@5.36.0)) transitivePeerDependencies: - '@types/node' - '@types/react' @@ -13667,13 +13805,13 @@ snapshots: '@adobe/css-tools': 4.4.0 hast-util-select: 6.0.3 hast-util-to-string: 3.0.1 - jsx-email: 2.1.2(@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.3)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))(@jsx-email/plugin-inline@1.0.1)(@jsx-email/plugin-minify@1.0.1)(@jsx-email/plugin-pretty@1.0.0)(@types/node@22.8.6)(@types/react@18.3.12)(react@18.3.1)(terser@5.36.0) + jsx-email: 2.1.2(@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.4)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))(@jsx-email/plugin-inline@1.0.1)(@jsx-email/plugin-minify@1.0.1)(@jsx-email/plugin-pretty@1.0.0)(@types/node@22.8.6)(@types/react@18.3.12)(react@18.3.1)(terser@5.36.0) unist-util-remove: 4.0.0 unist-util-visit: 5.0.0 '@jsx-email/plugin-minify@1.0.1(jsx-email@2.1.2)': dependencies: - jsx-email: 2.1.2(@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.3)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))(@jsx-email/plugin-inline@1.0.1)(@jsx-email/plugin-minify@1.0.1)(@jsx-email/plugin-pretty@1.0.0)(@types/node@22.8.6)(@types/react@18.3.12)(react@18.3.1)(terser@5.36.0) + jsx-email: 2.1.2(@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.4)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))(@jsx-email/plugin-inline@1.0.1)(@jsx-email/plugin-minify@1.0.1)(@jsx-email/plugin-pretty@1.0.0)(@types/node@22.8.6)(@types/react@18.3.12)(react@18.3.1)(terser@5.36.0) rehype-minify-attribute-whitespace: 4.0.1 rehype-minify-css-style: 4.0.1 rehype-minify-enumerated-attribute: 5.0.1 @@ -13695,7 +13833,7 @@ snapshots: '@jsx-email/plugin-pretty@1.0.0(jsx-email@2.1.2)': dependencies: - jsx-email: 2.1.2(@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.3)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))(@jsx-email/plugin-inline@1.0.1)(@jsx-email/plugin-minify@1.0.1)(@jsx-email/plugin-pretty@1.0.0)(@types/node@22.8.6)(@types/react@18.3.12)(react@18.3.1)(terser@5.36.0) + jsx-email: 2.1.2(@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.4)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))(@jsx-email/plugin-inline@1.0.1)(@jsx-email/plugin-minify@1.0.1)(@jsx-email/plugin-pretty@1.0.0)(@types/node@22.8.6)(@types/react@18.3.12)(react@18.3.1)(terser@5.36.0) pretty: 2.0.0 '@libsql/client-wasm@0.14.0': @@ -13779,7 +13917,7 @@ snapshots: ast-types: 0.14.2 cli-high: 0.4.3 diff: 5.2.0 - effect: 3.10.8 + effect: 3.10.12 nanoid: 5.0.8 recast: 0.23.9 xycolors: 0.1.2 @@ -13789,7 +13927,7 @@ snapshots: '@axiomhq/js': 1.0.0-rc.3 '@babel/core': 7.25.2 '@babel/types': 7.25.2 - '@hono/node-server': 1.13.4(hono@4.6.8) + '@hono/node-server': 1.13.5(hono@4.6.8) '@million/install': 1.0.11 '@rollup/pluginutils': 5.1.3(rollup@2.79.2) '@rrweb/types': 2.0.0-alpha.16 @@ -14391,7 +14529,7 @@ snapshots: '@oslojs/crypto': 1.0.0 '@oslojs/encoding': 1.0.0 - '@paddle/paddle-js@1.3.1': {} + '@paddle/paddle-js@1.3.2': {} '@paddle/paddle-node-sdk@1.9.1': dependencies: @@ -15721,13 +15859,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@rollup/plugin-inject@5.0.5(rollup@4.24.3)': + '@rollup/plugin-inject@5.0.5(rollup@4.24.4)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@4.24.3) + '@rollup/pluginutils': 5.1.3(rollup@4.24.4) estree-walker: 2.0.2 magic-string: 0.30.12 optionalDependencies: - rollup: 4.24.3 + rollup: 4.24.4 '@rollup/plugin-node-resolve@15.3.0(rollup@2.79.2)': dependencies: @@ -15773,13 +15911,13 @@ snapshots: optionalDependencies: rollup: 2.79.2 - '@rollup/pluginutils@5.1.3(rollup@4.24.3)': + '@rollup/pluginutils@5.1.3(rollup@4.24.4)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.24.3 + rollup: 4.24.4 '@rollup/rollup-android-arm-eabi@4.24.0': optional: true @@ -15787,102 +15925,156 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.24.3': optional: true + '@rollup/rollup-android-arm-eabi@4.24.4': + optional: true + '@rollup/rollup-android-arm64@4.24.0': optional: true '@rollup/rollup-android-arm64@4.24.3': optional: true + '@rollup/rollup-android-arm64@4.24.4': + optional: true + '@rollup/rollup-darwin-arm64@4.24.0': optional: true '@rollup/rollup-darwin-arm64@4.24.3': optional: true + '@rollup/rollup-darwin-arm64@4.24.4': + optional: true + '@rollup/rollup-darwin-x64@4.24.0': optional: true '@rollup/rollup-darwin-x64@4.24.3': optional: true + '@rollup/rollup-darwin-x64@4.24.4': + optional: true + '@rollup/rollup-freebsd-arm64@4.24.3': optional: true + '@rollup/rollup-freebsd-arm64@4.24.4': + optional: true + '@rollup/rollup-freebsd-x64@4.24.3': optional: true + '@rollup/rollup-freebsd-x64@4.24.4': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.24.3': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.24.4': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.24.0': optional: true '@rollup/rollup-linux-arm-musleabihf@4.24.3': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.24.4': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.24.0': optional: true '@rollup/rollup-linux-arm64-gnu@4.24.3': optional: true + '@rollup/rollup-linux-arm64-gnu@4.24.4': + optional: true + '@rollup/rollup-linux-arm64-musl@4.24.0': optional: true '@rollup/rollup-linux-arm64-musl@4.24.3': optional: true + '@rollup/rollup-linux-arm64-musl@4.24.4': + optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': optional: true '@rollup/rollup-linux-powerpc64le-gnu@4.24.3': optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.24.4': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.24.0': optional: true '@rollup/rollup-linux-riscv64-gnu@4.24.3': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.24.4': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.24.0': optional: true '@rollup/rollup-linux-s390x-gnu@4.24.3': optional: true + '@rollup/rollup-linux-s390x-gnu@4.24.4': + optional: true + '@rollup/rollup-linux-x64-gnu@4.24.0': optional: true '@rollup/rollup-linux-x64-gnu@4.24.3': optional: true + '@rollup/rollup-linux-x64-gnu@4.24.4': + optional: true + '@rollup/rollup-linux-x64-musl@4.24.0': optional: true '@rollup/rollup-linux-x64-musl@4.24.3': optional: true + '@rollup/rollup-linux-x64-musl@4.24.4': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.24.0': optional: true '@rollup/rollup-win32-arm64-msvc@4.24.3': optional: true + '@rollup/rollup-win32-arm64-msvc@4.24.4': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.24.0': optional: true '@rollup/rollup-win32-ia32-msvc@4.24.3': optional: true + '@rollup/rollup-win32-ia32-msvc@4.24.4': + optional: true + '@rollup/rollup-win32-x64-msvc@4.24.0': optional: true '@rollup/rollup-win32-x64-msvc@4.24.3': optional: true + '@rollup/rollup-win32-x64-msvc@4.24.4': + optional: true + '@rrweb/types@2.0.0-alpha.16': dependencies: rrweb-snapshot: 2.0.0-alpha.17 @@ -15922,49 +16114,49 @@ snapshots: transitivePeerDependencies: - debug - '@sentry-internal/browser-utils@8.36.0': + '@sentry-internal/browser-utils@8.37.1': dependencies: - '@sentry/core': 8.36.0 - '@sentry/types': 8.36.0 - '@sentry/utils': 8.36.0 + '@sentry/core': 8.37.1 + '@sentry/types': 8.37.1 + '@sentry/utils': 8.37.1 - '@sentry-internal/feedback@8.36.0': + '@sentry-internal/feedback@8.37.1': dependencies: - '@sentry/core': 8.36.0 - '@sentry/types': 8.36.0 - '@sentry/utils': 8.36.0 + '@sentry/core': 8.37.1 + '@sentry/types': 8.37.1 + '@sentry/utils': 8.37.1 - '@sentry-internal/replay-canvas@8.36.0': + '@sentry-internal/replay-canvas@8.37.1': dependencies: - '@sentry-internal/replay': 8.36.0 - '@sentry/core': 8.36.0 - '@sentry/types': 8.36.0 - '@sentry/utils': 8.36.0 + '@sentry-internal/replay': 8.37.1 + '@sentry/core': 8.37.1 + '@sentry/types': 8.37.1 + '@sentry/utils': 8.37.1 - '@sentry-internal/replay@8.36.0': + '@sentry-internal/replay@8.37.1': dependencies: - '@sentry-internal/browser-utils': 8.36.0 - '@sentry/core': 8.36.0 - '@sentry/types': 8.36.0 - '@sentry/utils': 8.36.0 + '@sentry-internal/browser-utils': 8.37.1 + '@sentry/core': 8.37.1 + '@sentry/types': 8.37.1 + '@sentry/utils': 8.37.1 '@sentry/babel-plugin-component-annotate@2.22.6': {} - '@sentry/browser@8.36.0': + '@sentry/browser@8.37.1': dependencies: - '@sentry-internal/browser-utils': 8.36.0 - '@sentry-internal/feedback': 8.36.0 - '@sentry-internal/replay': 8.36.0 - '@sentry-internal/replay-canvas': 8.36.0 - '@sentry/core': 8.36.0 - '@sentry/types': 8.36.0 - '@sentry/utils': 8.36.0 + '@sentry-internal/browser-utils': 8.37.1 + '@sentry-internal/feedback': 8.37.1 + '@sentry-internal/replay': 8.37.1 + '@sentry-internal/replay-canvas': 8.37.1 + '@sentry/core': 8.37.1 + '@sentry/types': 8.37.1 + '@sentry/utils': 8.37.1 '@sentry/bundler-plugin-core@2.22.6': dependencies: '@babel/core': 7.26.0 '@sentry/babel-plugin-component-annotate': 2.22.6 - '@sentry/cli': 2.38.1 + '@sentry/cli': 2.38.2 dotenv: 16.4.5 find-up: 5.0.0 glob: 9.3.5 @@ -15977,24 +16169,45 @@ snapshots: '@sentry/cli-darwin@2.38.1': optional: true + '@sentry/cli-darwin@2.38.2': + optional: true + '@sentry/cli-linux-arm64@2.38.1': optional: true + '@sentry/cli-linux-arm64@2.38.2': + optional: true + '@sentry/cli-linux-arm@2.38.1': optional: true + '@sentry/cli-linux-arm@2.38.2': + optional: true + '@sentry/cli-linux-i686@2.38.1': optional: true + '@sentry/cli-linux-i686@2.38.2': + optional: true + '@sentry/cli-linux-x64@2.38.1': optional: true + '@sentry/cli-linux-x64@2.38.2': + optional: true + '@sentry/cli-win32-i686@2.38.1': optional: true + '@sentry/cli-win32-i686@2.38.2': + optional: true + '@sentry/cli-win32-x64@2.38.1': optional: true + '@sentry/cli-win32-x64@2.38.2': + optional: true + '@sentry/cli@2.38.1': dependencies: https-proxy-agent: 5.0.1 @@ -16014,32 +16227,51 @@ snapshots: - encoding - supports-color - '@sentry/core@8.36.0': + '@sentry/cli@2.38.2': + dependencies: + https-proxy-agent: 5.0.1 + node-fetch: 2.7.0 + progress: 2.0.3 + proxy-from-env: 1.1.0 + which: 2.0.2 + optionalDependencies: + '@sentry/cli-darwin': 2.38.2 + '@sentry/cli-linux-arm': 2.38.2 + '@sentry/cli-linux-arm64': 2.38.2 + '@sentry/cli-linux-i686': 2.38.2 + '@sentry/cli-linux-x64': 2.38.2 + '@sentry/cli-win32-i686': 2.38.2 + '@sentry/cli-win32-x64': 2.38.2 + transitivePeerDependencies: + - encoding + - supports-color + + '@sentry/core@8.37.1': dependencies: - '@sentry/types': 8.36.0 - '@sentry/utils': 8.36.0 + '@sentry/types': 8.37.1 + '@sentry/utils': 8.37.1 '@sentry/core@8.9.2': dependencies: '@sentry/types': 8.9.2 '@sentry/utils': 8.9.2 - '@sentry/react@8.36.0(react@18.3.1)': + '@sentry/react@8.37.1(react@18.3.1)': dependencies: - '@sentry/browser': 8.36.0 - '@sentry/core': 8.36.0 - '@sentry/types': 8.36.0 - '@sentry/utils': 8.36.0 + '@sentry/browser': 8.37.1 + '@sentry/core': 8.37.1 + '@sentry/types': 8.37.1 + '@sentry/utils': 8.37.1 hoist-non-react-statics: 3.3.2 react: 18.3.1 - '@sentry/types@8.36.0': {} + '@sentry/types@8.37.1': {} '@sentry/types@8.9.2': {} - '@sentry/utils@8.36.0': + '@sentry/utils@8.37.1': dependencies: - '@sentry/types': 8.36.0 + '@sentry/types': 8.37.1 '@sentry/utils@8.9.2': dependencies: @@ -16497,47 +16729,47 @@ snapshots: optionalDependencies: typescript: 5.6.3 - '@tailwindcss/typography@0.5.15(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))': + '@tailwindcss/typography@0.5.15(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)))': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)) + tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)) '@tanstack/history@1.61.1': {} - '@tanstack/query-core@5.59.16': {} + '@tanstack/query-core@5.59.20': {} - '@tanstack/query-devtools@5.58.0': {} + '@tanstack/query-devtools@5.59.20': {} - '@tanstack/query-persist-client-core@5.59.16': + '@tanstack/query-persist-client-core@5.59.20': dependencies: - '@tanstack/query-core': 5.59.16 + '@tanstack/query-core': 5.59.20 - '@tanstack/query-sync-storage-persister@5.59.16': + '@tanstack/query-sync-storage-persister@5.59.20': dependencies: - '@tanstack/query-core': 5.59.16 - '@tanstack/query-persist-client-core': 5.59.16 + '@tanstack/query-core': 5.59.20 + '@tanstack/query-persist-client-core': 5.59.20 - '@tanstack/react-query-devtools@5.59.16(@tanstack/react-query@5.59.16(react@18.3.1))(react@18.3.1)': + '@tanstack/react-query-devtools@5.59.20(@tanstack/react-query@5.59.20(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/query-devtools': 5.58.0 - '@tanstack/react-query': 5.59.16(react@18.3.1) + '@tanstack/query-devtools': 5.59.20 + '@tanstack/react-query': 5.59.20(react@18.3.1) react: 18.3.1 - '@tanstack/react-query-persist-client@5.59.16(@tanstack/react-query@5.59.16(react@18.3.1))(react@18.3.1)': + '@tanstack/react-query-persist-client@5.59.20(@tanstack/react-query@5.59.20(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/query-persist-client-core': 5.59.16 - '@tanstack/react-query': 5.59.16(react@18.3.1) + '@tanstack/query-persist-client-core': 5.59.20 + '@tanstack/react-query': 5.59.20(react@18.3.1) react: 18.3.1 - '@tanstack/react-query@5.59.16(react@18.3.1)': + '@tanstack/react-query@5.59.20(react@18.3.1)': dependencies: - '@tanstack/query-core': 5.59.16 + '@tanstack/query-core': 5.59.20 react: 18.3.1 - '@tanstack/react-router@1.78.0(@tanstack/router-generator@1.78.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tanstack/react-router@1.79.0(@tanstack/router-generator@1.79.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@tanstack/history': 1.61.1 '@tanstack/react-store': 0.5.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -16546,7 +16778,7 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 optionalDependencies: - '@tanstack/router-generator': 1.78.0 + '@tanstack/router-generator': 1.79.0 '@tanstack/react-store@0.5.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -16555,9 +16787,9 @@ snapshots: react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.2.2(react@18.3.1) - '@tanstack/router-devtools@1.78.0(@tanstack/react-router@1.78.0(@tanstack/router-generator@1.78.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tanstack/router-devtools@1.79.0(@tanstack/react-router@1.79.0(@tanstack/router-generator@1.79.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/react-router': 1.78.0(@tanstack/router-generator@1.78.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/react-router': 1.79.0(@tanstack/router-generator@1.79.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) clsx: 2.1.1 goober: 2.1.16(csstype@3.1.3) react: 18.3.1 @@ -16565,14 +16797,14 @@ snapshots: transitivePeerDependencies: - csstype - '@tanstack/router-generator@1.78.0': + '@tanstack/router-generator@1.79.0': dependencies: '@tanstack/virtual-file-routes': 1.64.0 prettier: 3.3.3 tsx: 4.19.2 zod: 3.23.8 - '@tanstack/router-plugin@1.78.0(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0))(webpack-sources@3.2.3)': + '@tanstack/router-plugin@1.79.0(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0))(webpack-sources@3.2.3)': dependencies: '@babel/core': 7.26.0 '@babel/generator': 7.26.2 @@ -16582,7 +16814,7 @@ snapshots: '@babel/template': 7.25.9 '@babel/traverse': 7.25.9 '@babel/types': 7.26.0 - '@tanstack/router-generator': 1.78.0 + '@tanstack/router-generator': 1.79.0 '@tanstack/virtual-file-routes': 1.64.0 '@types/babel__core': 7.20.5 '@types/babel__generator': 7.6.8 @@ -16593,7 +16825,7 @@ snapshots: unplugin: 1.15.0(webpack-sources@3.2.3) zod: 3.23.8 optionalDependencies: - vite: 5.4.10(@types/node@22.8.6)(terser@5.36.0) + vite: 5.4.10(@types/node@22.9.0)(terser@5.36.0) transitivePeerDependencies: - supports-color - webpack-sources @@ -16620,16 +16852,16 @@ snapshots: dependencies: '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1) - '@tiptap/extension-collaboration-cursor@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20))': + '@tiptap/extension-collaboration-cursor@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.36.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20))': dependencies: '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1) - y-prosemirror: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20) + y-prosemirror: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.36.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20) - '@tiptap/extension-collaboration@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20))': + '@tiptap/extension-collaboration@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.36.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20))': dependencies: '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1) '@tiptap/pm': 2.9.1 - y-prosemirror: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20) + y-prosemirror: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.36.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20) '@tiptap/extension-dropcursor@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)': dependencies: @@ -16716,9 +16948,9 @@ snapshots: prosemirror-schema-list: 1.4.1 prosemirror-state: 1.4.3 prosemirror-tables: 1.6.1 - prosemirror-trailing-node: 3.0.0(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0) + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.36.0) prosemirror-transform: 1.10.2 - prosemirror-view: 1.35.0 + prosemirror-view: 1.36.0 '@tiptap/react@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -16759,7 +16991,7 @@ snapshots: '@tus/s3-store@1.6.0': dependencies: - '@aws-sdk/client-s3': 3.678.0 + '@aws-sdk/client-s3': 3.685.0 '@shopify/semaphore': 3.1.0 '@tus/utils': 0.4.0 debug: 4.3.7 @@ -16768,6 +17000,16 @@ snapshots: - aws-crt - supports-color + '@tus/server@1.9.0': + dependencies: + '@tus/utils': 0.4.0 + debug: 4.3.7 + lodash.throttle: 4.1.1 + optionalDependencies: + '@redis/client': 1.6.0 + transitivePeerDependencies: + - supports-color + '@tus/utils@0.4.0': {} '@tybys/wasm-util@0.8.3': @@ -16877,7 +17119,7 @@ snapshots: '@types/mute-stream@0.0.4': dependencies: - '@types/node': 22.8.6 + '@types/node': 22.9.0 '@types/node-cron@3.0.11': {} @@ -16885,6 +17127,10 @@ snapshots: dependencies: undici-types: 6.19.8 + '@types/node@22.9.0': + dependencies: + undici-types: 6.19.8 + '@types/papaparse@5.3.15': dependencies: '@types/node': 22.8.6 @@ -17142,9 +17388,9 @@ snapshots: unconfig: 0.3.13 optional: true - '@vitejs/plugin-basic-ssl@1.1.0(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0))': + '@vitejs/plugin-basic-ssl@1.1.0(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0))': dependencies: - vite: 5.4.10(@types/node@22.8.6)(terser@5.36.0) + vite: 5.4.10(@types/node@22.9.0)(terser@5.36.0) '@vitejs/plugin-react@4.3.3(vite@4.5.5(@types/node@22.8.6)(terser@5.36.0))': dependencies: @@ -17168,6 +17414,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitejs/plugin-react@4.3.3(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0))': + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.2 + vite: 5.4.10(@types/node@22.9.0)(terser@5.36.0) + transitivePeerDependencies: + - supports-color + '@xstate/fsm@1.6.5': {} abbrev@1.1.1: @@ -17303,7 +17560,7 @@ snapshots: autoprefixer@10.4.20(postcss@8.4.47): dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001676 + caniuse-lite: 1.0.30001677 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -17500,8 +17757,8 @@ snapshots: browserslist@4.24.2: dependencies: - caniuse-lite: 1.0.30001676 - electron-to-chromium: 1.5.50 + caniuse-lite: 1.0.30001677 + electron-to-chromium: 1.5.52 node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.2) @@ -17559,7 +17816,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001676: {} + caniuse-lite@1.0.30001677: {} canvas@2.11.2: dependencies: @@ -17661,7 +17918,7 @@ snapshots: cli-high@0.4.3: dependencies: '@clack/prompts': 0.7.0 - sugar-high: 0.7.4 + sugar-high: 0.7.5 xycolors: 0.1.2 yargs: 17.7.2 @@ -17688,7 +17945,7 @@ snapshots: transitivePeerDependencies: - '@types/react' - cmdk@1.0.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + cmdk@1.0.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@radix-ui/react-dialog': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) @@ -18168,15 +18425,15 @@ snapshots: minimatch: 9.0.1 semver: 7.6.3 - effect@3.10.8: + effect@3.10.12: dependencies: - fast-check: 3.22.0 + fast-check: 3.23.1 ejs@3.1.10: dependencies: jake: 10.9.2 - electron-to-chromium@1.5.50: {} + electron-to-chromium@1.5.52: {} elliptic@6.6.0: dependencies: @@ -18204,7 +18461,7 @@ snapshots: embla-carousel@8.3.1: {} - emblor@1.4.6(@swc/core@1.7.26)(@types/react-dom@18.3.1)(@types/react@18.3.12)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3): + emblor@1.4.6(@swc/core@1.7.26)(@types/react-dom@18.3.1)(@types/react@18.3.12)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3))(typescript@5.6.3): dependencies: '@radix-ui/react-dialog': 1.0.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-popover': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -18216,7 +18473,7 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-easy-sort: 1.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) tailwind-merge: 2.5.4 - tsup: 6.7.0(@swc/core@1.7.26)(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3) + tsup: 6.7.0(@swc/core@1.7.26)(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3))(typescript@5.6.3) transitivePeerDependencies: - '@swc/core' - '@types/react' @@ -18585,7 +18842,7 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 - fast-check@3.22.0: + fast-check@3.23.1: dependencies: pure-rand: 6.1.0 @@ -19691,10 +19948,10 @@ snapshots: is-in-browser: 1.1.3 tiny-warning: 1.0.3 - jsx-email@2.1.2(@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.3)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))(@jsx-email/plugin-inline@1.0.1)(@jsx-email/plugin-minify@1.0.1)(@jsx-email/plugin-pretty@1.0.0)(@types/node@22.8.6)(@types/react@18.3.12)(react@18.3.1)(terser@5.36.0): + jsx-email@2.1.2(@jsx-email/app-preview@1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.4)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)))(@jsx-email/plugin-inline@1.0.1)(@jsx-email/plugin-minify@1.0.1)(@jsx-email/plugin-pretty@1.0.0)(@types/node@22.8.6)(@types/react@18.3.12)(react@18.3.1)(terser@5.36.0): dependencies: '@dot/log': 0.1.5 - '@jsx-email/app-preview': 1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.3)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)) + '@jsx-email/app-preview': 1.2.6(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react@18.3.1)(rollup@4.24.4)(terser@5.36.0)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)) '@jsx-email/doiuse-email': 1.0.4 '@jsx-email/plugin-inline': 1.0.1(jsx-email@2.1.2) '@jsx-email/plugin-minify': 1.0.1(jsx-email@2.1.2) @@ -20806,10 +21063,10 @@ snapshots: postcss: 8.4.47 postcss-value-parser: 4.2.0 - postcss-color-functional-notation@7.0.4(postcss@8.4.47): + postcss-color-functional-notation@7.0.5(postcss@8.4.47): dependencies: - '@csstools/css-color-parser': 3.0.4(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.5(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) '@csstools/utilities': 2.0.0(postcss@8.4.47) @@ -20827,27 +21084,27 @@ snapshots: postcss: 8.4.47 postcss-value-parser: 4.2.0 - postcss-custom-media@11.0.4(postcss@8.4.47): + postcss-custom-media@11.0.5(postcss@8.4.47): dependencies: - '@csstools/cascade-layer-name-parser': 2.0.3(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/cascade-layer-name-parser': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 - '@csstools/media-query-list-parser': 4.0.1(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/media-query-list-parser': 4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) postcss: 8.4.47 - postcss-custom-properties@14.0.3(postcss@8.4.47): + postcss-custom-properties@14.0.4(postcss@8.4.47): dependencies: - '@csstools/cascade-layer-name-parser': 2.0.3(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/cascade-layer-name-parser': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/utilities': 2.0.0(postcss@8.4.47) postcss: 8.4.47 postcss-value-parser: 4.2.0 - postcss-custom-selectors@8.0.3(postcss@8.4.47): + postcss-custom-selectors@8.0.4(postcss@8.4.47): dependencies: - '@csstools/cascade-layer-name-parser': 2.0.3(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/cascade-layer-name-parser': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 postcss: 8.4.47 postcss-selector-parser: 7.0.0 @@ -20907,22 +21164,22 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.4.47 - postcss-lab-function@7.0.4(postcss@8.4.47): + postcss-lab-function@7.0.5(postcss@8.4.47): dependencies: - '@csstools/css-color-parser': 3.0.4(@csstools/css-parser-algorithms@3.0.3(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.3(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.5(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) '@csstools/utilities': 2.0.0(postcss@8.4.47) postcss: 8.4.47 - postcss-load-config@3.1.4(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)): + postcss-load-config@3.1.4(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: postcss: 8.4.47 - ts-node: 10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3) + ts-node: 10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3) postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)): dependencies: @@ -20932,6 +21189,14 @@ snapshots: postcss: 8.4.47 ts-node: 10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3) + postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)): + dependencies: + lilconfig: 3.1.2 + yaml: 2.6.0 + optionalDependencies: + postcss: 8.4.47 + ts-node: 10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3) + postcss-load-config@6.0.1(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(yaml@2.6.0): dependencies: lilconfig: 3.1.2 @@ -20985,37 +21250,37 @@ snapshots: postcss: 8.4.47 postcss-value-parser: 4.2.0 - postcss-preset-env@10.0.8(postcss@8.4.47): + postcss-preset-env@10.0.9(postcss@8.4.47): dependencies: '@csstools/postcss-cascade-layers': 5.0.1(postcss@8.4.47) - '@csstools/postcss-color-function': 4.0.4(postcss@8.4.47) - '@csstools/postcss-color-mix-function': 3.0.4(postcss@8.4.47) - '@csstools/postcss-content-alt-text': 2.0.3(postcss@8.4.47) - '@csstools/postcss-exponential-functions': 2.0.3(postcss@8.4.47) + '@csstools/postcss-color-function': 4.0.5(postcss@8.4.47) + '@csstools/postcss-color-mix-function': 3.0.5(postcss@8.4.47) + '@csstools/postcss-content-alt-text': 2.0.4(postcss@8.4.47) + '@csstools/postcss-exponential-functions': 2.0.4(postcss@8.4.47) '@csstools/postcss-font-format-keywords': 4.0.0(postcss@8.4.47) - '@csstools/postcss-gamut-mapping': 2.0.4(postcss@8.4.47) - '@csstools/postcss-gradients-interpolation-method': 5.0.4(postcss@8.4.47) - '@csstools/postcss-hwb-function': 4.0.4(postcss@8.4.47) + '@csstools/postcss-gamut-mapping': 2.0.5(postcss@8.4.47) + '@csstools/postcss-gradients-interpolation-method': 5.0.5(postcss@8.4.47) + '@csstools/postcss-hwb-function': 4.0.5(postcss@8.4.47) '@csstools/postcss-ic-unit': 4.0.0(postcss@8.4.47) '@csstools/postcss-initial': 2.0.0(postcss@8.4.47) '@csstools/postcss-is-pseudo-class': 5.0.1(postcss@8.4.47) - '@csstools/postcss-light-dark-function': 2.0.6(postcss@8.4.47) + '@csstools/postcss-light-dark-function': 2.0.7(postcss@8.4.47) '@csstools/postcss-logical-float-and-clear': 3.0.0(postcss@8.4.47) '@csstools/postcss-logical-overflow': 2.0.0(postcss@8.4.47) '@csstools/postcss-logical-overscroll-behavior': 2.0.0(postcss@8.4.47) '@csstools/postcss-logical-resize': 3.0.0(postcss@8.4.47) - '@csstools/postcss-logical-viewport-units': 3.0.2(postcss@8.4.47) - '@csstools/postcss-media-minmax': 2.0.3(postcss@8.4.47) - '@csstools/postcss-media-queries-aspect-ratio-number-values': 3.0.3(postcss@8.4.47) + '@csstools/postcss-logical-viewport-units': 3.0.3(postcss@8.4.47) + '@csstools/postcss-media-minmax': 2.0.4(postcss@8.4.47) + '@csstools/postcss-media-queries-aspect-ratio-number-values': 3.0.4(postcss@8.4.47) '@csstools/postcss-nested-calc': 4.0.0(postcss@8.4.47) '@csstools/postcss-normalize-display-values': 4.0.0(postcss@8.4.47) - '@csstools/postcss-oklab-function': 4.0.4(postcss@8.4.47) + '@csstools/postcss-oklab-function': 4.0.5(postcss@8.4.47) '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.4.47) - '@csstools/postcss-relative-color-syntax': 3.0.4(postcss@8.4.47) + '@csstools/postcss-relative-color-syntax': 3.0.5(postcss@8.4.47) '@csstools/postcss-scope-pseudo-class': 4.0.1(postcss@8.4.47) - '@csstools/postcss-stepped-value-functions': 4.0.3(postcss@8.4.47) + '@csstools/postcss-stepped-value-functions': 4.0.4(postcss@8.4.47) '@csstools/postcss-text-decoration-shorthand': 4.0.1(postcss@8.4.47) - '@csstools/postcss-trigonometric-functions': 4.0.3(postcss@8.4.47) + '@csstools/postcss-trigonometric-functions': 4.0.4(postcss@8.4.47) '@csstools/postcss-unset-value': 4.0.0(postcss@8.4.47) autoprefixer: 10.4.20(postcss@8.4.47) browserslist: 4.24.2 @@ -21026,12 +21291,12 @@ snapshots: postcss: 8.4.47 postcss-attribute-case-insensitive: 7.0.1(postcss@8.4.47) postcss-clamp: 4.1.0(postcss@8.4.47) - postcss-color-functional-notation: 7.0.4(postcss@8.4.47) + postcss-color-functional-notation: 7.0.5(postcss@8.4.47) postcss-color-hex-alpha: 10.0.0(postcss@8.4.47) postcss-color-rebeccapurple: 10.0.0(postcss@8.4.47) - postcss-custom-media: 11.0.4(postcss@8.4.47) - postcss-custom-properties: 14.0.3(postcss@8.4.47) - postcss-custom-selectors: 8.0.3(postcss@8.4.47) + postcss-custom-media: 11.0.5(postcss@8.4.47) + postcss-custom-properties: 14.0.4(postcss@8.4.47) + postcss-custom-selectors: 8.0.4(postcss@8.4.47) postcss-dir-pseudo-class: 9.0.1(postcss@8.4.47) postcss-double-position-gradients: 6.0.0(postcss@8.4.47) postcss-focus-visible: 10.0.1(postcss@8.4.47) @@ -21039,7 +21304,7 @@ snapshots: postcss-font-variant: 5.0.0(postcss@8.4.47) postcss-gap-properties: 6.0.0(postcss@8.4.47) postcss-image-set-function: 7.0.0(postcss@8.4.47) - postcss-lab-function: 7.0.4(postcss@8.4.47) + postcss-lab-function: 7.0.5(postcss@8.4.47) postcss-logical: 8.0.0(postcss@8.4.47) postcss-nesting: 13.0.1(postcss@8.4.47) postcss-opacity-percentage: 3.0.0(postcss@8.4.47) @@ -21194,20 +21459,20 @@ snapshots: dependencies: prosemirror-state: 1.4.3 prosemirror-transform: 1.10.2 - prosemirror-view: 1.35.0 + prosemirror-view: 1.36.0 prosemirror-gapcursor@1.3.2: dependencies: prosemirror-keymap: 1.2.2 prosemirror-model: 1.23.0 prosemirror-state: 1.4.3 - prosemirror-view: 1.35.0 + prosemirror-view: 1.36.0 prosemirror-history@1.4.1: dependencies: prosemirror-state: 1.4.3 prosemirror-transform: 1.10.2 - prosemirror-view: 1.35.0 + prosemirror-view: 1.36.0 rope-sequence: 1.3.4 prosemirror-inputrules@1.4.0: @@ -21251,7 +21516,7 @@ snapshots: dependencies: prosemirror-model: 1.23.0 prosemirror-transform: 1.10.2 - prosemirror-view: 1.35.0 + prosemirror-view: 1.36.0 prosemirror-tables@1.6.1: dependencies: @@ -21259,21 +21524,21 @@ snapshots: prosemirror-model: 1.23.0 prosemirror-state: 1.4.3 prosemirror-transform: 1.10.2 - prosemirror-view: 1.35.0 + prosemirror-view: 1.36.0 - prosemirror-trailing-node@3.0.0(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0): + prosemirror-trailing-node@3.0.0(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.36.0): dependencies: '@remirror/core-constants': 3.0.0 escape-string-regexp: 4.0.0 prosemirror-model: 1.23.0 prosemirror-state: 1.4.3 - prosemirror-view: 1.35.0 + prosemirror-view: 1.36.0 prosemirror-transform@1.10.2: dependencies: prosemirror-model: 1.23.0 - prosemirror-view@1.35.0: + prosemirror-view@1.36.0: dependencies: prosemirror-model: 1.23.0 prosemirror-state: 1.4.3 @@ -21376,7 +21641,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-day-picker@9.2.1(react@18.3.1): + react-day-picker@9.3.0(react@18.3.1): dependencies: '@date-fns/tz': 1.2.0 date-fns: 4.1.0 @@ -21598,7 +21863,7 @@ snapshots: dependencies: decimal.js-light: 2.5.1 - recharts@2.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + recharts@2.13.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: clsx: 2.1.1 eventemitter3: 4.0.7 @@ -21970,6 +22235,30 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.24.3 fsevents: 2.3.3 + rollup@4.24.4: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.24.4 + '@rollup/rollup-android-arm64': 4.24.4 + '@rollup/rollup-darwin-arm64': 4.24.4 + '@rollup/rollup-darwin-x64': 4.24.4 + '@rollup/rollup-freebsd-arm64': 4.24.4 + '@rollup/rollup-freebsd-x64': 4.24.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.24.4 + '@rollup/rollup-linux-arm-musleabihf': 4.24.4 + '@rollup/rollup-linux-arm64-gnu': 4.24.4 + '@rollup/rollup-linux-arm64-musl': 4.24.4 + '@rollup/rollup-linux-powerpc64le-gnu': 4.24.4 + '@rollup/rollup-linux-riscv64-gnu': 4.24.4 + '@rollup/rollup-linux-s390x-gnu': 4.24.4 + '@rollup/rollup-linux-x64-gnu': 4.24.4 + '@rollup/rollup-linux-x64-musl': 4.24.4 + '@rollup/rollup-win32-arm64-msvc': 4.24.4 + '@rollup/rollup-win32-ia32-msvc': 4.24.4 + '@rollup/rollup-win32-x64-msvc': 4.24.4 + fsevents: 2.3.3 + rope-sequence@1.3.4: {} rrdom@0.1.7: @@ -22182,7 +22471,7 @@ snapshots: transitivePeerDependencies: - supports-color - sonner@1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + sonner@1.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -22333,7 +22622,7 @@ snapshots: pirates: 4.0.6 ts-interface-checker: 0.1.13 - sugar-high@0.7.4: {} + sugar-high@0.7.5: {} superstruct@1.0.4: {} @@ -22354,9 +22643,9 @@ snapshots: tailwind-merge@2.5.4: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3))): dependencies: - tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)) + tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)) tailwindcss@3.4.0(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)): dependencies: @@ -22385,7 +22674,7 @@ snapshots: transitivePeerDependencies: - ts-node - tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)): + tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -22404,7 +22693,7 @@ snapshots: postcss: 8.4.47 postcss-import: 15.1.0(postcss@8.4.47) postcss-js: 4.0.1(postcss@8.4.47) - postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)) + postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)) postcss-nested: 6.2.0(postcss@8.4.47) postcss-selector-parser: 6.1.2 resolve: 1.22.8 @@ -22582,13 +22871,34 @@ snapshots: '@swc/core': 1.7.26 optional: true + ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.9.0 + acorn: 8.14.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.6.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.7.26 + optional: true + tslib@2.0.1: {} tslib@2.7.0: {} tslib@2.8.1: {} - tsup@6.7.0(@swc/core@1.7.26)(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3): + tsup@6.7.0(@swc/core@1.7.26)(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3))(typescript@5.6.3): dependencies: bundle-require: 4.2.1(esbuild@0.17.19) cac: 6.7.14 @@ -22598,7 +22908,7 @@ snapshots: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 3.1.4(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.8.6)(typescript@5.6.3)) + postcss-load-config: 3.1.4(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26)(@types/node@22.9.0)(typescript@5.6.3)) resolve-from: 5.0.0 rollup: 3.29.5 source-map: 0.8.0-beta.0 @@ -22640,34 +22950,6 @@ snapshots: - tsx - yaml - tsup@8.3.5(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.6.0): - dependencies: - bundle-require: 5.0.0(esbuild@0.24.0) - cac: 6.7.14 - chokidar: 4.0.1 - consola: 3.2.3 - debug: 4.3.7 - esbuild: 0.24.0 - joycon: 3.1.1 - picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(yaml@2.6.0) - resolve-from: 5.0.0 - rollup: 4.24.3 - source-map: 0.8.0-beta.0 - sucrase: 3.35.0 - tinyexec: 0.3.1 - tinyglobby: 0.2.10 - tree-kill: 1.2.2 - optionalDependencies: - '@swc/core': 1.7.26 - postcss: 8.4.47 - typescript: 5.6.3 - transitivePeerDependencies: - - jiti - - supports-color - - tsx - - yaml - tsup@8.3.5(@swc/core@1.7.26)(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.6.0): dependencies: bundle-require: 5.0.0(esbuild@0.24.0) @@ -23105,7 +23387,7 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-plugin-html@3.2.2(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0)): + vite-plugin-html@3.2.2(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0)): dependencies: '@rollup/pluginutils': 4.2.1 colorette: 2.0.20 @@ -23119,11 +23401,11 @@ snapshots: html-minifier-terser: 6.1.0 node-html-parser: 5.4.2 pathe: 0.2.0 - vite: 5.4.10(@types/node@22.8.6)(terser@5.36.0) + vite: 5.4.10(@types/node@22.9.0)(terser@5.36.0) - vite-plugin-node-polyfills@0.16.0(rollup@4.24.3)(vite@4.5.5(@types/node@22.8.6)(terser@5.36.0)): + vite-plugin-node-polyfills@0.16.0(rollup@4.24.4)(vite@4.5.5(@types/node@22.8.6)(terser@5.36.0)): dependencies: - '@rollup/plugin-inject': 5.0.5(rollup@4.24.3) + '@rollup/plugin-inject': 5.0.5(rollup@4.24.4) buffer-polyfill: buffer@6.0.3 node-stdlib-browser: 1.2.1 process: 0.11.10 @@ -23131,12 +23413,12 @@ snapshots: transitivePeerDependencies: - rollup - vite-plugin-pwa@0.20.5(@vite-pwa/assets-generator@0.2.6)(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0): + vite-plugin-pwa@0.20.5(@vite-pwa/assets-generator@0.2.6)(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0): dependencies: debug: 4.3.7 pretty-bytes: 6.1.1 tinyglobby: 0.2.10 - vite: 5.4.10(@types/node@22.8.6)(terser@5.36.0) + vite: 5.4.10(@types/node@22.9.0)(terser@5.36.0) workbox-build: 7.1.1(@types/babel__core@7.20.5) workbox-window: 7.3.0 optionalDependencies: @@ -23144,13 +23426,13 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-static-copy@1.0.6(vite@5.4.10(@types/node@22.8.6)(terser@5.36.0)): + vite-plugin-static-copy@1.0.6(vite@5.4.10(@types/node@22.9.0)(terser@5.36.0)): dependencies: chokidar: 3.6.0 fast-glob: 3.3.2 fs-extra: 11.2.0 picocolors: 1.1.1 - vite: 5.4.10(@types/node@22.8.6)(terser@5.36.0) + vite: 5.4.10(@types/node@22.9.0)(terser@5.36.0) vite@4.5.5(@types/node@22.8.6)(terser@5.36.0): dependencies: @@ -23166,12 +23448,22 @@ snapshots: dependencies: esbuild: 0.21.5 postcss: 8.4.47 - rollup: 4.24.3 + rollup: 4.24.4 optionalDependencies: '@types/node': 22.8.6 fsevents: 2.3.3 terser: 5.36.0 + vite@5.4.10(@types/node@22.9.0)(terser@5.36.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.47 + rollup: 4.24.4 + optionalDependencies: + '@types/node': 22.9.0 + fsevents: 2.3.3 + terser: 5.36.0 + vm-browserify@1.1.2: {} void-elements@3.1.0: {} @@ -23397,12 +23689,12 @@ snapshots: xycolors@0.1.2: {} - y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20): + y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.36.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20): dependencies: lib0: 0.2.98 prosemirror-model: 1.23.0 prosemirror-state: 1.4.3 - prosemirror-view: 1.35.0 + prosemirror-view: 1.36.0 y-protocols: 1.0.6(yjs@13.6.20) yjs: 13.6.20 diff --git a/tus/package.json b/tus/package.json index c86c92a21..d061d2435 100644 --- a/tus/package.json +++ b/tus/package.json @@ -13,14 +13,19 @@ "author": "CellaJS ", "license": "UNLICENSED", "dependencies": { - "@cellajs/imado": "^0.1.3", + "@aws-sdk/client-s3": "^3.685.0", "@t3-oss/env-core": "^0.11.0", + "@tus/file-store": "^1.5.0", + "@tus/s3-store": "^1.6.0", + "@tus/server": "^1.9.0", "config": "workspace:*", "dotenv": "^16.4.5", + "jsonwebtoken": "^9.0.2", "tsx": "^4.17.0", "zod": "^3.23.8" }, "devDependencies": { + "@types/jsonwebtoken": "^9.0.7", "tsup": "^8.2.4", "typescript": "^5.5.4" } diff --git a/tus/src/imado-tus.ts b/tus/src/imado-tus.ts new file mode 100644 index 000000000..3816f7b7c --- /dev/null +++ b/tus/src/imado-tus.ts @@ -0,0 +1,125 @@ +import { CopyObjectCommand, DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3'; +import { FileStore } from '@tus/file-store'; +import { S3Store } from '@tus/s3-store'; +import { MemoryLocker, Server, type ServerOptions, type Upload } from '@tus/server'; + +import type http from 'node:http'; +import jwt from 'jsonwebtoken'; + +type ModifiedServerOptions = Omit; + +interface AWSCredentials { + bucket: string; + region: string; + accessKeyId: string; + secretAccessKey: string; +} + +interface TusOptions { + secret: string; + credentials: AWSCredentials; + serverOptions?: ModifiedServerOptions; +} + +async function moveS3Object(oldKey: string, newKey: string, credentials: AWSCredentials) { + const s3Client = new S3Client({ + region: credentials.region, + credentials: { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + }, + }); + + await s3Client.send( + new CopyObjectCommand({ + Bucket: credentials.bucket, + CopySource: `${credentials.bucket}/${oldKey}`, + Key: newKey, + MetadataDirective: 'COPY', + }), + ); + + await s3Client.send( + new DeleteObjectCommand({ + Bucket: credentials.bucket, + Key: oldKey, + }), + ); +} + +function optionallyStoreInS3(options: ServerOptions & { datastore: FileStore }, credentials: AWSCredentials) { + return { + ...options, + datastore: new S3Store({ + partSize: 8 * 1024 * 1024, // Each uploaded part will have ~8MiB, + s3ClientConfig: { + bucket: credentials.bucket, + region: credentials.region, + credentials: { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + }, + }, + }), + async onUploadFinish(req: http.IncomingMessage, res: http.ServerResponse, upload: Upload) { + const auth = req.headers.authorization; + const token = (auth as string).split(' ')[1]; + const sub = jwt.decode(token)?.sub; + + // sub in JWT is used as path in S3 (e.g. user id, or w/ organization id) + await moveS3Object(upload.id, `${sub}/${upload.id}`, credentials); + // also move the info file + await moveS3Object(`${upload.id}.info`, `${sub}/${upload.id}.info`, credentials); + + return res; + }, + }; +} + +function extractContentType(upload: Upload) { + const mimeType = upload.metadata?.type; + return mimeType; +} + +export const ImadoTus = (opts: TusOptions) => { + return new Server( + optionallyStoreInS3( + { + ...opts.serverOptions, + path: '/upload', + locker: new MemoryLocker(), + datastore: new FileStore({ + directory: './files', + }), + async onUploadCreate(_, res, upload) { + console.info('Upload created:', upload); + const contentType = extractContentType(upload) ?? null; + const metadata = { ...upload.metadata, contentType }; + return { res, metadata }; + }, + async onIncomingRequest(req: http.IncomingMessage, _res: http.ServerResponse) { + const auth = req.headers.authorization; + + if (!auth) { + throw { status_code: 401, body: 'Unauthorized' }; + } + + try { + const token = auth.split(' ')[1]; + + // If you want to know who uploaded or create secrets per client you can implement something like this: + // const sub = jwt.decode(token)?.sub; + // const secret = await db.query('SELECT secret FROM users WHERE id = $1', [sub]); + + // Verify secret and token, will throw error if invalid + await jwt.verify(token, opts.secret); + } catch (error) { + console.error('Invalid token', error); + throw { status_code: 401, body: 'Invalid token' }; + } + }, + }, + opts.credentials, + ), + ); +}; diff --git a/tus/src/index.ts b/tus/src/index.ts index a2d883856..126d5b2ed 100644 --- a/tus/src/index.ts +++ b/tus/src/index.ts @@ -1,6 +1,6 @@ -import { ImadoTus } from '@cellajs/imado'; import { config } from 'config'; import { env } from '../env'; +import { ImadoTus } from './imado-tus'; const tus = ImadoTus({ secret: env.TUS_UPLOAD_API_SECRET, From 54dab03c4df54e097b451f0dd042fff21517ecad Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 6 Nov 2024 19:21:27 +0200 Subject: [PATCH 14/36] refactor: Refactor schemas --- backend/src/db/db.ts | 36 ++++++----- backend/src/db/schema/attachments.ts | 15 +++-- backend/src/db/schema/memberships.ts | 14 ++--- backend/src/db/schema/oauth-accounts.ts | 8 +-- backend/src/db/schema/organizations.ts | 30 ++++----- backend/src/db/schema/passkeys.ts | 8 +-- backend/src/db/schema/requests.ts | 2 +- backend/src/db/schema/sessions.ts | 16 ++--- backend/src/db/schema/tokens.ts | 8 +-- backend/src/db/schema/users.ts | 28 ++++----- backend/src/middlewares/rate-limiter/index.ts | 4 +- .../organizations/attachments-table/index.tsx | 62 +++++++------------ 12 files changed, 111 insertions(+), 120 deletions(-) diff --git a/backend/src/db/db.ts b/backend/src/db/db.ts index 564f54b08..e2442e8fa 100644 --- a/backend/src/db/db.ts +++ b/backend/src/db/db.ts @@ -1,25 +1,33 @@ -import { drizzle as pgDrizzle } from 'drizzle-orm/node-postgres'; +import { type NodePgClient, drizzle as pgDrizzle } from 'drizzle-orm/node-postgres'; import { drizzle as pgliteDrizzle } from 'drizzle-orm/pglite'; -import pg from 'pg'; import { env } from '#/../env'; +import type { PGlite } from '@electric-sql/pglite'; import { config } from 'config'; -import { sql } from 'drizzle-orm'; +import { type DrizzleConfig, sql } from 'drizzle-orm'; import type { PgDatabase } from 'drizzle-orm/pg-core'; -export const queryClient = env.PGLITE - ? await (await import('@electric-sql/pglite')).PGlite.create({ - dataDir: './.db', - }) - : new pg.Pool({ - connectionString: env.DATABASE_URL, - connectionTimeoutMillis: 10000, - }); - -const dbConfig = { +const dbConfig: DrizzleConfig = { logger: config.debug, + casing: 'snake_case', }; + // biome-ignore lint/suspicious/noExplicitAny: Can be two different types -export const db: PgDatabase = queryClient instanceof pg.Pool ? pgDrizzle(queryClient, dbConfig) : pgliteDrizzle(queryClient, dbConfig); +export const db: PgDatabase & { + $client: PGlite | NodePgClient; +} = env.PGLITE + ? pgliteDrizzle({ + connection: { + dataDir: './.db', + }, + ...dbConfig, + }) + : pgDrizzle({ + connection: { + connectionString: env.DATABASE_URL, + connectionTimeoutMillis: 10000, + }, + ...dbConfig, + }); export const coalesce = (column: T, value: number) => sql`COALESCE(${column}, ${value})`.mapWith(Number); diff --git a/backend/src/db/schema/attachments.ts b/backend/src/db/schema/attachments.ts index 1bf55b67b..bf0b061b1 100644 --- a/backend/src/db/schema/attachments.ts +++ b/backend/src/db/schema/attachments.ts @@ -1,3 +1,4 @@ +import { getTableColumns } from 'drizzle-orm'; import { pgTable, timestamp, varchar } from 'drizzle-orm/pg-core'; import { nanoid } from '#/utils/nanoid'; import { organizationsTable } from './organizations'; @@ -7,26 +8,28 @@ export const attachmentsTable = pgTable('attachments', { id: varchar().primaryKey().$defaultFn(nanoid), name: varchar().notNull().default('attachment'), filename: varchar().notNull(), - contentType: varchar('content_type').notNull(), + contentType: varchar().notNull(), size: varchar().notNull(), entity: varchar({ enum: ['attachment'] }) .notNull() .default('attachment'), url: varchar().notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), - createdBy: varchar('created_by').references(() => usersTable.id, { + createdAt: timestamp().defaultNow().notNull(), + createdBy: varchar().references(() => usersTable.id, { onDelete: 'set null', }), - modifiedAt: timestamp('modified_at'), - modifiedBy: varchar('modified_by').references(() => usersTable.id, { + modifiedAt: timestamp(), + modifiedBy: varchar().references(() => usersTable.id, { onDelete: 'set null', }), - organizationId: varchar('organization_id') + organizationId: varchar() .notNull() .references(() => organizationsTable.id, { onDelete: 'cascade', }), }); +export const attachmentsTableColumns = getTableColumns(attachmentsTable); + export type AttachmentModel = typeof attachmentsTable.$inferSelect; export type InsertAttachmentModel = typeof attachmentsTable.$inferInsert; diff --git a/backend/src/db/schema/memberships.ts b/backend/src/db/schema/memberships.ts index 606a06ffb..6f53dd02d 100644 --- a/backend/src/db/schema/memberships.ts +++ b/backend/src/db/schema/memberships.ts @@ -9,18 +9,18 @@ const roleEnum = config.rolesByType.entityRoles; export const membershipsTable = pgTable('memberships', { id: varchar().primaryKey().$defaultFn(nanoid), type: varchar({ enum: config.contextEntityTypes }).notNull(), - userId: varchar('user_id') + userId: varchar() .notNull() .references(() => usersTable.id, { onDelete: 'cascade' }), role: varchar({ enum: roleEnum }).notNull().default('member'), - createdAt: timestamp('created_at').defaultNow().notNull(), - createdBy: varchar('created_by').references(() => usersTable.id, { onDelete: 'set null' }), - modifiedAt: timestamp('modified_at'), - modifiedBy: varchar('modified_by').references(() => usersTable.id, { onDelete: 'set null' }), + createdAt: timestamp().defaultNow().notNull(), + createdBy: varchar().references(() => usersTable.id, { onDelete: 'set null' }), + modifiedAt: timestamp(), + modifiedBy: varchar().references(() => usersTable.id, { onDelete: 'set null' }), archived: boolean().default(false).notNull(), muted: boolean().default(false).notNull(), - order: doublePrecision('sort_order').notNull(), - organizationId: varchar('organization_id') + order: doublePrecision().notNull(), + organizationId: varchar() .notNull() .references(() => organizationsTable.id, { onDelete: 'cascade' }), }); diff --git a/backend/src/db/schema/oauth-accounts.ts b/backend/src/db/schema/oauth-accounts.ts index bb6830ec9..b886fa55e 100644 --- a/backend/src/db/schema/oauth-accounts.ts +++ b/backend/src/db/schema/oauth-accounts.ts @@ -8,12 +8,12 @@ export const supportedOauthProviders = ['github', 'google', 'microsoft'] as cons export const oauthAccountsTable = pgTable( 'oauth_accounts', { - providerId: varchar('provider_id', { enum: supportedOauthProviders }).notNull(), - providerUserId: varchar('provider_user_id').notNull(), - userId: varchar('user_id') + providerId: varchar({ enum: supportedOauthProviders }).notNull(), + providerUserId: varchar().notNull(), + userId: varchar() .notNull() .references(() => usersTable.id, { onDelete: 'cascade' }), - createdAt: timestamp('created_at').defaultNow().notNull(), + createdAt: timestamp().defaultNow().notNull(), }, (table) => { return { diff --git a/backend/src/db/schema/organizations.ts b/backend/src/db/schema/organizations.ts index 01acb1cc6..af42f3196 100644 --- a/backend/src/db/schema/organizations.ts +++ b/backend/src/db/schema/organizations.ts @@ -15,26 +15,26 @@ export const organizationsTable = pgTable( .notNull() .default('organization'), name: varchar().notNull(), - shortName: varchar('short_name'), + shortName: varchar(), slug: varchar().unique().notNull(), country: varchar(), timezone: varchar(), - defaultLanguage: varchar('default_language', { enum: languages }).notNull().default(config.defaultLanguage), + defaultLanguage: varchar({ enum: languages }).notNull().default(config.defaultLanguage), languages: json().$type().notNull().default([config.defaultLanguage]), - notificationEmail: varchar('notification_email'), - emailDomains: json('email_domains').$type().notNull().default([]), + notificationEmail: varchar(), + emailDomains: json().$type().notNull().default([]), color: varchar(), - thumbnailUrl: varchar('thumbnail_url'), - bannerUrl: varchar('banner_url'), - logoUrl: varchar('logo_url'), - websiteUrl: varchar('website_url'), - welcomeText: varchar('welcome_text'), - authStrategies: json('auth_strategies').$type().notNull().default([]), - chatSupport: boolean('chat_support').notNull().default(false), - createdAt: timestamp('created_at').defaultNow().notNull(), - createdBy: varchar('created_by').references(() => usersTable.id, { onDelete: 'set null' }), - modifiedAt: timestamp('modified_at'), - modifiedBy: varchar('modified_by').references(() => usersTable.id, { onDelete: 'set null' }), + thumbnailUrl: varchar(), + bannerUrl: varchar(), + logoUrl: varchar(), + websiteUrl: varchar(), + welcomeText: varchar(), + authStrategies: json().$type().notNull().default([]), + chatSupport: boolean().notNull().default(false), + createdAt: timestamp().defaultNow().notNull(), + createdBy: varchar().references(() => usersTable.id, { onDelete: 'set null' }), + modifiedAt: timestamp(), + modifiedBy: varchar().references(() => usersTable.id, { onDelete: 'set null' }), }, (table) => { return { diff --git a/backend/src/db/schema/passkeys.ts b/backend/src/db/schema/passkeys.ts index e0b070611..9228f7b46 100644 --- a/backend/src/db/schema/passkeys.ts +++ b/backend/src/db/schema/passkeys.ts @@ -4,12 +4,12 @@ import { nanoid } from '#/utils/nanoid'; export const passkeysTable = pgTable('passkeys', { id: varchar().primaryKey().$defaultFn(nanoid), - userEmail: varchar('user_email') + userEmail: varchar() .notNull() .references(() => usersTable.email, { onDelete: 'cascade' }), - credentialId: varchar('credential_id').notNull(), - publicKey: varchar('public_key').notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), + credentialId: varchar().notNull(), + publicKey: varchar().notNull(), + createdAt: timestamp().defaultNow().notNull(), }); export type PasskeyModel = typeof passkeysTable.$inferSelect; diff --git a/backend/src/db/schema/requests.ts b/backend/src/db/schema/requests.ts index f7c0a329d..8db568fb5 100644 --- a/backend/src/db/schema/requests.ts +++ b/backend/src/db/schema/requests.ts @@ -11,7 +11,7 @@ export const requestsTable = pgTable( message: varchar(), email: varchar().notNull(), type: varchar({ enum: requestTypeEnum }).notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), + createdAt: timestamp().defaultNow().notNull(), }, (table) => { return { diff --git a/backend/src/db/schema/sessions.ts b/backend/src/db/schema/sessions.ts index cd0059933..8a23f2337 100644 --- a/backend/src/db/schema/sessions.ts +++ b/backend/src/db/schema/sessions.ts @@ -5,16 +5,16 @@ export const sessionsTable = pgTable( 'sessions', { id: varchar().primaryKey(), - userId: varchar('user_id') + userId: varchar() .notNull() .references(() => usersTable.id, { onDelete: 'cascade' }), - deviceName: varchar('device_name'), - deviceType: varchar('device_type', { enum: ['desktop', 'mobile'] }) + deviceName: varchar(), + deviceType: varchar({ enum: ['desktop', 'mobile'] }) .notNull() .default('desktop'), - deviceOs: varchar('device_os'), + deviceOs: varchar(), browser: varchar(), - authStrategy: varchar('auth_strategy', { + authStrategy: varchar({ enum: ['github', 'google', 'microsoft', 'password', 'passkey'], }), type: varchar({ @@ -22,9 +22,9 @@ export const sessionsTable = pgTable( }) .notNull() .default('regular'), - createdAt: timestamp('created_at').defaultNow().notNull(), - expiresAt: timestamp('expires_at', { withTimezone: true, mode: 'date' }).notNull(), - adminUserId: varchar('admin_user_id').references(() => usersTable.id, { onDelete: 'cascade' }), + createdAt: timestamp().defaultNow().notNull(), + expiresAt: timestamp({ withTimezone: true, mode: 'date' }).notNull(), + adminUserId: varchar().references(() => usersTable.id, { onDelete: 'cascade' }), }, (table) => { return { diff --git a/backend/src/db/schema/tokens.ts b/backend/src/db/schema/tokens.ts index 105b417e7..7cdc23f1b 100644 --- a/backend/src/db/schema/tokens.ts +++ b/backend/src/db/schema/tokens.ts @@ -11,10 +11,10 @@ export const tokensTable = pgTable('tokens', { type: varchar({ enum: tokenTypeEnum }).notNull(), email: varchar(), role: varchar({ enum: roleEnum }), - userId: varchar('user_id').references(() => usersTable.id, { onDelete: 'cascade' }), - organizationId: varchar('organization_id').references(() => organizationsTable.id, { onDelete: 'cascade' }), - createdAt: timestamp('created_at').defaultNow().notNull(), - expiresAt: timestamp('expires_at', { withTimezone: true, mode: 'date' }).notNull(), + userId: varchar().references(() => usersTable.id, { onDelete: 'cascade' }), + organizationId: varchar().references(() => organizationsTable.id, { onDelete: 'cascade' }), + createdAt: timestamp().defaultNow().notNull(), + expiresAt: timestamp({ withTimezone: true, mode: 'date' }).notNull(), }); export type TokenModel = typeof tokensTable.$inferSelect; diff --git a/backend/src/db/schema/users.ts b/backend/src/db/schema/users.ts index 1753ab353..471840192 100644 --- a/backend/src/db/schema/users.ts +++ b/backend/src/db/schema/users.ts @@ -11,30 +11,30 @@ export const usersTable = pgTable( entity: varchar({ enum: ['user'] }) .notNull() .default('user'), - hashedPassword: varchar('hashed_password'), + hashedPassword: varchar(), slug: varchar().unique().notNull(), - unsubscribeToken: varchar('unsubscribe_token').unique().notNull(), + unsubscribeToken: varchar().unique().notNull(), name: varchar().notNull(), - firstName: varchar('first_name'), - lastName: varchar('last_name'), + firstName: varchar(), + lastName: varchar(), email: varchar().notNull().unique(), - emailVerified: boolean('email_verified').notNull().default(false), + emailVerified: boolean().notNull().default(false), bio: varchar(), language: varchar({ enum: ['en', 'nl'], }) .notNull() .default(config.defaultLanguage), - bannerUrl: varchar('banner_url'), - thumbnailUrl: varchar('thumbnail_url'), + bannerUrl: varchar(), + thumbnailUrl: varchar(), newsletter: boolean().notNull().default(false), - lastSeenAt: timestamp('last_seen_at'), // last time any GET request has been made - lastStartedAt: timestamp('last_started_at'), // last time GET me - lastSignInAt: timestamp('last_sign_in_at'), // last time user went through authentication flow - createdAt: timestamp('created_at').defaultNow().notNull(), - modifiedAt: timestamp('modified_at'), - modifiedBy: varchar('modified_by'), - role: varchar('role', { enum: roleEnum }).notNull().default('user'), + lastSeenAt: timestamp(), // last time any GET request has been made + lastStartedAt: timestamp(), // last time GET me + lastSignInAt: timestamp(), // last time user went through authentication flow + createdAt: timestamp().defaultNow().notNull(), + modifiedAt: timestamp(), + modifiedBy: varchar(), + role: varchar({ enum: roleEnum }).notNull().default('user'), }, (table) => { return { diff --git a/backend/src/middlewares/rate-limiter/index.ts b/backend/src/middlewares/rate-limiter/index.ts index 541dae5f7..a58a3dd40 100644 --- a/backend/src/middlewares/rate-limiter/index.ts +++ b/backend/src/middlewares/rate-limiter/index.ts @@ -4,7 +4,7 @@ import { type IRateLimiterPostgresOptions, RateLimiterMemory, RateLimiterPostgre import { errorResponse } from '#/lib/errors'; import { env } from '#/../env'; -import { queryClient } from '#/db/db'; +import { db } from '#/db/db'; import { getContextUser } from '#/lib/context'; import type { Env } from '#/types/app'; @@ -100,7 +100,7 @@ export const getRateLimiterInstance = (options: Omit = defaultOptions, mode: RateLimiterMode = 'fail') => diff --git a/frontend/src/modules/organizations/attachments-table/index.tsx b/frontend/src/modules/organizations/attachments-table/index.tsx index 761cd70aa..9de40702e 100644 --- a/frontend/src/modules/organizations/attachments-table/index.tsx +++ b/frontend/src/modules/organizations/attachments-table/index.tsx @@ -33,6 +33,7 @@ import { useGeneralStore } from '~/store/general'; import { useUserStore } from '~/store/user'; import { type Attachment, type Organization, UploadType } from '~/types/common'; import { objectKeys } from '~/utils/object'; +import { attachmentsTableColumns } from '#/db/schema/attachments'; import type { attachmentsQuerySchema } from '#/modules/attachments/schema'; import { env } from '../../../../env'; import { useColumns } from './columns'; @@ -62,6 +63,20 @@ interface AttachmentsTableProps { isSheet?: boolean; } +const parseRawAttachment = (rawAttachment: RawAttachment): Attachment => { + const columnEntries = Object.entries(attachmentsTableColumns); + const attachment = {} as unknown as Attachment; + for (const key of objectKeys(rawAttachment)) { + const columnEntry = columnEntries.find(([, c]) => c.name === key); + if (!columnEntry) { + continue; + } + const columnName = columnEntry[0] as keyof Attachment; + attachment[columnName] = rawAttachment[key] as never; + } + return attachment; +}; + const attachmentShape = (organization_id?: string): ShapeStreamOptions => ({ url: new URL('/v1/shape/attachments', config.electricUrl).href, where: organization_id ? `organization_id = '${organization_id}'` : undefined, @@ -242,31 +257,13 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro const value = createMessage.value; queryClient.setQueryData(queryKey, (data) => { if (!data) return; - const created = {} as unknown as Attachment; - // TODO: Refactor - for (const key of objectKeys(value)) { - if (key === 'content_type') { - created.contentType = value[key]; - } else if (key === 'organization_id') { - created.organizationId = value[key]; - } else if (key === 'created_at') { - created.createdAt = value[key]; - } else if (key === 'created_by') { - created.createdBy = value[key]; - } else if (key === 'modified_at') { - created.modifiedAt = value[key]; - } else if (key === 'modified_by') { - created.modifiedBy = value[key]; - } else { - created[key] = value[key] as never; - } - } + const createdAttachment = parseRawAttachment(value); return { ...data, pages: [ { ...data.pages[0], - items: [created, ...data.pages[0].items], + items: [createdAttachment, ...data.pages[0].items], }, ...data.pages.slice(1), ], @@ -286,28 +283,11 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro ...page, items: page.items.map((attachment) => { if (attachment.id === value.id) { - const updated = { + const updatedAttachment = { ...attachment, - } as unknown as Attachment; - // TODO: Refactor - for (const key of objectKeys(value)) { - if (key === 'content_type') { - updated.contentType = value[key]; - } else if (key === 'organization_id') { - updated.organizationId = value[key]; - } else if (key === 'created_at') { - updated.createdAt = value[key]; - } else if (key === 'created_by') { - updated.createdBy = value[key]; - } else if (key === 'modified_at') { - updated.modifiedAt = value[key]; - } else if (key === 'modified_by') { - updated.modifiedBy = value[key]; - } else { - updated[key] = value[key] as never; - } - } - return updated; + ...parseRawAttachment(value), + }; + return updatedAttachment; } return attachment; From b4b9422df277cf82ae6478760b1ea33d1ddf29eb Mon Sep 17 00:00:00 2001 From: flipvanhaaren Date: Thu, 7 Nov 2024 10:27:17 +0100 Subject: [PATCH 15/36] small text fix --- frontend/src/modules/organizations/attachments-table/index.tsx | 2 +- locales/en/common.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/modules/organizations/attachments-table/index.tsx b/frontend/src/modules/organizations/attachments-table/index.tsx index 9de40702e..53d23ab18 100644 --- a/frontend/src/modules/organizations/attachments-table/index.tsx +++ b/frontend/src/modules/organizations/attachments-table/index.tsx @@ -221,7 +221,7 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro { id: 'upload-attachment', drawerOnMobile: false, - title: t('common:upload_attachment'), + title: t('common:upload_attachments'), className: 'md:max-w-xl', }, ); diff --git a/locales/en/common.json b/locales/en/common.json index a26ce6303..52452d6f8 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -445,5 +445,6 @@ "welcome_back": "Welcome back", "without_color": "Without color", "year": "year", - "your_role": "Your role" + "your_role": "Your role", + "upload_attachments": "Upload attachments" } From 16d382d5dd32afb71ad9166c375d6efb60f86975 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 7 Nov 2024 13:28:34 +0200 Subject: [PATCH 16/36] refactor: Refactor attachments sync --- .../attachments-table/helpers/use-sync.ts | 126 ++++++++++++++++++ .../organizations/attachments-table/index.tsx | 125 +---------------- 2 files changed, 131 insertions(+), 120 deletions(-) create mode 100644 frontend/src/modules/organizations/attachments-table/helpers/use-sync.ts diff --git a/frontend/src/modules/organizations/attachments-table/helpers/use-sync.ts b/frontend/src/modules/organizations/attachments-table/helpers/use-sync.ts new file mode 100644 index 000000000..450c20e0e --- /dev/null +++ b/frontend/src/modules/organizations/attachments-table/helpers/use-sync.ts @@ -0,0 +1,126 @@ +import { type ChangeMessage, ShapeStream, type ShapeStreamOptions } from '@electric-sql/client'; +import { config } from 'config'; +import { useEffect } from 'react'; +import { queryClient } from '~/lib/router'; +import type { AttachmentInfiniteQueryFnData } from '~/modules/common/query-client-provider/attachments'; +import { useGeneralStore } from '~/store/general'; +import type { Attachment } from '~/types/common'; +import { objectKeys } from '~/utils/object'; +import { attachmentsTableColumns } from '#/db/schema/attachments'; +import { env } from '../../../../../env'; +import { attachmentsQueryOptions } from './query-options'; + +type RawAttachment = { + id: string; + filename: string; + content_type: string; + size: string; + organization_id: string; + created_at: string; + created_by: string; + modified_at: string; + modified_by: string; +}; + +const parseRawAttachment = (rawAttachment: RawAttachment): Attachment => { + const columnEntries = Object.entries(attachmentsTableColumns); + const attachment = {} as unknown as Attachment; + for (const key of objectKeys(rawAttachment)) { + const columnEntry = columnEntries.find(([, c]) => c.name === key); + if (!columnEntry) { + continue; + } + const columnName = columnEntry[0] as keyof Attachment; + attachment[columnName] = rawAttachment[key] as never; + } + return attachment; +}; + +const attachmentShape = (organization_id?: string): ShapeStreamOptions => ({ + url: new URL('/v1/shape/attachments', config.electricUrl).href, + where: organization_id ? `organization_id = '${organization_id}'` : undefined, + backoffOptions: { + initialDelay: 500, + maxDelay: 32000, + multiplier: 2, + }, +}); + +export const useSync = (organizationId: string) => { + const { networkMode } = useGeneralStore(); + + // Subscribe to attachments updates + useEffect(() => { + if (networkMode !== 'online' || !config.has.sync || !env.VITE_HAS_SYNC) return; + + const shapeStream = new ShapeStream(attachmentShape(organizationId)); + const queryKey = attachmentsQueryOptions({ orgIdOrSlug: organizationId }).queryKey; + const unsubscribe = shapeStream.subscribe((messages) => { + const createMessage = messages.find((m) => m.headers.operation === 'insert') as ChangeMessage | undefined; + if (createMessage) { + const value = createMessage.value; + queryClient.setQueryData(queryKey, (data) => { + if (!data) return; + const createdAttachment = parseRawAttachment(value); + return { + ...data, + pages: [ + { + ...data.pages[0], + items: [createdAttachment, ...data.pages[0].items], + }, + ...data.pages.slice(1), + ], + }; + }); + } + + const updateMessage = messages.find((m) => m.headers.operation === 'update') as ChangeMessage | undefined; + if (updateMessage) { + const value = updateMessage.value; + queryClient.setQueryData(queryKey, (data) => { + if (!data) return; + return { + ...data, + pages: data.pages.map((page) => { + return { + ...page, + items: page.items.map((attachment) => { + if (attachment.id === value.id) { + const updatedAttachment = { + ...attachment, + ...parseRawAttachment(value), + }; + return updatedAttachment; + } + + return attachment; + }), + }; + }), + }; + }); + } + + const deleteMessage = messages.find((m) => m.headers.operation === 'delete') as ChangeMessage | undefined; + if (deleteMessage) { + queryClient.setQueryData(queryKey, (data) => { + if (!data) return; + return { + ...data, + pages: [ + { + ...data.pages[0], + items: data.pages[0].items.filter((item) => item.id !== deleteMessage.value.id), + }, + ...data.pages.slice(1), + ], + }; + }); + } + }); + return () => { + unsubscribe(); + }; + }, [networkMode]); +}; diff --git a/frontend/src/modules/organizations/attachments-table/index.tsx b/frontend/src/modules/organizations/attachments-table/index.tsx index 53d23ab18..1dd88a0bd 100644 --- a/frontend/src/modules/organizations/attachments-table/index.tsx +++ b/frontend/src/modules/organizations/attachments-table/index.tsx @@ -1,8 +1,7 @@ import { onlineManager, useMutation, useSuspenseInfiniteQuery } from '@tanstack/react-query'; import { useSearch } from '@tanstack/react-router'; -import { Suspense, useEffect, useMemo, useState } from 'react'; +import { Suspense, useMemo, useState } from 'react'; -import { type ChangeMessage, ShapeStream, type ShapeStreamOptions } from '@electric-sql/client'; import { config } from 'config'; import { motion } from 'framer-motion'; import { Trash, Upload, XSquare } from 'lucide-react'; @@ -13,7 +12,6 @@ import { updateAttachment } from '~/api/attachments'; import { useBreakpoints } from '~/hooks/use-breakpoints'; import useMapQueryDataToRows from '~/hooks/use-map-query-data-to-rows'; import useSaveInSearchParams from '~/hooks/use-save-in-search-params'; -import { queryClient } from '~/lib/router'; import { showToast } from '~/lib/toasts'; import CarouselDialog from '~/modules/common/carousel-dialog'; import { DataTable } from '~/modules/common/data-table'; @@ -25,19 +23,16 @@ import { FilterBarActions, FilterBarContent, TableFilterBar } from '~/modules/co import TableSearch from '~/modules/common/data-table/table-search'; import { dialog } from '~/modules/common/dialoger/state'; import { FocusView } from '~/modules/common/focus-view'; -import { type AttachmentInfiniteQueryFnData, useAttachmentCreateMutation } from '~/modules/common/query-client-provider/attachments'; +import { useAttachmentCreateMutation } from '~/modules/common/query-client-provider/attachments'; import UploadUppy from '~/modules/common/upload/upload-uppy'; import { Badge } from '~/modules/ui/badge'; import { Button } from '~/modules/ui/button'; -import { useGeneralStore } from '~/store/general'; import { useUserStore } from '~/store/user'; import { type Attachment, type Organization, UploadType } from '~/types/common'; -import { objectKeys } from '~/utils/object'; -import { attachmentsTableColumns } from '#/db/schema/attachments'; import type { attachmentsQuerySchema } from '#/modules/attachments/schema'; -import { env } from '../../../../env'; import { useColumns } from './columns'; import { attachmentsQueryOptions } from './helpers/query-options'; +import { useSync } from './helpers/use-sync'; import RemoveAttachmentsForm from './remove-attachments-form'; const LIMIT = config.requestLimits.attachments; @@ -46,52 +41,17 @@ const maxAttachmentsUpload = 20; type AttachmentSearch = z.infer; -type RawAttachment = { - id: string; - filename: string; - content_type: string; - size: string; - organization_id: string; - created_at: string; - created_by: string; - modified_at: string; - modified_by: string; -}; - interface AttachmentsTableProps { organization: Organization; isSheet?: boolean; } -const parseRawAttachment = (rawAttachment: RawAttachment): Attachment => { - const columnEntries = Object.entries(attachmentsTableColumns); - const attachment = {} as unknown as Attachment; - for (const key of objectKeys(rawAttachment)) { - const columnEntry = columnEntries.find(([, c]) => c.name === key); - if (!columnEntry) { - continue; - } - const columnName = columnEntry[0] as keyof Attachment; - attachment[columnName] = rawAttachment[key] as never; - } - return attachment; -}; - -const attachmentShape = (organization_id?: string): ShapeStreamOptions => ({ - url: new URL('/v1/shape/attachments', config.electricUrl).href, - where: organization_id ? `organization_id = '${organization_id}'` : undefined, - backoffOptions: { - initialDelay: 500, - maxDelay: 32000, - multiplier: 2, - }, -}); - const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTableProps) => { const { t } = useTranslation(); const search = useSearch({ strict: false }); const user = useUserStore((state) => state.user); - const { networkMode } = useGeneralStore(); + + useSync(organization.id); const isAdmin = organization.membership?.role === 'admin' || user?.role === 'admin'; const isMobile = useBreakpoints('max', 'sm'); @@ -245,81 +205,6 @@ const AttachmentsTable = ({ organization, isSheet = false }: AttachmentsTablePro ); }; - // Subscribe to attachments updates - useEffect(() => { - if (networkMode !== 'online' || !config.has.sync || !env.VITE_HAS_SYNC) return; - - const shapeStream = new ShapeStream(attachmentShape(organization.id)); - const queryKey = attachmentsQueryOptions({ orgIdOrSlug: organization.id }).queryKey; - const unsubscribe = shapeStream.subscribe((messages) => { - const createMessage = messages.find((m) => m.headers.operation === 'insert') as ChangeMessage | undefined; - if (createMessage) { - const value = createMessage.value; - queryClient.setQueryData(queryKey, (data) => { - if (!data) return; - const createdAttachment = parseRawAttachment(value); - return { - ...data, - pages: [ - { - ...data.pages[0], - items: [createdAttachment, ...data.pages[0].items], - }, - ...data.pages.slice(1), - ], - }; - }); - } - - const updateMessage = messages.find((m) => m.headers.operation === 'update') as ChangeMessage | undefined; - if (updateMessage) { - const value = updateMessage.value; - queryClient.setQueryData(queryKey, (data) => { - if (!data) return; - return { - ...data, - pages: data.pages.map((page) => { - return { - ...page, - items: page.items.map((attachment) => { - if (attachment.id === value.id) { - const updatedAttachment = { - ...attachment, - ...parseRawAttachment(value), - }; - return updatedAttachment; - } - - return attachment; - }), - }; - }), - }; - }); - } - - const deleteMessage = messages.find((m) => m.headers.operation === 'delete') as ChangeMessage | undefined; - if (deleteMessage) { - queryClient.setQueryData(queryKey, (data) => { - if (!data) return; - return { - ...data, - pages: [ - { - ...data.pages[0], - items: data.pages[0].items.filter((item) => item.id !== deleteMessage.value.id), - }, - ...data.pages.slice(1), - ], - }; - }); - } - }); - return () => { - unsubscribe(); - }; - }, [networkMode]); - return ( <> Date: Thu, 7 Nov 2024 15:01:22 +0200 Subject: [PATCH 17/36] improve: newsletter blocknote --- backend/emails/helpers/index.tsx | 49 ++++ backend/emails/organization-newsletter.tsx | 3 +- backend/package.json | 2 + .../custom-slash-menu/custom-slash-menu.tsx | 19 +- .../blocknote/custom-slash-menu/index.tsx | 6 +- .../src/modules/common/blocknote/helpers.ts | 75 ++++++ .../src/modules/common/blocknote/index.tsx | 20 +- .../src/modules/common/blocknote/types.ts | 2 + .../src/modules/common/carousel-dialog.tsx | 4 +- frontend/src/modules/common/sheet-nav.tsx | 2 +- .../common/upload/blocknote-upload-panel.tsx | 84 ++++++ .../organizations-table/index.tsx | 38 ++- .../src/modules/system/newsletter-draft.tsx | 68 +++++ .../system/organizations-newsletter-form.tsx | 5 +- locales/en/common.json | 2 + pnpm-lock.yaml | 251 +++++++++++++++++- 16 files changed, 604 insertions(+), 26 deletions(-) create mode 100644 backend/emails/helpers/index.tsx create mode 100644 frontend/src/modules/common/upload/blocknote-upload-panel.tsx create mode 100644 frontend/src/modules/system/newsletter-draft.tsx diff --git a/backend/emails/helpers/index.tsx b/backend/emails/helpers/index.tsx new file mode 100644 index 000000000..63ef813f8 --- /dev/null +++ b/backend/emails/helpers/index.tsx @@ -0,0 +1,49 @@ +import { JSDOM } from 'jsdom'; + +export const updateSourcesFromDataUrl = (passedHTML: string): string => { + // Parse the HTML string with JSDOM + const dom = new JSDOM(passedHTML); + const document = dom.window.document; + + // Select all elements with a 'data-url' attribute within the document + const elementsWithDataUrl = document.querySelectorAll('[data-url]'); + + // Loop through each element with a 'data-url' attribute + for (const el of elementsWithDataUrl) { + const url = el.getAttribute('data-url'); + const contentType = el.getAttribute('data-content-type'); + + if (!url) continue; + + if (contentType === 'image') { + const imageElement = el.querySelector('img'); + if (imageElement) imageElement.setAttribute('src', url); + } + + // Add if email send library support video & audio + // if (contentType === 'video') { + // const videoElement = el.querySelector('video'); + // if (videoElement) videoElement.setAttribute('src', url); + // } + + // if (contentType === 'audio') { + // const audioElement = el.querySelector('audio'); + // if (audioElement) audioElement.setAttribute('src', url); + // } + + if (contentType === 'file') { + const fileLinkElement = document.createElement('a'); + fileLinkElement.setAttribute('href', url); + + // Set the 'download' attribute using the 'data-name' attribute or other source + const fileName = el.getAttribute('data-name') || 'file'; + fileLinkElement.setAttribute('download', fileName); + + // Move the original element (el) inside the tag + el.parentNode?.replaceChild(fileLinkElement, el); + fileLinkElement.appendChild(el); + } + } + + return document.documentElement.outerHTML; +}; diff --git a/backend/emails/organization-newsletter.tsx b/backend/emails/organization-newsletter.tsx index 2414a4721..f6a3e51fe 100644 --- a/backend/emails/organization-newsletter.tsx +++ b/backend/emails/organization-newsletter.tsx @@ -5,6 +5,7 @@ import { EmailContainer } from './components/container'; import { EmailHeader } from './components/email-header'; import { EmailReplyTo } from './components/email-reply-to'; import { Footer } from './components/footer'; +import { updateSourcesFromDataUrl } from './helpers'; import type { BasicTemplateType } from './types'; interface Props extends BasicTemplateType { @@ -48,7 +49,7 @@ export const organizationsNewsletter = ({ userLanguage: lng, authorEmail, conten > {subject} {/* biome-ignore lint/security/noDangerouslySetInnerHtml: we need send it cos blackNote return an html*/} -
+
, editor: CustomBlockNoteSchema) => { +const fileTypes = ['File', 'Image', 'Video', 'Audio']; + +export const slashMenu = ( + props: SuggestionMenuProps, + editor: CustomBlockNoteSchema, + allowedFilePanelTypes: FileTypesNames[], +) => { const { items, selectedIndex, onItemClick } = props; const [inputValue, setInputValue] = useState(''); + // Find and remove the first matching item + const filteredSortList = sortList.filter((item) => { + if (!fileTypes.includes(item)) return true; + if ((allowedFilePanelTypes as string[]).includes(item.toLowerCase())) return true; + return false; + }); + // Create a mapping from title to index for quick lookup - const sortOrder = new Map((sortList as string[]).map((title, index) => [title, index])); + const sortOrder = new Map((filteredSortList as string[]).map((title, index) => [title, index])); const sortedItems = items.sort((a, b) => { const indexA = sortOrder.get(a.title); const indexB = sortOrder.get(b.title); diff --git a/frontend/src/modules/common/blocknote/custom-slash-menu/index.tsx b/frontend/src/modules/common/blocknote/custom-slash-menu/index.tsx index 41317b910..7cc3cbe81 100644 --- a/frontend/src/modules/common/blocknote/custom-slash-menu/index.tsx +++ b/frontend/src/modules/common/blocknote/custom-slash-menu/index.tsx @@ -1,12 +1,12 @@ import { SuggestionMenuController } from '@blocknote/react'; import { getNotifyItems } from '~/modules/common/blocknote/custom-elements/notify'; import { slashMenu } from '~/modules/common/blocknote/custom-slash-menu/custom-slash-menu'; -import type { CustomBlockNoteSchema } from '~/modules/common/blocknote/types'; +import type { CustomBlockNoteSchema, FileTypesNames } from '~/modules/common/blocknote/types'; -export const CustomSlashMenu = ({ editor }: { editor: CustomBlockNoteSchema }) => ( +export const CustomSlashMenu = ({ editor, allowedFilePanelTypes }: { editor: CustomBlockNoteSchema; allowedFilePanelTypes: FileTypesNames[] }) => ( getNotifyItems(query, editor)} - suggestionMenuComponent={(props) => slashMenu(props, editor)} + suggestionMenuComponent={(props) => slashMenu(props, editor, allowedFilePanelTypes)} /> ); diff --git a/frontend/src/modules/common/blocknote/helpers.ts b/frontend/src/modules/common/blocknote/helpers.ts index 32ed4669e..93cd8fbcd 100644 --- a/frontend/src/modules/common/blocknote/helpers.ts +++ b/frontend/src/modules/common/blocknote/helpers.ts @@ -1,4 +1,5 @@ import type { Block } from '@blocknote/core'; +import type { Slides } from '../carousel-dialog'; import type { CustomBlockNoteSchema } from './types'; export const getContentAsString = (blocks: Block[]) => { @@ -29,3 +30,77 @@ export const handleSubmitOnEnter = (editor: CustomBlockNoteSchema): CustomBlockN } return null; }; + +export const updateSourcesFromDataUrl = (openDialog?: (slide: number) => void) => { + // Select all elements that have a 'data-url' attribute + const elementsWithDataUrl = document.querySelectorAll('[data-url]'); + // Exit early if no matching elements are found + if (elementsWithDataUrl.length === 0) return; + const urls: Slides[] = []; + + const onElClick = (e: MouseEvent) => { + if (!openDialog || !e.target) return; + e.preventDefault(); + const target = e.target as HTMLImageElement | HTMLVideoElement | HTMLAudioElement; + // Find the slide based on the currentSrc of the target + const slideNum = urls.findIndex(({ src }) => src === target.currentSrc); + + openDialog(slideNum >= 0 ? slideNum : 0); + }; + + for (const element of elementsWithDataUrl) { + const url = element.getAttribute('data-url'); + const contentType = element.getAttribute('data-content-type') || 'file'; + + if (!url) continue; + + urls.push({ src: url, fileType: contentType }); + + switch (contentType) { + case 'image': { + const imageElement = element.querySelector('img'); + if (imageElement) { + imageElement.onclick = onElClick; + imageElement.setAttribute('src', url); + } + break; + } + + case 'video': { + const videoElement = element.querySelector('video'); + if (videoElement) { + videoElement.onclick = onElClick; + videoElement.setAttribute('src', url); + } + break; + } + + case 'audio': { + const audioElement = element.querySelector('audio'); + if (audioElement) { + audioElement.onclick = onElClick; + audioElement.setAttribute('src', url); + } + break; + } + + case 'file': { + const fileLinkElement = document.createElement('a'); + fileLinkElement.setAttribute('href', url); + + const fileName = element.getAttribute('data-name') || 'file'; + fileLinkElement.setAttribute('download', fileName); + + fileLinkElement.onclick = onElClick; + // Move the original element (el) inside the tag + element.parentNode?.replaceChild(fileLinkElement, element); + fileLinkElement.appendChild(element); + break; + } + + default: + break; + } + } + return urls; +}; diff --git a/frontend/src/modules/common/blocknote/index.tsx b/frontend/src/modules/common/blocknote/index.tsx index fb46d1b81..f15e54772 100644 --- a/frontend/src/modules/common/blocknote/index.tsx +++ b/frontend/src/modules/common/blocknote/index.tsx @@ -33,6 +33,8 @@ import { FloatingPortal } from '@floating-ui/react'; import { dispatchCustomEvent } from '~/lib/custom-events'; import router from '~/lib/router'; import { focusEditor, getContentAsString, handleSubmitOnEnter } from '~/modules/common/blocknote/helpers'; +import type { FileTypesNames } from '~/modules/common/blocknote/types'; + import './styles.css'; type BlockNoteProps = { @@ -47,13 +49,23 @@ type BlockNoteProps = { emojis?: boolean; members?: Member[]; updateData: (html: string) => void; - filePanel?: (props: FilePanelProps) => JSX.Element; onChange?: (value: string) => void; onFocus?: () => void; onEscapeClick?: () => void; onEnterClick?: () => void; onTextDifference?: () => void; -}; +} & ( + | { + // filePanel and allowedFilePanelTypes req to add together + filePanel: (props: FilePanelProps) => JSX.Element; + allowedFilePanelTypes: FileTypesNames[]; + } + | { + // if neither is provided, it allows the omission of both + filePanel?: never; + allowedFilePanelTypes?: never; + } +); export const BlockNote = ({ id, @@ -65,6 +77,8 @@ export const BlockNote = ({ emojis = true, trailingBlock = true, updateDataOnBeforeLoad = false, + // on default file panel allowed all types + allowedFilePanelTypes = ['image', 'video', 'audio', 'file'], members, updateData, filePanel, @@ -223,7 +237,7 @@ export const BlockNote = ({ {slashMenu && (
- +
)} diff --git a/frontend/src/modules/common/blocknote/types.ts b/frontend/src/modules/common/blocknote/types.ts index 4b8f5285a..3c57cb09a 100644 --- a/frontend/src/modules/common/blocknote/types.ts +++ b/frontend/src/modules/common/blocknote/types.ts @@ -1,5 +1,7 @@ import type { customSchema } from '~/modules/common/blocknote/blocknote-config'; +export type FileTypesNames = 'image' | 'video' | 'audio' | 'file'; + export type BlockAlignTypes = 'right' | 'center' | 'left'; export type BlockTypes = 'heading' | 'paragraph' | 'bulletListItem' | 'numberedListItem' | 'checkListItem'; diff --git a/frontend/src/modules/common/carousel-dialog.tsx b/frontend/src/modules/common/carousel-dialog.tsx index 4429d47c4..b556cbd0c 100644 --- a/frontend/src/modules/common/carousel-dialog.tsx +++ b/frontend/src/modules/common/carousel-dialog.tsx @@ -3,11 +3,13 @@ import { DialogTitle } from '@radix-ui/react-dialog'; import { Dialog, DialogContent, DialogDescription, DialogHeader } from '~/modules/ui/dialog'; import Carousel from './carousel'; +export type Slides = { src: string; fileType?: string }; + interface CarouselDialogProps { isOpen: boolean; title: string; carouselSlide: number; - slides?: { src: string; fileType?: string }[]; + slides?: Slides[]; onOpenChange: (open: boolean) => void; } diff --git a/frontend/src/modules/common/sheet-nav.tsx b/frontend/src/modules/common/sheet-nav.tsx index 55f30dbbc..d8bb4d66c 100644 --- a/frontend/src/modules/common/sheet-nav.tsx +++ b/frontend/src/modules/common/sheet-nav.tsx @@ -17,7 +17,7 @@ export const SheetNav = ({ tabs }: Props) => { return (
{tabs.length > 1 && ( -