fix: packages update + various perf fixes

This commit is contained in:
diced
2026-04-06 22:14:15 -07:00
parent 9b7759520c
commit 4c86b7fc38
19 changed files with 1331 additions and 1631 deletions

View File

@@ -101,7 +101,7 @@ export default defineConfig(
}, },
settings: { settings: {
react: { version: 'detect' }, react: { version: '19' },
}, },
}, },
); );

View File

@@ -5,12 +5,12 @@
"version": "4.5.2", "version": "4.5.2",
"scripts": { "scripts": {
"build": "tsx scripts/build.ts", "build": "tsx scripts/build.ts",
"dev": "cross-env NODE_ENV=development DEBUG=zipline tsx --require dotenv/config --enable-source-maps ./src/server", "dev": "cross-env NODE_ENV=development DEBUG=zipline tsx --require ./src/dotenv.js --enable-source-maps ./src/server",
"dev:nd": "cross-env NODE_ENV=development tsx --require dotenv/config --enable-source-maps ./src/server", "dev:nd": "cross-env NODE_ENV=development tsx --require ./src/dotenv.js --enable-source-maps ./src/server",
"dev:inspector": "cross-env NODE_ENV=development DEBUG=zipline tsx --require dotenv/config --inspect=0.0.0.0:9229 --enable-source-maps ./src/server", "dev:inspector": "cross-env NODE_ENV=development DEBUG=zipline tsx --require ./src/dotenv.js --inspect=0.0.0.0:9229 --enable-source-maps ./src/server",
"start": "cross-env NODE_ENV=production node --trace-warnings --require dotenv/config ./build/server", "start": "cross-env NODE_ENV=production node --trace-warnings --require ./src/dotenv.js ./build/server",
"start:inspector": "cross-env NODE_ENV=production node --require dotenv/config --inspect=0.0.0.0:9229 --enable-source-maps ./build/server", "start:inspector": "cross-env NODE_ENV=production node --require ./src/dotenv.js --inspect=0.0.0.0:9229 --enable-source-maps ./build/server",
"ctl": "NODE_ENV=production node --require dotenv/config --enable-source-maps ./build/ctl", "ctl": "NODE_ENV=production node --require ./src/dotenv.js --enable-source-maps ./build/ctl",
"validate": "tsx scripts/validate.ts", "validate": "tsx scripts/validate.ts",
"openapi": "tsx scripts/openapi.ts", "openapi": "tsx scripts/openapi.ts",
"db:prototype": "prisma db push --skip-generate && prisma generate --no-hints", "db:prototype": "prisma db push --skip-generate && prisma generate --no-hints",
@@ -22,8 +22,8 @@
"docker:compose:dev:logs": "docker compose --file docker-compose.dev.yml logs -f" "docker:compose:dev:logs": "docker compose --file docker-compose.dev.yml logs -f"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "3.726.1", "@aws-sdk/client-s3": "3.1025.0",
"@aws-sdk/lib-storage": "3.726.1", "@aws-sdk/lib-storage": "3.1025.0",
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
@@ -34,15 +34,15 @@
"@fastify/sensible": "^6.0.4", "@fastify/sensible": "^6.0.4",
"@fastify/static": "^9.0.0", "@fastify/static": "^9.0.0",
"@fastify/swagger": "^9.7.0", "@fastify/swagger": "^9.7.0",
"@mantine/charts": "^8.3.18", "@mantine/charts": "^9.0.1",
"@mantine/code-highlight": "^8.3.18", "@mantine/code-highlight": "^9.0.1",
"@mantine/core": "^8.3.18", "@mantine/core": "^9.0.1",
"@mantine/dates": "^8.3.18", "@mantine/dates": "^9.0.1",
"@mantine/dropzone": "^8.3.18", "@mantine/dropzone": "^9.0.1",
"@mantine/form": "^8.3.18", "@mantine/form": "^9.0.1",
"@mantine/hooks": "^8.3.18", "@mantine/hooks": "^9.0.1",
"@mantine/modals": "^8.3.18", "@mantine/modals": "^9.0.1",
"@mantine/notifications": "^8.3.18", "@mantine/notifications": "^9.0.1",
"@prisma/adapter-pg": "6.13.0", "@prisma/adapter-pg": "6.13.0",
"@prisma/client": "6.13.0", "@prisma/client": "6.13.0",
"@prisma/engines": "6.13.0", "@prisma/engines": "6.13.0",
@@ -50,8 +50,8 @@
"@prisma/migrate": "6.13.0", "@prisma/migrate": "6.13.0",
"@simplewebauthn/browser": "^13.3.0", "@simplewebauthn/browser": "^13.3.0",
"@simplewebauthn/server": "^13.3.0", "@simplewebauthn/server": "^13.3.0",
"@smithy/node-http-handler": "^4.1.1", "@smithy/node-http-handler": "^4.5.2",
"@tabler/icons-react": "^3.40.0", "@tabler/icons-react": "^3.41.1",
"archiver": "^7.0.1", "archiver": "^7.0.1",
"argon2": "^0.44.0", "argon2": "^0.44.0",
"asciinema-player": "^3.15.1", "asciinema-player": "^3.15.1",
@@ -63,8 +63,7 @@
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"dayjs": "^1.11.20", "dayjs": "^1.11.20",
"detect-browser": "^5.3.0", "detect-browser": "^5.3.0",
"devalue": "^5.6.4", "devalue": "^5.7.0",
"dotenv": "^17.3.1",
"fast-glob": "^3.3.3", "fast-glob": "^3.3.3",
"fastify": "^5.8.4", "fastify": "^5.8.4",
"fastify-plugin": "^5.1.0", "fastify-plugin": "^5.1.0",
@@ -74,7 +73,7 @@
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"iron-session": "^8.0.4", "iron-session": "^8.0.4",
"isomorphic-dompurify": "^3.7.1", "isomorphic-dompurify": "^3.7.1",
"katex": "^0.16.42", "katex": "^0.16.45",
"mantine-datatable": "^8.3.13", "mantine-datatable": "^8.3.13",
"ms": "^2.1.3", "ms": "^2.1.3",
"multer": "2.1.1", "multer": "2.1.1",
@@ -84,13 +83,12 @@
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4", "react-dom": "^19.2.4",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-router-dom": "^7.13.2", "react-router-dom": "^7.14.0",
"react-window": "1.8.11", "react-virtuoso": "^4.18.4",
"remark-gfm": "^4.0.1", "remark-gfm": "^4.0.1",
"sharp": "^0.34.5", "sharp": "^0.34.5",
"swr": "^2.4.1", "swr": "^2.4.1",
"typescript-eslint": "^8.57.2", "vite": "^8.0.5",
"vite": "^8.0.2",
"zod": "^4.3.6", "zod": "^4.3.6",
"zustand": "^5.0.12" "zustand": "^5.0.12"
}, },
@@ -102,19 +100,18 @@
"@types/katex": "^0.16.8", "@types/katex": "^0.16.8",
"@types/ms": "^2.1.0", "@types/ms": "^2.1.0",
"@types/multer": "^2.1.0", "@types/multer": "^2.1.0",
"@types/node": "^24.10.1", "@types/node": "^24.12.2",
"@types/qrcode": "^1.5.6", "@types/qrcode": "^1.5.6",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@types/react-window": "^1.8.8",
"@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.1", "eslint": "^10.2.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "^5.5.4", "eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24", "eslint-plugin-react-refresh": "^0.5.2",
"eslint-plugin-unused-imports": "^4.3.0", "eslint-plugin-unused-imports": "^4.3.0",
"postcss": "^8.5.8", "postcss": "^8.5.8",
"postcss-preset-mantine": "^1.18.0", "postcss-preset-mantine": "^1.18.0",
@@ -124,7 +121,8 @@
"tsc-alias": "^1.8.16", "tsc-alias": "^1.8.16",
"tsup": "^8.5.1", "tsup": "^8.5.1",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "^5.9.3" "typescript": "^6.0.2",
"typescript-eslint": "^8.58.0"
}, },
"engines": { "engines": {
"node": ">=22" "node": ">=22"

2763
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@ export default function ReloadPage() {
Why am I seeing this? Why am I seeing this?
</Button> </Button>
<Collapse in={view}> <Collapse expanded={view}>
<GenericError <GenericError
title='Failed to fetch dynamically imported module' title='Failed to fetch dynamically imported module'
message='This error can occur when a new version of the app is deployed while you have the page open. Please reload the page to update to the latest version.' message='This error can occur when a new version of the app is deployed while you have the page open. Please reload the page to update to the latest version.'

View File

@@ -114,7 +114,7 @@ export default function ViewFileId() {
</Group> </Group>
</Paper> </Paper>
<Collapse in={detailsOpen}> <Collapse expanded={detailsOpen}>
<Paper m='md' p='md' withBorder> <Paper m='md' p='md' withBorder>
{user?.view!.content && ( {user?.view!.content && (
<Typography> <Typography>

View File

@@ -396,7 +396,7 @@ export default function FileTable({
)} )}
<Box> <Box>
<Collapse in={selectedFiles.length > 0}> <Collapse expanded={selectedFiles.length > 0}>
<Paper withBorder p='sm' my='sm'> <Paper withBorder p='sm' my='sm'>
<Text size='sm' c='dimmed' mb='xs'> <Text size='sm' c='dimmed' mb='xs'>
Selections are saved across page changes Selections are saved across page changes
@@ -487,7 +487,7 @@ export default function FileTable({
</Collapse> </Collapse>
{modals && setModals && modals.idSearch && ( {modals && setModals && modals.idSearch && (
<Collapse in={modals.idSearch}> <Collapse expanded={modals.idSearch}>
<Paper withBorder p='sm' mt='sm'> <Paper withBorder p='sm' mt='sm'>
<TextInput <TextInput
placeholder='Search by ID' placeholder='Search by ID'

View File

@@ -236,7 +236,7 @@ export default function DashboardFolders() {
{filesOpen ? '▼' : '▶'} {currentFolder.name}&#39;s files{' '} {filesOpen ? '▼' : '▶'} {currentFolder.name}&#39;s files{' '}
{currentFolder._count ? `(${currentFolder._count.files})` : ''} {currentFolder._count ? `(${currentFolder._count.files})` : ''}
</Text> </Text>
<Collapse in={filesOpen}> <Collapse expanded={filesOpen}>
{view === 'grid' ? ( {view === 'grid' ? (
<Paper withBorder p='sm'> <Paper withBorder p='sm'>
<FilesGridView folderId={currentFolderId} /> <FilesGridView folderId={currentFolderId} />

View File

@@ -139,7 +139,7 @@ export default function Export3Details({ export3 }: { export3: Export3 }) {
{envOpened ? 'Hide' : 'Show'} OS Details {envOpened ? 'Hide' : 'Show'} OS Details
</Button> </Button>
<Collapse in={osOpened}> <Collapse expanded={osOpened}>
<HighlightCode language='json' code={JSON.stringify(export3.request.os, null, 2)} /> <HighlightCode language='json' code={JSON.stringify(export3.request.os, null, 2)} />
</Collapse> </Collapse>
@@ -147,7 +147,7 @@ export default function Export3Details({ export3 }: { export3: Export3 }) {
{envOpened ? 'Hide' : 'Show'} Environment {envOpened ? 'Hide' : 'Show'} Environment
</Button> </Button>
<Collapse in={envOpened}> <Collapse expanded={envOpened}>
<Paper withBorder> <Paper withBorder>
<Table> <Table>
<Table.Thead> <Table.Thead>

View File

@@ -195,7 +195,7 @@ export default function Export3Details({ export4 }: { export4: Export4 }) {
{envOpened ? 'Hide' : 'Show'} OS Details {envOpened ? 'Hide' : 'Show'} OS Details
</Button> </Button>
<Collapse in={osOpened}> <Collapse expanded={osOpened}>
<Paper withBorder> <Paper withBorder>
<Table> <Table>
<Table.Thead> <Table.Thead>
@@ -217,7 +217,7 @@ export default function Export3Details({ export4 }: { export4: Export4 }) {
{envOpened ? 'Hide' : 'Show'} Environment {envOpened ? 'Hide' : 'Show'} Environment
</Button> </Button>
<Collapse in={envOpened}> <Collapse expanded={envOpened}>
<Paper withBorder> <Paper withBorder>
<Table> <Table>
<Table.Thead> <Table.Thead>

View File

@@ -32,7 +32,7 @@ export default function Export4ImportSettings({
{showSettings ? 'Hide' : 'Show'} Settings to be Imported {showSettings ? 'Hide' : 'Show'} Settings to be Imported
</Button> </Button>
<Collapse in={showSettings}> <Collapse expanded={showSettings}>
<Paper withBorder> <Paper withBorder>
<Table> <Table>
<Table.Thead> <Table.Thead>

View File

@@ -297,7 +297,7 @@ export default function DashboardServerSettings() {
</Group> </Group>
{(data?.tampered?.length ?? 0) > 0 && ( {(data?.tampered?.length ?? 0) > 0 && (
<Collapse in={opened} transitionDuration={180}> <Collapse expanded={opened} transitionDuration={180}>
<Alert <Alert
color='red' color='red'
title='Environment Variable Settings' title='Environment Variable Settings'

View File

@@ -245,7 +245,7 @@ export default function Discord({
{...formOnUpload.getInputProps('discordOnUploadEmbed', { type: 'checkbox' })} {...formOnUpload.getInputProps('discordOnUploadEmbed', { type: 'checkbox' })}
/> />
<Collapse in={formOnUpload.values.discordOnUploadEmbed}> <Collapse expanded={formOnUpload.values.discordOnUploadEmbed}>
<Paper withBorder p='sm' mt='md'> <Paper withBorder p='sm' mt='md'>
<SimpleGrid cols={{ base: 1, md: 2 }} spacing='lg'> <SimpleGrid cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput <TextInput
@@ -348,7 +348,7 @@ export default function Discord({
{...formOnShorten.getInputProps('discordOnShortenEmbed', { type: 'checkbox' })} {...formOnShorten.getInputProps('discordOnShortenEmbed', { type: 'checkbox' })}
/> />
<Collapse in={formOnShorten.values.discordOnShortenEmbed}> <Collapse expanded={formOnShorten.values.discordOnShortenEmbed}>
<Paper withBorder p='sm' mt='md'> <Paper withBorder p='sm' mt='md'>
<SimpleGrid cols={{ base: 1, md: 2 }} spacing='lg'> <SimpleGrid cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput <TextInput

View File

@@ -57,7 +57,7 @@ export default function SettingsDashboard() {
label='Default Domain' label='Default Domain'
description='Set the default domain used for copied links anywhere in the dashboard. Leave blank or select "Default domain" to use the current domain that serves the dashboard.' description='Set the default domain used for copied links anywhere in the dashboard. Leave blank or select "Default domain" to use the current domain that serves the dashboard.'
value={settings.domain} value={settings.domain}
onChange={(value) => update('domain', value ?? '')} onChange={(value) => update('domain', (value as string) ?? '')}
/> />
<Select <Select

View File

@@ -194,7 +194,7 @@ export default function UploadFile({ title, folder }: { title?: string; folder?:
</Group> </Group>
</Dropzone> </Dropzone>
<Collapse in={progress.percent > 0 && progress.percent < 100}> <Collapse expanded={progress.percent > 0 && progress.percent < 100}>
{progress.percent > 0 && progress.percent < 100 && ( {progress.percent > 0 && progress.percent < 100 && (
<Progress.Root my='sm' size='xl'> <Progress.Root my='sm' size='xl'>
<Progress.Section value={progress.percent} animated> <Progress.Section value={progress.percent} animated>
@@ -204,7 +204,7 @@ export default function UploadFile({ title, folder }: { title?: string; folder?:
)} )}
</Collapse> </Collapse>
<Collapse in={progress.speed > 0 && progress.remaining > 0}> <Collapse expanded={progress.speed > 0 && progress.remaining > 0}>
<Paper withBorder p='xs' radius='sm'> <Paper withBorder p='xs' radius='sm'>
<Text ta='center' size='sm'> <Text ta='center' size='sm'>
{bytes(progress.speed)}/s, {humanizeDuration(progress.remaining)} remaining {bytes(progress.speed)}/s, {humanizeDuration(progress.remaining)} remaining
@@ -212,7 +212,7 @@ export default function UploadFile({ title, folder }: { title?: string; folder?:
</Paper> </Paper>
</Collapse> </Collapse>
<Collapse in={progress.percent === 100}> <Collapse expanded={progress.percent === 100}>
<Paper withBorder p='xs' radius='sm'> <Paper withBorder p='xs' radius='sm'>
<Text ta='center' size='sm' c='yellow' fw={500}> <Text ta='center' size='sm' c='yellow' fw={500}>
Finalizing upload(s)... Finalizing upload(s)...

View File

@@ -380,7 +380,7 @@ export default function UploadOptionsButton({ folder, numFiles }: { folder?: str
</> </>
} }
value={options.overrides_returnDomain ?? ''} value={options.overrides_returnDomain ?? ''}
onChange={(value) => setOption('overrides_returnDomain', value || null)} onChange={(value) => setOption('overrides_returnDomain', (value as string) || null)}
comboboxProps={{ comboboxProps={{
withinPortal: true, withinPortal: true,
portalProps: { portalProps: {

View File

@@ -1,12 +1,11 @@
import { ActionIcon, Button, CopyButton, Paper, ScrollArea, Text, useMantineTheme } from '@mantine/core'; import { ActionIcon, Button, CopyButton, Paper, Text, useMantineTheme } from '@mantine/core';
import { IconCheck, IconChevronDown, IconChevronUp, IconClipboardCopy } from '@tabler/icons-react'; import { IconCheck, IconChevronDown, IconChevronUp, IconClipboardCopy } from '@tabler/icons-react';
import type { HLJSApi } from 'highlight.js'; import type { HLJSApi } from 'highlight.js';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { FixedSizeList as List } from 'react-window'; import { Virtuoso } from 'react-virtuoso';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import './HighlightCode.theme.scss';
import * as sanitize from 'isomorphic-dompurify'; import * as sanitize from 'isomorphic-dompurify';
import './HighlightCode.theme.scss';
export default function HighlightCode({ language, code }: { language: string; code: string }) { export default function HighlightCode({ language, code }: { language: string; code: string }) {
const { pathname } = useLocation(); const { pathname } = useLocation();
@@ -20,37 +19,33 @@ export default function HighlightCode({ language, code }: { language: string; co
import('highlight.js').then((mod) => setHljs(mod.default || mod)); import('highlight.js').then((mod) => setHljs(mod.default || mod));
}, []); }, []);
const lines = useMemo(() => code.split('\n'), [code]); const cleanedCode = sanitize.sanitize(code, { USE_PROFILES: { html: true } });
const visible = expanded || noClamp ? lines.length : Math.min(lines.length, 50); const lines = cleanedCode.split('\n');
const expandable = !noClamp && lines.length > 50; const isExpandable = !noClamp && lines.length > 50;
const totalCount = isExpandable && !expanded ? 50 : lines.length;
const estimatedHeight = Math.min(totalCount * 24, 400);
const lang = useMemo(() => { const lang = useMemo(() => {
if (!hljs) return 'plaintext'; if (!hljs) return 'plaintext';
if (hljs.getLanguage(language)) return language; return hljs.getLanguage(language) ? language : 'plaintext';
return 'plaintext';
}, [hljs, language]); }, [hljs, language]);
const hlLines = useMemo(() => { const hlLines = useMemo(() => {
if (!hljs) return lines; if (!hljs) return lines;
return lines.map((line) => hljs.highlight(line || ' ', { language: lang }).value);
return lines.map(
(line) =>
hljs.highlight(line, {
language: lang,
}).value,
);
}, [lines, hljs, lang]); }, [lines, hljs, lang]);
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => ( const rowRenderer = (index: number) => (
<div <div
style={{ style={{
...style,
display: 'flex', display: 'flex',
alignItems: 'flex-start', alignItems: 'flex-start',
whiteSpace: 'pre', whiteSpace: 'pre',
fontFamily: 'monospace', fontFamily: theme.fontFamilyMonospace,
fontSize: '0.8rem', fontSize: '0.8rem',
lineHeight: '1.5',
backgroundColor: 'transparent',
}} }}
> >
<Text <Text
@@ -69,16 +64,14 @@ export default function HighlightCode({ language, code }: { language: string; co
<code <code
className='theme hljs' className='theme hljs'
style={{ flex: 1, fontSize: '0.8rem' }} style={{ flex: 1, padding: 0, background: 'none', alignSelf: 'center' }}
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{ __html: hlLines[index] }}
__html: sanitize.sanitize(hlLines[index], { USE_PROFILES: { html: true } }),
}}
/> />
</div> </div>
); );
return ( return (
<Paper withBorder p='xs' my='md' pos='relative'> <Paper withBorder p='xs' my='md' pos='relative' style={{ overflow: 'hidden' }}>
<CopyButton value={code}> <CopyButton value={code}>
{({ copied, copy }) => ( {({ copied, copy }) => (
<ActionIcon <ActionIcon
@@ -86,40 +79,39 @@ export default function HighlightCode({ language, code }: { language: string; co
variant='outline' variant='outline'
color={copied ? 'green' : 'gray'} color={copied ? 'green' : 'gray'}
size='md' size='md'
style={{ zIndex: 4, position: 'absolute', top: '0.5rem', right: '0.5rem' }} style={{ zIndex: 10, position: 'absolute', top: '0.5rem', right: '0.5rem' }}
> >
{!copied ? ( {copied ? (
<IconClipboardCopy size='1rem' />
) : (
<IconCheck color={theme.colors.green[4]} size='1rem' /> <IconCheck color={theme.colors.green[4]} size='1rem' />
) : (
<IconClipboardCopy size='1rem' />
)} )}
</ActionIcon> </ActionIcon>
)} )}
</CopyButton> </CopyButton>
{noClamp ? ( <div style={{ height: noClamp && (expanded || !isExpandable) ? 'auto' : estimatedHeight }}>
<ScrollArea type='auto' offsetScrollbars={false}> <Virtuoso
<div> style={{ height: '100%' }}
{hlLines.map((_, index) => ( totalCount={totalCount}
<Row key={index} index={index} style={{}} /> itemContent={rowRenderer}
))} initialItemCount={30}
increaseViewportBy={200}
/>
</div> </div>
</ScrollArea>
) : (
<ScrollArea type='auto' offsetScrollbars={false} style={{ maxHeight: 400 }}>
<List height={400} width='100%' itemCount={visible} itemSize={20} overscanCount={10}>
{Row}
</List>
</ScrollArea>
)}
{expandable && ( {isExpandable && (
<Button <Button
variant='light' variant='light'
size='compact-sm' size='compact-sm'
onClick={() => setExpanded((e) => !e)} onClick={() => setExpanded((e) => !e)}
leftSection={expanded ? <IconChevronUp size='1rem' /> : <IconChevronDown size='1rem' />} leftSection={expanded ? <IconChevronUp size='1rem' /> : <IconChevronDown size='1rem' />}
style={{ position: 'absolute', bottom: '0.5rem', right: '0.5rem' }} style={{
position: 'absolute',
bottom: '0.5rem',
right: '0.5rem',
zIndex: 10,
}}
> >
{expanded ? 'Show Less' : `Show More (${lines.length - 50} more lines)`} {expanded ? 'Show Less' : `Show More (${lines.length - 50} more lines)`}
</Button> </Button>

5
src/dotenv.js Normal file
View File

@@ -0,0 +1,5 @@
// --require ./src/dotenv.js
// loads environment variables from a .env file into process.env on startup
try {
process.loadEnvFile('.env');
} catch {}

View File

@@ -63,6 +63,8 @@ export class S3Datasource extends Datasource {
keepAlive: true, keepAlive: true,
}), }),
}), }),
requestChecksumCalculation: 'WHEN_REQUIRED',
responseChecksumValidation: 'WHEN_REQUIRED',
}); });
this.ensureReadWriteAccess(); this.ensureReadWriteAccess();

View File

@@ -4,10 +4,14 @@ const MAX = 256 - (256 % CHARSET_LENGTH);
function getRandomValues(array: Uint8Array) { function getRandomValues(array: Uint8Array) {
if (typeof crypto !== 'undefined' && crypto.getRandomValues) { if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
return crypto.getRandomValues(array); // TODO: remove any cast when the types are fixed...
return crypto.getRandomValues(<any>array);
} else { } else {
// eslint-disable-next-line @typescript-eslint/no-require-imports console.error(
return require('crypto').webcrypto.getRandomValues(array); 'No secure random number generator available. Please use node@22+ and a supported platform.',
);
process.exit(1);
} }
} }