upgrade execa and node types

pull/2263/head
Mikael Finstad 2024-12-06 18:24:23 +08:00
rodzic 2fb78214fc
commit ca8eccf899
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 25AB36E3E81CBC26
15 zmienionych plików z 226 dodań i 81 usunięć

Wyświetl plik

@ -45,6 +45,7 @@
"@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-switch": "^1.0.1", "@radix-ui/react-switch": "^1.0.1",
"@tsconfig/node18": "^18.2.2", "@tsconfig/node18": "^18.2.2",
"@tsconfig/node20": "^20.1.4",
"@tsconfig/strictest": "^2.0.2", "@tsconfig/strictest": "^2.0.2",
"@tsconfig/vite-react": "^3.0.0", "@tsconfig/vite-react": "^3.0.0",
"@types/color": "^3.0.6", "@types/color": "^3.0.6",
@ -58,7 +59,7 @@
"@types/mime-types": "^2.1.4", "@types/mime-types": "^2.1.4",
"@types/morgan": "^1.9.9", "@types/morgan": "^1.9.9",
"@types/mousetrap": "^1.6.15", "@types/mousetrap": "^1.6.15",
"@types/node": "18", "@types/node": "20",
"@types/react": "^18.2.66", "@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22", "@types/react-dom": "^18.2.22",
"@types/sortablejs": "^1.15.0", "@types/sortablejs": "^1.15.0",
@ -122,7 +123,7 @@
"tiny-invariant": "^1.3.3", "tiny-invariant": "^1.3.3",
"tsx": "^4.7.1", "tsx": "^4.7.1",
"type-fest": "^4.23.0", "type-fest": "^4.23.0",
"typescript": "^5.5.4", "typescript": "^5.7.2",
"use-debounce": "^5.1.0", "use-debounce": "^5.1.0",
"use-trace-update": "^1.3.0", "use-trace-update": "^1.3.0",
"vite": "^5.3.6", "vite": "^5.3.6",
@ -134,7 +135,7 @@
"cue-parser": "^0.3.0", "cue-parser": "^0.3.0",
"electron-store": "5.1.1", "electron-store": "5.1.1",
"electron-unhandled": "^5.0.0", "electron-unhandled": "^5.0.0",
"execa": "^8.0.1", "execa": "^9.5.1",
"express": "^4.20.0", "express": "^4.20.0",
"express-async-handler": "^1.2.0", "express-async-handler": "^1.2.0",
"file-type": "patch:file-type@npm%3A19.4.1#~/.yarn/patches/file-type-npm-19.4.1-d18086444c.patch", "file-type": "patch:file-type@npm%3A19.4.1#~/.yarn/patches/file-type-npm-19.4.1-d18086444c.patch",

Wyświetl plik

@ -5,7 +5,7 @@ import { DateTime } from 'luxon';
const xmlUrl = new URL('../no.mifi.losslesscut.appdata.xml', import.meta.url); const xmlUrl = new URL('../no.mifi.losslesscut.appdata.xml', import.meta.url);
const xmlData = await readFile(xmlUrl); const xmlData = await readFile(xmlUrl);
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)) as unknown as string); const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
const parser = new XMLParser({ alwaysCreateTextNode: true, ignoreAttributes: false, ignoreDeclaration: false }); const parser = new XMLParser({ alwaysCreateTextNode: true, ignoreAttributes: false, ignoreDeclaration: false });
const xml = parser.parse(xmlData); const xml = parser.parse(xmlData);

Wyświetl plik

@ -18,7 +18,7 @@ const bundleId = args[4];
// seems to be the same // seems to be the same
const ascPublicId = apiIssuer; const ascPublicId = apiIssuer;
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)) as unknown as string); const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
console.log('Using version', packageJson.version); console.log('Using version', packageJson.version);

Wyświetl plik

@ -1,4 +1,5 @@
import assert from 'node:assert'; import assert from 'node:assert';
import { ExecaError } from 'execa';
import logger from './logger.js'; import logger from './logger.js';
import { createMediaSourceProcess, readOneJpegFrame as readOneJpegFrameRaw } from './ffmpeg.js'; import { createMediaSourceProcess, readOneJpegFrame as readOneJpegFrameRaw } from './ffmpeg.js';
@ -64,16 +65,12 @@ export function createMediaSourceStream({ path, videoStreamIndex, audioStreamInd
try { try {
await process; await process;
} catch (err) { } catch (err) {
if (err instanceof Error && err.name === 'AbortError') { if (err instanceof ExecaError && err.isTerminated) {
return; return;
} }
// @ts-expect-error todo logger.warn((err as Error).message);
if (!(err.killed)) { logger.warn(stderr.toString('utf8'));
// @ts-expect-error todo
logger.warn(err.message);
logger.warn(stderr.toString('utf8'));
}
} }
})(); })();

Wyświetl plik

