From a6e86eb976b033a993bcbc985bb76e5d5c5682d8 Mon Sep 17 00:00:00 2001 From: Ender Date: Fri, 24 Oct 2025 15:38:32 +0200 Subject: [PATCH] feat: enhance admin UI with data grid, search and sorting features --- apps/admin/package.json | 1 + apps/admin/src/components/MediaLibrary.tsx | 90 +++- apps/admin/src/components/PostsList.tsx | 110 +++-- apps/admin/src/features/recorder/Recorder.tsx | 16 +- apps/admin/src/layout/AdminLayout.tsx | 6 +- apps/api/src/posts.ts | 44 +- pnpm-lock.yaml | 383 ++++++++++++++++++ 7 files changed, 590 insertions(+), 60 deletions(-) diff --git a/apps/admin/package.json b/apps/admin/package.json index 7ded928..148d567 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -14,6 +14,7 @@ "@emotion/styled": "^11.14.1", "@mui/icons-material": "^7.3.4", "@mui/material": "^7.3.4", + "@mui/x-data-grid": "^8.15.0", "@tiptap/extension-image": "^3.7.2", "@tiptap/extension-link": "^3.7.2", "@tiptap/extension-placeholder": "^3.7.2", diff --git a/apps/admin/src/components/MediaLibrary.tsx b/apps/admin/src/components/MediaLibrary.tsx index b80efdc..aca7670 100644 --- a/apps/admin/src/components/MediaLibrary.tsx +++ b/apps/admin/src/components/MediaLibrary.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState } from 'react'; -import { Box, Button, Stack, Typography } from '@mui/material'; +import { useEffect, useMemo, useState } from 'react'; +import { Box, Button, Stack, Typography, Paper, TextField, MenuItem } from '@mui/material'; type MediaItem = { key: string; @@ -11,6 +11,8 @@ export default function MediaLibrary({ onInsert, onSetFeature, showSetFeature }: const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); + const [query, setQuery] = useState(''); + const [sortBy, setSortBy] = useState<'date_desc' | 'date_asc' | 'name_asc' | 'size_desc'>('date_desc'); const load = async () => { try { @@ -46,37 +48,85 @@ export default function MediaLibrary({ onInsert, onSetFeature, showSetFeature }: } }; + const filtered = useMemo(() => { + const q = query.trim().toLowerCase(); + let arr = items.filter(it => it.key.toLowerCase().includes(q)); + arr.sort((a, b) => { + if (sortBy === 'date_desc') return (new Date(b.lastModified || 0).getTime()) - (new Date(a.lastModified || 0).getTime()); + if (sortBy === 'date_asc') return (new Date(a.lastModified || 0).getTime()) - (new Date(b.lastModified || 0).getTime()); + if (sortBy === 'size_desc') return (b.size - a.size); + // name_asc + const an = a.key.split('/').slice(-1)[0].toLowerCase(); + const bn = b.key.split('/').slice(-1)[0].toLowerCase(); + return an.localeCompare(bn); + }); + return arr; + }, [items, query, sortBy]); + + const fmtSize = (n: number) => { + if (n >= 1024 * 1024) return (n / (1024 * 1024)).toFixed(1) + ' MB'; + if (n >= 1024) return (n / 1024).toFixed(1) + ' KB'; + return n + ' B'; + }; + + const fmtDate = (s: string | null) => s ? new Date(s).toLocaleString() : ''; + + const copyUrl = async (url: string) => { + try { + await navigator.clipboard.writeText(url); + } catch {} + }; + return ( - - - Media Library - + + + Media Library + + setQuery(e.target.value)} + /> + setSortBy(e.target.value as any)}> + Newest + Oldest + Name + Largest + + + {error && {error}} - - {items.map((it) => { + + {filtered.map((it) => { const url = `/api/media/obj?key=${encodeURIComponent(it.key)}`; const name = it.key.split('/').slice(-1)[0]; return ( - - - {name} + + + + {name} + - {name} - - + {name} + {fmtSize(it.size)} · {fmtDate(it.lastModified)} + + + {showSetFeature && onSetFeature && ( - + )} - + + - + ); })} - {items.length === 0 && !loading && ( - No images yet. + {filtered.length === 0 && !loading && ( + No images found. )} - + ); } diff --git a/apps/admin/src/components/PostsList.tsx b/apps/admin/src/components/PostsList.tsx index 900182d..1194537 100644 --- a/apps/admin/src/components/PostsList.tsx +++ b/apps/admin/src/components/PostsList.tsx @@ -1,5 +1,7 @@ -import { useEffect, useState } from 'react'; -import { Box, Button, Chip, Stack, Typography } from '@mui/material'; +import { useEffect, useMemo, useState } from 'react'; +import { Box, Button, Chip, Stack, Typography, TextField } from '@mui/material'; +import { DataGrid } from '@mui/x-data-grid'; +import type { GridColDef, GridPaginationModel, GridSortModel } from '@mui/x-data-grid'; export type PostSummary = { id: string; @@ -9,52 +11,108 @@ export type PostSummary = { }; export default function PostsList({ onSelect, onNew }: { onSelect: (id: string) => void; onNew?: () => void }) { - const [items, setItems] = useState([]); + const [rows, setRows] = useState([]); + const [rowCount, setRowCount] = useState(0); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); + const [query, setQuery] = useState(''); + const [effectiveQuery, setEffectiveQuery] = useState(''); + const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 20 }); + const [sortModel, setSortModel] = useState([{ field: 'updatedAt', sort: 'desc' }]); + + useEffect(() => { + const handle = setTimeout(() => setEffectiveQuery(query), 300); + return () => clearTimeout(handle); + }, [query]); useEffect(() => { (async () => { try { setLoading(true); setError(''); - const res = await fetch('/api/posts'); + const page = paginationModel.page + 1; // server is 1-based + const pageSize = paginationModel.pageSize; + const sort = sortModel[0] ? `${sortModel[0].field}:${sortModel[0].sort}` : 'updatedAt:desc'; + const params = new URLSearchParams({ page: String(page), pageSize: String(pageSize), sort }); + if (effectiveQuery.trim()) params.set('q', effectiveQuery.trim()); + const res = await fetch(`/api/posts?${params.toString()}`); if (!res.ok) throw new Error('Failed to load posts'); const data = await res.json(); - const rows = (data.items || []) as Array<{ id: string; title?: string; status: string; updatedAt: string }>; - setItems(rows); + setRows(data.items || []); + setRowCount(data.total || 0); } catch (e: any) { setError(e?.message || 'Failed to load posts'); } finally { setLoading(false); } })(); - }, []); + }, [paginationModel, sortModel, effectiveQuery]); + + const columns = useMemo(() => [ + { + field: 'title', headerName: 'Title', flex: 1, minWidth: 160, + renderCell: (p) => { + const title = (((p.row as any).title as string | null) || '').trim(); + return {title || 'Untitled'}; + } + }, + { + field: 'status', headerName: 'Status', width: 140, + renderCell: (p) => { + const value = ((p.row as any).status as string) ?? ''; + const status = value.toLowerCase(); + const color: any = status === 'published' ? 'success' : status === 'ready_for_publish' ? 'secondary' : status === 'archived' ? 'warning' : status === 'editing' ? 'primary' : 'default'; + return + } + }, + { + field: 'updatedAt', headerName: 'Updated', width: 180, + renderCell: (p) => { + const v = (p.row as any).updatedAt as string; + return {new Date(v).toLocaleString()}; + }, + sortable: true, + }, + { + field: 'id', headerName: 'ID', width: 120, + renderCell: (p) => {((p.row as any).id as string).slice(0, 8)}, + sortable: false, + }, + { + field: 'actions', headerName: 'Actions', width: 120, sortable: false, filterable: false, + renderCell: (p) => ( + + ) + } + ], [onSelect]); return ( Posts - - - {loading && Loading…} - {error && {error}} - {!loading && items.length === 0 && ( - No posts yet. Click New Post to create one. - )} - - {items.map((p) => ( - - - {(p.title && p.title.trim()) || 'Untitled'} - - {new Date(p.updatedAt).toLocaleString()} - ID: {p.id.slice(0, 8)} - - - - ))} + + setQuery(e.target.value)} /> + + + {error && {error}} +
+ + rows={rows} + columns={columns} + loading={loading} + rowCount={rowCount} + paginationMode="server" + sortingMode="server" + paginationModel={paginationModel} + onPaginationModelChange={setPaginationModel} + sortModel={sortModel} + onSortModelChange={setSortModel} + disableRowSelectionOnClick + pageSizeOptions={[10, 20, 50, 100]} + autoHeight + /> +
); } diff --git a/apps/admin/src/features/recorder/Recorder.tsx b/apps/admin/src/features/recorder/Recorder.tsx index 2f8ccd4..c3fff60 100644 --- a/apps/admin/src/features/recorder/Recorder.tsx +++ b/apps/admin/src/features/recorder/Recorder.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from 'react'; -import { Box, Button, Stack, Typography } from '@mui/material'; +import { Box, Button, Stack, Typography, Paper } from '@mui/material'; export default function Recorder({ postId, initialClips, onInsertAtCursor, onTranscript }: { postId?: string; initialClips?: Array<{ id: string; bucket: string; key: string; mime: string; transcript?: string }>; onInsertAtCursor?: (html: string) => void; onTranscript?: (t: string) => void }) { const mediaRecorderRef = useRef(null); @@ -183,9 +183,9 @@ export default function Recorder({ postId, initialClips, onInsertAtCursor, onTra }, [clips]); return ( - + Audio Recorder - + @@ -195,15 +195,15 @@ export default function Recorder({ postId, initialClips, onInsertAtCursor, onTra {clips.length === 0 && ( No recordings yet. )} - + {clips.map((c, idx) => ( - + Clip {idx + 1} - + )} - + ))}
-
+ ); } diff --git a/apps/admin/src/layout/AdminLayout.tsx b/apps/admin/src/layout/AdminLayout.tsx index 7985516..ccf8445 100644 --- a/apps/admin/src/layout/AdminLayout.tsx +++ b/apps/admin/src/layout/AdminLayout.tsx @@ -4,7 +4,11 @@ import type { ReactNode } from 'react'; export default function AdminLayout({ title, onLogout, children }: { title?: string; onLogout?: () => void; children: ReactNode }) { return ( - + {title || 'VoxBlog Admin'} diff --git a/apps/api/src/posts.ts b/apps/api/src/posts.ts index 648d449..5459a1d 100644 --- a/apps/api/src/posts.ts +++ b/apps/api/src/posts.ts @@ -2,19 +2,53 @@ import express from 'express'; import crypto from 'crypto'; import { db } from './db'; import { posts, audioClips } from './db/schema'; -import { desc, eq } from 'drizzle-orm'; +import { desc, asc, eq, and, or, like, sql } from 'drizzle-orm'; const router = express.Router(); // List posts (minimal info) -router.get('/', async (_req, res) => { +router.get('/', async (req, res) => { try { + const page = Math.max(parseInt((req.query.page as string) || '1', 10), 1); + const pageSizeRaw = Math.max(parseInt((req.query.pageSize as string) || '20', 10), 1); + const pageSize = Math.min(pageSizeRaw, 100); + const q = ((req.query.q as string) || '').trim(); + const sort = ((req.query.sort as string) || 'updatedAt:desc').toLowerCase(); + const [sortField, sortDir] = sort.split(':'); + + const whereConds = [] as any[]; + if (q) { + const likeQ = `%${q}%`; + whereConds.push(or(like(posts.title, likeQ), like(posts.contentHtml, likeQ))); + } + + const whereExpr = whereConds.length > 0 ? and(...whereConds) : undefined; + + // total count + const countRows = await db + .select({ cnt: sql`COUNT(*)` }) + .from(posts) + .where(whereExpr as any); + const total = (countRows?.[0]?.cnt as unknown as number) || 0; + + // sorting + let orderByExpr: any = desc(posts.updatedAt); + const dir = sortDir === 'asc' ? 'asc' : 'desc'; + if (sortField === 'title') orderByExpr = dir === 'asc' ? asc(posts.title) : desc(posts.title); + else if (sortField === 'status') orderByExpr = dir === 'asc' ? asc(posts.status) : desc(posts.status); + else orderByExpr = dir === 'asc' ? asc(posts.updatedAt) : desc(posts.updatedAt); + + const offset = (page - 1) * pageSize; + const rows = await db .select({ id: posts.id, title: posts.title, status: posts.status, updatedAt: posts.updatedAt }) .from(posts) - .orderBy(desc(posts.updatedAt)) - .limit(200); - return res.json({ items: rows }); + .where(whereExpr as any) + .orderBy(orderByExpr) + .limit(pageSize) + .offset(offset); + + return res.json({ items: rows, total, page, pageSize }); } catch (err) { console.error('List posts error:', err); return res.status(500).json({ error: 'Failed to list posts' }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0833e35..d698ae6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,6 +22,9 @@ importers: '@mui/material': specifier: ^7.3.4 version: 7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@mui/x-data-grid': + specifier: ^8.15.0 + version: 8.15.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@mui/system@7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@tiptap/extension-image': specifier: ^3.7.2 version: 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2)) @@ -86,6 +89,12 @@ importers: '@aws-sdk/client-s3': specifier: ^3.916.0 version: 3.916.0 + '@aws-sdk/s3-request-presigner': + specifier: ^3.916.0 + version: 3.916.0 + '@types/jsonwebtoken': + specifier: ^9.0.10 + version: 9.0.10 accepts: specifier: ^2.0.0 version: 2.0.0 @@ -116,6 +125,9 @@ importers: dotenv: specifier: ^17.2.3 version: 17.2.3 + drizzle-orm: + specifier: ^0.44.7 + version: 0.44.7(mysql2@3.15.3) encodeurl: specifier: ^2.0.0 version: 2.0.0 @@ -137,6 +149,9 @@ importers: http-errors: specifier: ^2.0.0 version: 2.0.0 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 merge-descriptors: specifier: ^2.0.0 version: 2.0.0 @@ -146,6 +161,9 @@ importers: multer: specifier: ^2.0.2 version: 2.0.2 + mysql2: + specifier: ^3.15.3 + version: 3.15.3 on-finished: specifier: ^2.4.1 version: 2.4.1 @@ -371,6 +389,10 @@ packages: resolution: {integrity: sha512-KlmHhRbn1qdwXUdsdrJ7S/MAkkC1jLpQ11n+XvxUUUCGAJd1gjC7AjxPZUM7ieQ2zcb8bfEzIU7al+Q3ZT0u7Q==} engines: {node: '>=18.0.0'} + '@aws-sdk/s3-request-presigner@3.916.0': + resolution: {integrity: sha512-XkHIhRISbdQHZ08Tq5Zt6A4n49mjVvcZd/PeY1SPCv47rzLnxdfxVSuEFynAg3Lgw/f/iHdv3lok2PAesCvi4A==} + engines: {node: '>=18.0.0'} + '@aws-sdk/signature-v4-multi-region@3.916.0': resolution: {integrity: sha512-fuzUMo6xU7e0NBzBA6TQ4FUf1gqNbg4woBSvYfxRRsIfKmSMn9/elXXn4sAE5UKvlwVQmYnb6p7dpVRPyFvnQA==} engines: {node: '>=18.0.0'} @@ -391,6 +413,10 @@ packages: resolution: {integrity: sha512-bAgUQwvixdsiGNcuZSDAOWbyHlnPtg8G8TyHD6DTfTmKTHUW6tAn+af/ZYJPXEzXhhpwgJqi58vWnsiDhmr7NQ==} engines: {node: '>=18.0.0'} + '@aws-sdk/util-format-url@3.914.0': + resolution: {integrity: sha512-QpdkoQjvPaYyzZwgk41vFyHQM5s0DsrsbQ8IoPUggQt4HaJUvmL1ShwMcSldbgdzwiRMqXUK8q7jrqUvkYkY6w==} + engines: {node: '>=18.0.0'} + '@aws-sdk/util-locate-window@3.893.0': resolution: {integrity: sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==} engines: {node: '>=18.0.0'} @@ -914,6 +940,35 @@ packages: '@types/react': optional: true + '@mui/x-data-grid@8.15.0': + resolution: {integrity: sha512-JNPG2WSYJVKbUAbDpLCbWmIY25k9hyfUjAVnzDREbJMwPL+/5B9pIK0ikRQEXc0wRKY2T59SeR/Um2FZjBeeWQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.9.0 + '@emotion/styled': ^11.8.1 + '@mui/material': ^5.15.14 || ^6.0.0 || ^7.0.0 + '@mui/system': ^5.15.14 || ^6.0.0 || ^7.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/x-internals@8.14.0': + resolution: {integrity: sha512-esYyl61nuuFXiN631TWuPh2tqdoyTdBI/4UXgwH3rytF8jiWvy6prPBPRHEH1nvW3fgw9FoBI48FlOO+yEI8xg==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@mui/x-virtualizer@0.2.5': + resolution: {integrity: sha512-kCo/i9YfNavbupqZGO1649CHwIABrwUDHVZh+GvGierHhIglUc9MHxYKsPhuojOg6izWa2HP+klt3nq2n/arOw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} @@ -1475,6 +1530,9 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/jsonwebtoken@9.0.10': + resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} + '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} @@ -1490,6 +1548,9 @@ packages: '@types/morgan@1.9.10': resolution: {integrity: sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/multer@2.0.0': resolution: {integrity: sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==} @@ -1676,6 +1737,10 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + aws-ssl-profiles@1.1.2: + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} + babel-plugin-macros@3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} @@ -1720,6 +1785,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -1926,6 +1994,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -1952,6 +2024,98 @@ packages: resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} + drizzle-orm@0.44.7: + resolution: {integrity: sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -1959,6 +2123,9 @@ packages: dynamic-dedupe@0.3.0: resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -2228,6 +2395,9 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -2407,6 +2577,9 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -2491,6 +2664,16 @@ packages: engines: {node: '>=6'} hasBin: true + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + + jwa@1.4.2: + resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + keygrip@1.1.0: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} @@ -2522,13 +2705,37 @@ packages: lodash.flattendeep@4.4.0: resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2536,6 +2743,14 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + lru.min@1.1.2: + resolution: {integrity: sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -2654,6 +2869,14 @@ packages: resolution: {integrity: sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==} engines: {node: '>= 10.16.0'} + mysql2@3.15.3: + resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} + engines: {node: '>= 8.0'} + + named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2946,6 +3169,9 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3013,6 +3239,9 @@ packages: resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} engines: {node: '>= 18'} + seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -3079,6 +3308,10 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -3830,6 +4063,17 @@ snapshots: '@smithy/types': 4.8.0 tslib: 2.8.1 + '@aws-sdk/s3-request-presigner@3.916.0': + dependencies: + '@aws-sdk/signature-v4-multi-region': 3.916.0 + '@aws-sdk/types': 3.914.0 + '@aws-sdk/util-format-url': 3.914.0 + '@smithy/middleware-endpoint': 4.3.5 + '@smithy/protocol-http': 5.3.3 + '@smithy/smithy-client': 4.9.1 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@aws-sdk/signature-v4-multi-region@3.916.0': dependencies: '@aws-sdk/middleware-sdk-s3': 3.916.0 @@ -3868,6 +4112,13 @@ snapshots: '@smithy/util-endpoints': 3.2.3 tslib: 2.8.1 + '@aws-sdk/util-format-url@3.914.0': + dependencies: + '@aws-sdk/types': 3.914.0 + '@smithy/querystring-builder': 4.2.3 + '@smithy/types': 4.8.0 + tslib: 2.8.1 + '@aws-sdk/util-locate-window@3.893.0': dependencies: tslib: 2.8.1 @@ -4397,6 +4648,45 @@ snapshots: optionalDependencies: '@types/react': 19.2.2 + '@mui/x-data-grid@8.15.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@mui/system@7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/material': 7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@mui/system': 7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0) + '@mui/utils': 7.3.3(@types/react@19.2.2)(react@19.2.0) + '@mui/x-internals': 8.14.0(@types/react@19.2.2)(react@19.2.0) + '@mui/x-virtualizer': 0.2.5(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + clsx: 2.1.1 + prop-types: 15.8.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + use-sync-external-store: 1.6.0(react@19.2.0) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.2)(react@19.2.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0) + transitivePeerDependencies: + - '@types/react' + + '@mui/x-internals@8.14.0(@types/react@19.2.2)(react@19.2.0)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/utils': 7.3.3(@types/react@19.2.2)(react@19.2.0) + react: 19.2.0 + reselect: 5.1.1 + use-sync-external-store: 1.6.0(react@19.2.0) + transitivePeerDependencies: + - '@types/react' + + '@mui/x-virtualizer@0.2.5(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/utils': 7.3.3(@types/react@19.2.2)(react@19.2.0) + '@mui/x-internals': 8.14.0(@types/react@19.2.2)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + transitivePeerDependencies: + - '@types/react' + '@noble/hashes@1.8.0': {} '@nodelib/fs.scandir@2.1.5': @@ -5071,6 +5361,11 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/jsonwebtoken@9.0.10': + dependencies: + '@types/ms': 2.1.0 + '@types/node': 24.9.1 + '@types/linkify-it@5.0.0': {} '@types/markdown-it@14.1.2': @@ -5086,6 +5381,8 @@ snapshots: dependencies: '@types/node': 24.9.1 + '@types/ms@2.1.0': {} + '@types/multer@2.0.0': dependencies: '@types/express': 5.0.3 @@ -5304,6 +5601,8 @@ snapshots: asynckit@0.4.0: {} + aws-ssl-profiles@1.1.2: {} + babel-plugin-macros@3.1.0: dependencies: '@babel/runtime': 7.28.4 @@ -5359,6 +5658,8 @@ snapshots: node-releases: 2.0.26 update-browserslist-db: 1.1.3(browserslist@4.26.3) + buffer-equal-constant-time@1.0.1: {} + buffer-from@1.1.2: {} busboy@1.6.0: @@ -5546,6 +5847,8 @@ snapshots: delayed-stream@1.0.0: {} + denque@2.1.0: {} + depd@2.0.0: {} dezalgo@1.0.4: @@ -5568,6 +5871,10 @@ snapshots: dotenv@17.2.3: {} + drizzle-orm@0.44.7(mysql2@3.15.3): + optionalDependencies: + mysql2: 3.15.3 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5578,6 +5885,10 @@ snapshots: dependencies: xtend: 4.0.2 + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + ee-first@1.1.1: {} ejs@3.1.10: @@ -5956,6 +6267,10 @@ snapshots: function-bind@1.1.2: {} + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -6122,6 +6437,8 @@ snapshots: is-promise@4.0.0: {} + is-property@1.0.2: {} + is-stream@2.0.1: {} is-typedarray@1.0.0: {} @@ -6205,6 +6522,30 @@ snapshots: json5@2.2.3: {} + jsonwebtoken@9.0.2: + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.3 + + jwa@1.4.2: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@3.2.2: + dependencies: + jwa: 1.4.2 + safe-buffer: 5.2.1 + keygrip@1.1.0: dependencies: tsscmp: 1.0.6 @@ -6236,13 +6577,29 @@ snapshots: lodash.flattendeep@4.4.0: {} + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + lodash.merge@4.6.2: {} + lodash.once@4.1.1: {} + log-symbols@4.1.0: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 + long@5.3.2: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -6251,6 +6608,10 @@ snapshots: dependencies: yallist: 3.1.1 + lru-cache@7.18.3: {} + + lru.min@1.1.2: {} + make-dir@3.1.0: dependencies: semver: 6.3.1 @@ -6381,6 +6742,22 @@ snapshots: type-is: 1.6.18 xtend: 4.0.2 + mysql2@3.15.3: + dependencies: + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.7.0 + long: 5.3.2 + lru.min: 1.1.2 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + named-placeholders@1.1.3: + dependencies: + lru-cache: 7.18.3 + nanoid@3.3.11: {} natural-compare@1.4.0: {} @@ -6717,6 +7094,8 @@ snapshots: require-main-filename@2.0.0: {} + reselect@5.1.1: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -6809,6 +7188,8 @@ snapshots: transitivePeerDependencies: - supports-color + seq-queue@0.0.5: {} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -6886,6 +7267,8 @@ snapshots: sprintf-js@1.0.3: {} + sqlstring@2.3.3: {} + statuses@2.0.1: {} statuses@2.0.2: {}