fix(desktop): recover from macOS directory permission failures

This commit is contained in:
deatheros
2026-08-07 01:49:44 +03:00
parent 7e0e22f6e2
commit d8518bf053
30 changed files with 497 additions and 106 deletions
@@ -0,0 +1,28 @@
import { describe, expect, test } from 'bun:test';
import {
FilesystemError,
isFilesystemError,
parseFilesystemErrorReason,
} from './files-errors';
describe('FilesystemError', () => {
test('retains a stable reason and HTTP status', () => {
const error = new FilesystemError('Access denied', {
reason: 'os-permission',
status: 403,
});
expect(isFilesystemError(error)).toBe(true);
expect(error.name).toBe('FilesystemError');
expect(error.message).toBe('Access denied');
expect(error.reason).toBe('os-permission');
expect(error.status).toBe(403);
});
test('normalizes unsupported response reasons to unknown', () => {
expect(parseFilesystemErrorReason('os-permission')).toBe('os-permission');
expect(parseFilesystemErrorReason('made-up')).toBe('unknown');
expect(parseFilesystemErrorReason(undefined)).toBe('unknown');
});
});
+40
View File
@@ -0,0 +1,40 @@
export type FilesystemErrorReason =
| 'os-permission'
| 'not-found'
| 'not-directory'
| 'invalid-response'
| 'unknown';
export class FilesystemError extends Error {
readonly reason: FilesystemErrorReason;
readonly status?: number;
constructor(message: string, options: { reason?: FilesystemErrorReason; status?: number } = {}) {
super(message);
this.name = 'FilesystemError';
this.reason = options.reason ?? 'unknown';
this.status = options.status;
}
}
export const isFilesystemError = (error: unknown): error is FilesystemError => (
error instanceof FilesystemError
|| Boolean(
error
&& typeof error === 'object'
&& 'reason' in error
&& typeof (error as { reason?: unknown }).reason === 'string'
)
);
export const parseFilesystemErrorReason = (value: unknown): FilesystemErrorReason => {
switch (value) {
case 'os-permission':
case 'not-found':
case 'not-directory':
case 'invalid-response':
return value;
default:
return 'unknown';
}
};