@ -1,7 +1,7 @@
import { join } from 'node:path'; import { join } from 'node:path';
import readline from 'node:readline'; import readline from 'node:readline';
import stringToStream from 'string-to-stream'; import stringToStream from 'string-to-stream';
import { BufferEncodingOption, execa, ExecaChildProcess, Options as ExecaOptions } from 'execa'; import { execa, Options as ExecaOptions, ResultPromise } from 'execa';
import assert from 'node:assert'; import assert from 'node:assert';
import { Readable } from 'node:stream'; import { Readable } from 'node:stream';
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
@ -14,7 +14,7 @@ import logger from './logger.js';
import { parseFfmpegProgressLine } from './progress.js'; import { parseFfmpegProgressLine } from './progress.js';
const runningFfmpegs = new Set<ExecaChildProcess<Buffer>>(); const runningFfmpegs = new Set<ResultPromise<Omit<ExecaOptions, 'encoding'> & { encoding: 'buffer' }>>();
// setInterval(() => console.log(runningFfmpegs.size), 1000); // setInterval(() => console.log(runningFfmpegs.size), 1000);
let customFfPath: string | undefined; let customFfPath: string | undefined;
@ -59,7 +59,7 @@ export const getFfmpegPath = () => getFfPath('ffmpeg');
export function abortFfmpegs() { export function abortFfmpegs() {
logger.info('Aborting', runningFfmpegs.size, 'ffmpeg process(es)'); logger.info('Aborting', runningFfmpegs.size, 'ffmpeg process(es)');
runningFfmpegs.forEach((process) => { runningFfmpegs.forEach((process) => {
process.kill('SIGTERM', { forceKillAfterTimeout: 10000 }); process.kill();
}); });
} }
@ -88,20 +88,27 @@ function handleProgress(
}); });
} }
function getExecaOptions({ env, ...customExecaOptions }: Omit<ExecaOptions<BufferEncodingOption>, 'buffer'> = {}) { function getExecaOptions({ env, cancelSignal, ...rest }: ExecaOptions = {}) {
const execaOptions: Omit<ExecaOptions<BufferEncodingOption>, 'buffer'> = { ...customExecaOptions, encoding: 'buffer' }; // This is a ugly hack to please execa which expects cancelSignal to be a prototype of AbortSignal
// https://github.com/mifi/lossless-cut/issues/1143#issuecomment-1500883489 // however this gets lost during @electron/remote passing
if (isLinux && !isDev && !customFfPath) { // https://github.com/sindresorhus/execa/blob/c8cff27a47b6e6f1cfbfec2bf7fa9dcd08cefed1/lib/terminate/cancel.js#L5
return { if (cancelSignal != null) Object.setPrototypeOf(cancelSignal, new AbortController().signal);
...execaOptions,
env: { ...env, LD_LIBRARY_PATH: process.resourcesPath }, const execaOptions: Pick<ExecaOptions, 'env'> & { encoding: 'buffer' } = {
}; ...(cancelSignal != null && { cancelSignal }),
} ...rest,
encoding: 'buffer' as const,
env: {
...env,
// https://github.com/mifi/lossless-cut/issues/1143#issuecomment-1500883489
...(isLinux && !isDev && !customFfPath && { LD_LIBRARY_PATH: process.resourcesPath }),
},
};
return execaOptions; return execaOptions;
} }
// todo collect warnings from ffmpeg output and show them after export? example: https://github.com/mifi/lossless-cut/issues/1469 // todo collect warnings from ffmpeg output and show them after export? example: https://github.com/mifi/lossless-cut/issues/1469
function runFfmpegProcess(args: readonly string[], customExecaOptions?: Omit<ExecaOptions<BufferEncodingOption>, 'encoding'>, additionalOptions?: { logCli?: boolean }) { function runFfmpegProcess(args: readonly string[], customExecaOptions?: ExecaOptions, additionalOptions?: { logCli?: boolean }) {
const ffmpegPath = getFfmpegPath(); const ffmpegPath = getFfmpegPath();
const { logCli = true } = additionalOptions ?? {}; const { logCli = true } = additionalOptions ?? {};
if (logCli) logger.info(getFfCommandLine('ffmpeg', args)); if (logCli) logger.info(getFfCommandLine('ffmpeg', args));
@ -187,8 +194,8 @@ export async function renderWaveformPng({ filePath, start, duration, color, stre
logger.info(`${getFfCommandLine('ffmpeg1', args1)} | \n${getFfCommandLine('ffmpeg2', args2)}`); logger.info(`${getFfCommandLine('ffmpeg1', args1)} | \n${getFfCommandLine('ffmpeg2', args2)}`);
let ps1: ExecaChildProcess<Buffer> | undefined; let ps1: ResultPromise<{ encoding: 'buffer' }> | undefined;
let ps2: ExecaChildProcess<Buffer> | undefined; let ps2: ResultPromise<{ encoding: 'buffer' }> | undefined;
try { try {
ps1 = runFfmpegProcess(args1, { buffer: false }, { logCli: false }); ps1 = runFfmpegProcess(args1, { buffer: false }, { logCli: false });
ps2 = runFfmpegProcess(args2, undefined, { logCli: false }); ps2 = runFfmpegProcess(args2, undefined, { logCli: false });
@ -210,7 +217,7 @@ export async function renderWaveformPng({ filePath, start, duration, color, stre
} }
return { return {
buffer: stdout, buffer: Buffer.from(stdout),
}; };
} catch (err) { } catch (err) {
if (ps1) ps1.kill(); if (ps1) ps1.kill();
@ -465,7 +472,7 @@ async function readFormatData(filePath: string) {
const { stdout } = await runFfprobe([ const { stdout } = await runFfprobe([
'-of', 'json', '-show_format', '-i', filePath, '-hide_banner', '-of', 'json', '-show_format', '-i', filePath, '-hide_banner',
]); ]);
return JSON.parse(stdout as unknown as string).format; return JSON.parse(new TextDecoder().decode(stdout)).format;
} }
export async function getDuration(filePath: string) { export async function getDuration(filePath: string) {
@ -557,7 +564,7 @@ export function createMediaSourceProcess({ path, videoStreamIndex, audioStreamIn
if (enableLog) logger.info(getFfCommandLine('ffmpeg', args)); if (enableLog) logger.info(getFfCommandLine('ffmpeg', args));
return execa(getFfmpegPath(), args, { encoding: null, buffer: false, stderr: enableLog ? 'inherit' : 'pipe' }); return execa(getFfmpegPath(), args, { encoding: 'buffer', buffer: false, stderr: enableLog ? 'inherit' : 'pipe' });
} }
export async function downloadMediaUrl(url: string, outPath: string) { export async function downloadMediaUrl(url: string, outPath: string) {

Wyświetl plik

@ -230,7 +230,7 @@ async function startPlayback({ path, video, videoStreamIndex, audioStreamIndex,
processChunk(); processChunk();
} }
function drawJpegFrame(canvas: HTMLCanvasElement | null, jpegImage: Buffer) { function drawJpegFrame(canvas: HTMLCanvasElement | null, jpegImage: Uint8Array) {
if (!canvas) return; if (!canvas) return;
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
@ -244,7 +244,8 @@ function drawJpegFrame(canvas: HTMLCanvasElement | null, jpegImage: Buffer) {
img.onload = () => ctx.drawImage(img, 0, 0, canvas.width, canvas.height); img.onload = () => ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// eslint-disable-next-line unicorn/prefer-add-event-listener // eslint-disable-next-line unicorn/prefer-add-event-listener
img.onerror = (error) => console.error('Canvas JPEG image error', error); img.onerror = (error) => console.error('Canvas JPEG image error', error);
img.src = `data:image/jpeg;base64,${jpegImage.toString('base64')}`; // todo use Blob?
img.src = `data:image/jpeg;base64,${Buffer.from(jpegImage).toString('base64')}`;
} }
async function createPauseImage({ path, seekTo, videoStreamIndex, canvas, signal }: { async function createPauseImage({ path, seekTo, videoStreamIndex, canvas, signal }: {

Wyświetl plik

@ -84,7 +84,7 @@ export async function saveLlcProject({ savePath, filePath, cutSegments }) {
} }
export async function loadLlcProject(path: string) { export async function loadLlcProject(path: string) {
const parsed = JSON5.parse(await readFile(path) as unknown as string) as unknown; const parsed = JSON5.parse(await readFile(path, 'utf8')) as unknown;
if (parsed == null || typeof parsed !== 'object') throw new Error('Invalid LLC file'); if (parsed == null || typeof parsed !== 'object') throw new Error('Invalid LLC file');
let mediaFileName: string | undefined; let mediaFileName: string | undefined;
if ('mediaFileName' in parsed && typeof parsed.mediaFileName === 'string') { if ('mediaFileName' in parsed && typeof parsed.mediaFileName === 'string') {

Wyświetl plik

@ -27,23 +27,23 @@ export class RefuseOverwriteError extends Error {
} }
} }
export function fixRemoteBuffer(buffer: Buffer) { export function safeCreateBlob(array: Uint8Array, options?: BlobPropertyBag) {
// if we don't do this when creating a Blob, we get: // if we don't do this when creating a Blob, we get:
// "Failed to construct 'Blob': The provided ArrayBufferView value must not be resizable." // "Failed to construct 'Blob': The provided ArrayBufferView value must not be resizable."
// maybe when moving away from @electron/remote, it's not needed anymore? // maybe when moving away from @electron/remote, it's not needed anymore?
const buffer2 = Buffer.allocUnsafe(buffer.length); // https://stackoverflow.com/a/25255750/6519037
buffer.copy(buffer2); const cloned = new Uint8Array(array);
return buffer2; return new Blob([cloned], options);
} }
export function logStdoutStderr({ stdout, stderr }: { stdout: Buffer, stderr: Buffer }) { export function logStdoutStderr({ stdout, stderr }: { stdout: Uint8Array, stderr: Uint8Array }) {
if (stdout.length > 0) { if (stdout.length > 0) {
console.log('%cSTDOUT:', 'color: green; font-weight: bold'); console.log('%cSTDOUT:', 'color: green; font-weight: bold');
console.log(stdout.toString('utf8')); console.log(new TextDecoder().decode(stdout));
} }
if (stderr.length > 0) { if (stderr.length > 0) {
console.log('%cSTDERR:', 'color: blue; font-weight: bold'); console.log('%cSTDERR:', 'color: blue; font-weight: bold');
console.log(stderr.toString('utf8')); console.log(new TextDecoder().decode(stderr));
} }
} }
@ -77,7 +77,7 @@ export async function readFrames({ filePath, from, to, streamIndex }: {
}) { }) {
const intervalsArgs = from != null && to != null ? ['-read_intervals', `${from}%${to}`] : []; const intervalsArgs = from != null && to != null ? ['-read_intervals', `${from}%${to}`] : [];
const { stdout } = await runFfprobe(['-v', 'error', ...intervalsArgs, '-show_packets', '-select_streams', String(streamIndex), '-show_entries', 'packet=pts_time,flags', '-of', 'json', filePath], { logCli: false }); const { stdout } = await runFfprobe(['-v', 'error', ...intervalsArgs, '-show_packets', '-select_streams', String(streamIndex), '-show_entries', 'packet=pts_time,flags', '-of', 'json', filePath], { logCli: false });
const packetsFiltered: Frame[] = (JSON.parse(stdout as unknown as string).packets as { flags: string, pts_time: string }[]) const packetsFiltered: Frame[] = (JSON.parse(new TextDecoder().decode(stdout)).packets as { flags: string, pts_time: string }[])
.map((p) => ({ .map((p) => ({
keyframe: p.flags[0] === 'K', keyframe: p.flags[0] === 'K',
time: parseFloat(p.pts_time), time: parseFloat(p.pts_time),
@ -356,7 +356,7 @@ export async function readFileMeta(filePath: string) {
let parsedJson: FFprobeProbeResult; let parsedJson: FFprobeProbeResult;
try { try {
// https://github.com/mifi/lossless-cut/issues/1342 // https://github.com/mifi/lossless-cut/issues/1342
parsedJson = JSON.parse(stdout.toString('utf8')); parsedJson = JSON.parse(new TextDecoder().decode(stdout));
} catch { } catch {
console.log('ffprobe stdout', stdout); console.log('ffprobe stdout', stdout);
throw new Error('ffprobe returned malformed data'); throw new Error('ffprobe returned malformed data');
@ -383,9 +383,9 @@ async function renderThumbnail(filePath: string, timestamp: number, signal: Abor
'-', '-',
]; ];
const { stdout } = await runFfmpeg(args, { signal }, { logCli: false }); const { stdout } = await runFfmpeg(args, { cancelSignal: signal }, { logCli: false });
const blob = new Blob([fixRemoteBuffer(stdout)], { type: 'image/jpeg' }); const blob = safeCreateBlob(stdout, { type: 'image/jpeg' });
return URL.createObjectURL(blob); return URL.createObjectURL(blob);
} }
@ -399,7 +399,7 @@ export async function extractSubtitleTrack(filePath: string, streamId: number) {
]; ];
const { stdout } = await runFfmpeg(args); const { stdout } = await runFfmpeg(args);
return stdout.toString('utf8'); return new TextDecoder().decode(stdout);
} }
export async function extractSubtitleTrackToSegments(filePath: string, streamId: number) { export async function extractSubtitleTrackToSegments(filePath: string, streamId: number) {
@ -423,7 +423,7 @@ export async function extractSubtitleTrackVtt(filePath: string, streamId: number
const { stdout } = await runFfmpeg(args); const { stdout } = await runFfmpeg(args);
const blob = new Blob([fixRemoteBuffer(stdout)], { type: 'text/vtt' }); const blob = safeCreateBlob(stdout, { type: 'text/vtt' });
return URL.createObjectURL(blob); return URL.createObjectURL(blob);
} }

Wyświetl plik

@ -762,7 +762,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
appendFfmpegCommandLog(ffmpegArgs); appendFfmpegCommandLog(ffmpegArgs);
const { stdout } = await runFfmpegWithProgress({ ffmpegArgs, duration, onProgress }); const { stdout } = await runFfmpegWithProgress({ ffmpegArgs, duration, onProgress });
console.log(stdout.toString('utf8')); console.log(new TextDecoder().decode(stdout));
invariant(outPath != null); invariant(outPath != null);
await transferTimestamps({ inPath: filePathArg, outPath, treatOutputFileModifiedTimeAsStart }); await transferTimestamps({ inPath: filePathArg, outPath, treatOutputFileModifiedTimeAsStart });
@ -901,7 +901,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
appendFfmpegCommandLog(ffmpegArgs); appendFfmpegCommandLog(ffmpegArgs);
const { stdout } = await runFfmpeg(ffmpegArgs); const { stdout } = await runFfmpeg(ffmpegArgs);
console.log(stdout.toString('utf8')); console.log(new TextDecoder().decode(stdout));
return outPaths; return outPaths;
}, [appendFfmpegCommandLog, enableOverwriteOutput, filePath]); }, [appendFfmpegCommandLog, enableOverwriteOutput, filePath]);
@ -939,7 +939,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
try { try {
appendFfmpegCommandLog(ffmpegArgs); appendFfmpegCommandLog(ffmpegArgs);
const { stdout } = await runFfmpeg(ffmpegArgs); const { stdout } = await runFfmpeg(ffmpegArgs);
console.log(stdout.toString('utf8')); console.log(new TextDecoder().decode(stdout));
} catch (err) { } catch (err) {
// Unfortunately ffmpeg will exit with code 1 even though it's a success // Unfortunately ffmpeg will exit with code 1 even though it's a success
// Note: This is kind of hacky: // Note: This is kind of hacky:

Wyświetl plik

@ -6,6 +6,7 @@ import sortBy from 'lodash/sortBy';
import { renderThumbnails as ffmpegRenderThumbnails } from '../ffmpeg'; import { renderThumbnails as ffmpegRenderThumbnails } from '../ffmpeg';
import { Thumbnail } from '../types'; import { Thumbnail } from '../types';
import { isDurationValid } from '../segments'; import { isDurationValid } from '../segments';
import { isExecaError } from '../util';
export default ({ filePath, zoomedDuration, zoomWindowStartTime, showThumbnails }: { export default ({ filePath, zoomedDuration, zoomWindowStartTime, showThumbnails }: {
@ -39,7 +40,7 @@ export default ({ filePath, zoomedDuration, zoomWindowStartTime, showThumbnails
await ffmpegRenderThumbnails({ signal: abortController.signal, filePath: debounced.filePath, from: debounced.zoomWindowStartTime, duration: debounced.zoomedDuration, onThumbnail: addThumbnail }); await ffmpegRenderThumbnails({ signal: abortController.signal, filePath: debounced.filePath, from: debounced.zoomWindowStartTime, duration: debounced.zoomedDuration, onThumbnail: addThumbnail });
} catch (err) { } catch (err) {
if ((err as Error).name !== 'AbortError') { if ((err as Error).name !== 'AbortError' && !(isExecaError(err) && err.isCanceled)) {
console.error('Failed to render thumbnails', err); console.error('Failed to render thumbnails', err);
} }
} }

Wyświetl plik

@ -3,7 +3,7 @@ import sortBy from 'lodash/sortBy';
import { useThrottle } from '@uidotdev/usehooks'; import { useThrottle } from '@uidotdev/usehooks';
import { waveformColorDark, waveformColorLight } from '../colors'; import { waveformColorDark, waveformColorLight } from '../colors';
import { fixRemoteBuffer, renderWaveformPng } from '../ffmpeg'; import { renderWaveformPng, safeCreateBlob } from '../ffmpeg';
import { RenderableWaveform } from '../types'; import { RenderableWaveform } from '../types';
import { FFprobeStream } from '../../../../ffprobe'; import { FFprobeStream } from '../../../../ffprobe';
@ -75,7 +75,7 @@ export default ({ darkMode, filePath, relevantTime, duration, waveformEnabled, a
return { return {
...w, ...w,
url: URL.createObjectURL(new Blob([fixRemoteBuffer(buffer)], { type: 'image/png' })), url: URL.createObjectURL(safeCreateBlob(buffer, { type: 'image/png' })),
}; };
})); }));
} catch (err) { } catch (err) {

Wyświetl plik

@ -2,7 +2,7 @@ import i18n from 'i18next';
import { Trans } from 'react-i18next'; import { Trans } from 'react-i18next';
import CopyClipboardButton from './components/CopyClipboardButton'; import CopyClipboardButton from './components/CopyClipboardButton';
import { isStoreBuild, isMasBuild, isWindowsStoreBuild } from './util'; import { isStoreBuild, isMasBuild, isWindowsStoreBuild, isExecaError } from './util';
import { ReactSwal } from './swal'; import { ReactSwal } from './swal';
const electron = window.require('electron'); const electron = window.require('electron');
@ -33,15 +33,15 @@ export function openSendReportDialog(err: unknown | undefined, state?: unknown)
const version = app.getVersion(); const version = app.getVersion();
const text = `${err instanceof Error ? err.stack : 'No error occurred.'}\n\n${JSON.stringify({ const text = `${err instanceof Error ? err.stack : 'No error occurred.'}\n\n${JSON.stringify({
err: err instanceof Error && { err: isExecaError(err) && {
code: err['code'], code: err.code,
killed: err['killed'], isTerminated: err.isTerminated,
failed: err['failed'], failed: err.failed,
timedOut: err['timedOut'], timedOut: err.timedOut,
isCanceled: err['isCanceled'], isCanceled: err.isCanceled,
exitCode: err['exitCode'], exitCode: err.exitCode,
signal: err['signal'], signal: err.signal,
signalDescription: err['signalDescription'], signalDescription: err.signalDescription,
}, },
state, state,

Wyświetl plik

@ -291,17 +291,23 @@ export const deleteDispositionValue = 'llc_disposition_remove';
export const mirrorTransform = 'matrix(-1, 0, 0, 1, 0, 0)'; export const mirrorTransform = 'matrix(-1, 0, 0, 1, 0, 0)';
export type InvariantExecaError = ExecaError<string> | ExecaError<Buffer> | ExecaError<undefined>; // todo this is not a correct assumption
export type InvariantExecaError = ExecaError<{ encoding: 'utf8' }> | ExecaError<{ encoding: 'buffer' }>;
// note: I don't think we can use instanceof ExecaError because the error has been sent over the main-renderer bridge // We can't use `instanceof ExecaError` because the error has been sent over the main-renderer bridge (@electron/remote)
// so instead we just check if it has some of execa's specific error properties
export function isExecaError(err: unknown): err is InvariantExecaError { export function isExecaError(err: unknown): err is InvariantExecaError {
return err instanceof Error && 'stdout' in err && 'stderr' in err; // https://github.com/sindresorhus/execa/blob/main/docs/api.md#resultfailed
return err instanceof Error && ('failed' in err && 'shortMessage' in err && 'isForcefullyTerminated' in err);
} }
// execa killed (aborted by user) export const isAbortedError = (err: unknown) => (
export const isAbortedError = (err: unknown) => isExecaError(err) && err.killed; // execa killed (aborted by user). isTerminated because runningFfmpegs process.kill
(isExecaError(err) && (err.isCanceled || err.isTerminated))
|| (err instanceof Error && err.name === 'AbortError')
);
export const getStdioString = (stdio: string | Buffer | undefined) => (stdio instanceof Buffer ? stdio.toString('utf8') : stdio); export const getStdioString = (stdio: string | Uint8Array) => (stdio instanceof Uint8Array ? Buffer.from(stdio).toString('utf8') : stdio);
// A bit hacky but it works, unless someone has a file called "No space left on device" ( ͡° ͜ʖ ͡°) // A bit hacky but it works, unless someone has a file called "No space left on device" ( ͡° ͜ʖ ͡°)
export const isOutOfSpaceError = (err: InvariantExecaError) => ( export const isOutOfSpaceError = (err: InvariantExecaError) => (

Wyświetl plik

@ -1,5 +1,5 @@
{ {
"extends": ["@tsconfig/strictest", "@tsconfig/node18/tsconfig.json"], "extends": ["@tsconfig/strictest", "@tsconfig/node20/tsconfig.json"],
"compilerOptions": { "compilerOptions": {
"noEmit": true, "noEmit": true,
}, },

156
yarn.lock
Wyświetl plik

@ -1804,6 +1804,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@sindresorhus/merge-streams@npm:^4.0.0":
version: 4.0.0
resolution: "@sindresorhus/merge-streams@npm:4.0.0"
checksum: 10/16551c787f5328c8ef05fd9831ade64369ccc992df78deb635ec6c44af217d2f1b43f8728c348cdc4e00585ff2fad6e00d8155199cbf6b154acc45fe65cbf0aa
languageName: node
linkType: hard
"@szmarczak/http-timer@npm:^4.0.5": "@szmarczak/http-timer@npm:^4.0.5":
version: 4.0.6 version: 4.0.6
resolution: "@szmarczak/http-timer@npm:4.0.6" resolution: "@szmarczak/http-timer@npm:4.0.6"
@ -1843,6 +1850,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@tsconfig/node20@npm:^20.1.4":
version: 20.1.4
resolution: "@tsconfig/node20@npm:20.1.4"
checksum: 10/345dba8074647f6c11b8d78afa76d9c16e3436cb56a8e78fe2060014d33a09f3f4fd6ed81dc90e955d3509f926cd7fd61c6ddfd3d5a1d80758d7844f7cc3a99e
languageName: node
linkType: hard
"@tsconfig/strictest@npm:^2.0.2": "@tsconfig/strictest@npm:^2.0.2":
version: 2.0.2 version: 2.0.2
resolution: "@tsconfig/strictest@npm:2.0.2" resolution: "@tsconfig/strictest@npm:2.0.2"
@ -2172,12 +2186,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/node@npm:18": "@types/node@npm:20":
version: 18.19.21 version: 20.17.9
resolution: "@types/node@npm:18.19.21" resolution: "@types/node@npm:20.17.9"
dependencies: dependencies:
undici-types: "npm:~5.26.4" undici-types: "npm:~6.19.2"
checksum: 10/3a5c5841f294bc35b5b416a32764b5c0c2f22f4cef48cb7d2e3b4e068a52d5857e50da8e6e0685e743127c70344301c833849a3904ce3bd3f67448da5e85487a checksum: 10/11604a47adf383892394a59a339136b2746a71addf4a3b13f661d23b6e81e8a4f3b35e69dbcffc94698368e5ab5ec056a43a86c87eff00b1b8ea8cfcbfe641df
languageName: node languageName: node
linkType: hard linkType: hard
@ -5430,6 +5444,26 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"execa@npm:^9.5.1":
version: 9.5.1
resolution: "execa@npm:9.5.1"
dependencies:
"@sindresorhus/merge-streams": "npm:^4.0.0"
cross-spawn: "npm:^7.0.3"
figures: "npm:^6.1.0"
get-stream: "npm:^9.0.0"
human-signals: "npm:^8.0.0"
is-plain-obj: "npm:^4.1.0"
is-stream: "npm:^4.0.1"
npm-run-path: "npm:^6.0.0"
pretty-ms: "npm:^9.0.0"
signal-exit: "npm:^4.1.0"
strip-final-newline: "npm:^4.0.0"
yoctocolors: "npm:^2.0.0"
checksum: 10/aa030cdd43ffbf6a8825c16eec1515729553ce3655a8fa5165f0ddab2320957a9783effbeff37662e238e6f5d979d9732e3baa4bcaaeba4360856e627a214177
languageName: node
linkType: hard
"expand-template@npm:^2.0.3": "expand-template@npm:^2.0.3":
version: 2.0.3 version: 2.0.3
resolution: "expand-template@npm:2.0.3" resolution: "expand-template@npm:2.0.3"
@ -5622,6 +5656,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"figures@npm:^6.1.0":
version: 6.1.0
resolution: "figures@npm:6.1.0"
dependencies:
is-unicode-supported: "npm:^2.0.0"
checksum: 10/9822d13630bee8e6a9f2da866713adf13854b07e0bfde042defa8bba32d47a1c0b2afa627ce73837c674cf9a5e3edce7e879ea72cb9ea7960b2390432d8e1167
languageName: node
linkType: hard
"file-entry-cache@npm:^6.0.1": "file-entry-cache@npm:^6.0.1":
version: 6.0.1 version: 6.0.1
resolution: "file-entry-cache@npm:6.0.1" resolution: "file-entry-cache@npm:6.0.1"
@ -6040,7 +6083,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"get-stream@npm:^9.0.1": "get-stream@npm:^9.0.0, get-stream@npm:^9.0.1":
version: 9.0.1 version: 9.0.1
resolution: "get-stream@npm:9.0.1" resolution: "get-stream@npm:9.0.1"
dependencies: dependencies:
@ -6516,6 +6559,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"human-signals@npm:^8.0.0":
version: 8.0.0
resolution: "human-signals@npm:8.0.0"
checksum: 10/89acdc7081ac2a065e41cca7351c4b0fe2382e213b7372f90df6a554e340f31b49388a307adc1d6f4c60b2b4fe81eeff0bc1f44be6f5d844311cd92ccc7831c6
languageName: node
linkType: hard
"humanize-ms@npm:^1.2.1": "humanize-ms@npm:^1.2.1":
version: 1.2.1 version: 1.2.1
resolution: "humanize-ms@npm:1.2.1" resolution: "humanize-ms@npm:1.2.1"
@ -7016,7 +7066,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"is-plain-obj@npm:^4.0.0": "is-plain-obj@npm:^4.0.0, is-plain-obj@npm:^4.1.0":
version: 4.1.0 version: 4.1.0
resolution: "is-plain-obj@npm:4.1.0" resolution: "is-plain-obj@npm:4.1.0"
checksum: 10/6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce checksum: 10/6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce
@ -7111,6 +7161,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"is-unicode-supported@npm:^2.0.0":
version: 2.1.0
resolution: "is-unicode-supported@npm:2.1.0"
checksum: 10/f254e3da6b0ab1a57a94f7273a7798dd35d1d45b227759f600d0fa9d5649f9c07fa8d3c8a6360b0e376adf916d151ec24fc9a50c5295c58bae7ca54a76a063f9
languageName: node
linkType: hard
"is-valid-glob@npm:^1.0.0": "is-valid-glob@npm:^1.0.0":
version: 1.0.0 version: 1.0.0
resolution: "is-valid-glob@npm:1.0.0" resolution: "is-valid-glob@npm:1.0.0"
@ -7605,6 +7662,7 @@ __metadata:
"@radix-ui/react-checkbox": "npm:^1.0.4" "@radix-ui/react-checkbox": "npm:^1.0.4"
"@radix-ui/react-switch": "npm:^1.0.1" "@radix-ui/react-switch": "npm:^1.0.1"
"@tsconfig/node18": "npm:^18.2.2" "@tsconfig/node18": "npm:^18.2.2"
"@tsconfig/node20": "npm:^20.1.4"
"@tsconfig/strictest": "npm:^2.0.2" "@tsconfig/strictest": "npm:^2.0.2"
"@tsconfig/vite-react": "npm:^3.0.0" "@tsconfig/vite-react": "npm:^3.0.0"
"@types/color": "npm:^3.0.6" "@types/color": "npm:^3.0.6"
@ -7618,7 +7676,7 @@ __metadata:
"@types/mime-types": "npm:^2.1.4" "@types/mime-types": "npm:^2.1.4"
"@types/morgan": "npm:^1.9.9" "@types/morgan": "npm:^1.9.9"
"@types/mousetrap": "npm:^1.6.15" "@types/mousetrap": "npm:^1.6.15"
"@types/node": "npm:18" "@types/node": "npm:20"
"@types/react": "npm:^18.2.66" "@types/react": "npm:^18.2.66"
"@types/react-dom": "npm:^18.2.22" "@types/react-dom": "npm:^18.2.22"
"@types/sortablejs": "npm:^1.15.0" "@types/sortablejs": "npm:^1.15.0"
@ -7647,7 +7705,7 @@ __metadata:
eslint-plugin-react-hooks: "npm:^4.3.0" eslint-plugin-react-hooks: "npm:^4.3.0"
eslint-plugin-unicorn: "npm:^51.0.1" eslint-plugin-unicorn: "npm:^51.0.1"
evergreen-ui: "npm:^6.13.1" evergreen-ui: "npm:^6.13.1"
execa: "npm:^8.0.1" execa: "npm:^9.5.1"
express: "npm:^4.20.0" express: "npm:^4.20.0"
express-async-handler: "npm:^1.2.0" express-async-handler: "npm:^1.2.0"
fast-xml-parser: "npm:^4.4.1" fast-xml-parser: "npm:^4.4.1"
@ -7698,7 +7756,7 @@ __metadata:
tiny-invariant: "npm:^1.3.3" tiny-invariant: "npm:^1.3.3"
tsx: "npm:^4.7.1" tsx: "npm:^4.7.1"
type-fest: "npm:^4.23.0" type-fest: "npm:^4.23.0"
typescript: "npm:^5.5.4" typescript: "npm:^5.7.2"
use-debounce: "npm:^5.1.0" use-debounce: "npm:^5.1.0"
use-trace-update: "npm:^1.3.0" use-trace-update: "npm:^1.3.0"
vite: "npm:^5.3.6" vite: "npm:^5.3.6"
@ -8354,6 +8412,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"npm-run-path@npm:^6.0.0":
version: 6.0.0
resolution: "npm-run-path@npm:6.0.0"
dependencies:
path-key: "npm:^4.0.0"
unicorn-magic: "npm:^0.3.0"
checksum: 10/1a1b50aba6e6af7fd34a860ba2e252e245c4a59b316571a990356417c0cdf0414cabf735f7f52d9c330899cb56f0ab804a8e21fb12a66d53d7843e39ada4a3b6
languageName: node
linkType: hard
"npmlog@npm:^6.0.0": "npmlog@npm:^6.0.0":
version: 6.0.2 version: 6.0.2
resolution: "npmlog@npm:6.0.2" resolution: "npmlog@npm:6.0.2"
@ -8676,6 +8744,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"parse-ms@npm:^4.0.0":
version: 4.0.0
resolution: "parse-ms@npm:4.0.0"
checksum: 10/673c801d9f957ff79962d71ed5a24850163f4181a90dd30c4e3666b3a804f53b77f1f0556792e8b2adbb5d58757907d1aa51d7d7dc75997c2a56d72937cbc8b7
languageName: node
linkType: hard
"parse5-htmlparser2-tree-adapter@npm:^7.0.0": "parse5-htmlparser2-tree-adapter@npm:^7.0.0":
version: 7.0.0 version: 7.0.0
resolution: "parse5-htmlparser2-tree-adapter@npm:7.0.0" resolution: "parse5-htmlparser2-tree-adapter@npm:7.0.0"
@ -8912,6 +8987,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"pretty-ms@npm:^9.0.0":
version: 9.2.0
resolution: "pretty-ms@npm:9.2.0"
dependencies:
parse-ms: "npm:^4.0.0"
checksum: 10/a65a1d81560867f4f7128862fdbf0e1c2d3c5607bf75cae7758bf8111e2c4b744be46e084704125a38ba918bb43defa7a53aaff0f48c5c2d95367d3148c980d9
languageName: node
linkType: hard
"prismjs@npm:^1.25.0": "prismjs@npm:^1.25.0":
version: 1.26.0 version: 1.26.0
resolution: "prismjs@npm:1.26.0" resolution: "prismjs@npm:1.26.0"
@ -10483,6 +10567,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"strip-final-newline@npm:^4.0.0":
version: 4.0.0
resolution: "strip-final-newline@npm:4.0.0"
checksum: 10/b5fe48f695d74863153a3b3155220e6e9bf51f4447832998c8edec38e6559b3af87a9fe5ac0df95570a78a26f5fa91701358842eab3c15480e27980b154a145f
languageName: node
linkType: hard
"strip-indent@npm:^3.0.0": "strip-indent@npm:^3.0.0":
version: 3.0.0 version: 3.0.0
resolution: "strip-indent@npm:3.0.0" resolution: "strip-indent@npm:3.0.0"
@ -11068,7 +11159,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@npm:^5.0.4, typescript@npm:^5.5.4": "typescript@npm:^5.0.4":
version: 5.5.4 version: 5.5.4
resolution: "typescript@npm:5.5.4" resolution: "typescript@npm:5.5.4"
bin: bin:
@ -11078,6 +11169,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@npm:^5.7.2":
version: 5.7.2
resolution: "typescript@npm:5.7.2"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10/4caa3904df69db9d4a8bedc31bafc1e19ffb7b24fbde2997a1633ae1398d0de5bdbf8daf602ccf3b23faddf1aeeb9b795223a2ed9c9a4fdcaf07bfde114a401a
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A^4.0.2#optional!builtin<compat/typescript>": "typescript@patch:typescript@npm%3A^4.0.2#optional!builtin<compat/typescript>":
version: 4.9.5 version: 4.9.5
resolution: "typescript@patch:typescript@npm%3A4.9.5#optional!builtin<compat/typescript>::version=4.9.5&hash=289587" resolution: "typescript@patch:typescript@npm%3A4.9.5#optional!builtin<compat/typescript>::version=4.9.5&hash=289587"
@ -11088,7 +11189,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.5.4#optional!builtin<compat/typescript>": "typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>":
version: 5.5.4 version: 5.5.4
resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin<compat/typescript>::version=5.5.4&hash=379a07" resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin<compat/typescript>::version=5.5.4&hash=379a07"
bin: bin:
@ -11098,6 +11199,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@patch:typescript@npm%3A^5.7.2#optional!builtin<compat/typescript>":
version: 5.7.2
resolution: "typescript@patch:typescript@npm%3A5.7.2#optional!builtin<compat/typescript>::version=5.7.2&hash=74658d"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10/ff27fc124bceb8969be722baa38af945b2505767cf794de3e2715e58f61b43780284060287d651fcbbdfb6f917f4653b20f4751991f17e0706db389b9bb3f75d
languageName: node
linkType: hard
"ua-parser-js@npm:^0.7.30": "ua-parser-js@npm:^0.7.30":
version: 0.7.33 version: 0.7.33
resolution: "ua-parser-js@npm:0.7.33" resolution: "ua-parser-js@npm:0.7.33"
@ -11154,6 +11265,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"undici-types@npm:~6.19.2":
version: 6.19.8
resolution: "undici-types@npm:6.19.8"
checksum: 10/cf0b48ed4fc99baf56584afa91aaffa5010c268b8842f62e02f752df209e3dea138b372a60a963b3b2576ed932f32329ce7ddb9cb5f27a6c83040d8cd74b7a70
languageName: node
linkType: hard
"unicorn-magic@npm:^0.3.0":
version: 0.3.0
resolution: "unicorn-magic@npm:0.3.0"
checksum: 10/bdd7d7c522f9456f32a0b77af23f8854f9a7db846088c3868ec213f9550683ab6a2bdf3803577eacbafddb4e06900974385841ccb75338d17346ccef45f9cb01
languageName: node
linkType: hard
"unique-filename@npm:^2.0.0": "unique-filename@npm:^2.0.0":
version: 2.0.1 version: 2.0.1
resolution: "unique-filename@npm:2.0.1" resolution: "unique-filename@npm:2.0.1"
@ -11761,6 +11886,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"yoctocolors@npm:^2.0.0":
version: 2.1.1
resolution: "yoctocolors@npm:2.1.1"
checksum: 10/563fbec88bce9716d1044bc98c96c329e1d7a7c503e6f1af68f1ff914adc3ba55ce953c871395e2efecad329f85f1632f51a99c362032940321ff80c42a6f74d
languageName: node
linkType: hard
"zod@npm:^3.22.5": "zod@npm:^3.22.5":
version: 3.22.5 version: 3.22.5
resolution: "zod@npm:3.22.5" resolution: "zod@npm:3.22.5"