diff --git a/src/node-to-fsa/NodeFileSystemSyncAccessHandle.ts b/src/node-to-fsa/NodeFileSystemSyncAccessHandle.ts index ccd9708ba..9ad2df376 100644 --- a/src/node-to-fsa/NodeFileSystemSyncAccessHandle.ts +++ b/src/node-to-fsa/NodeFileSystemSyncAccessHandle.ts @@ -63,6 +63,31 @@ export class NodeFileSystemSyncAccessHandle { public async truncate(newSize: number): Promise { this.fs.truncateSync(this.fd, newSize); } + + /** + * Writes the content of a specified buffer to the file associated with the + * handle, optionally at a given offset. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystemSyncAccessHandle/write + * @param buffer + * @param options + */ + public async write(buffer: ArrayBuffer | ArrayBufferView | DataView, options: FileSystemReadWriteOptions = {}): Promise { + const buf: Buffer | ArrayBufferView = buffer instanceof ArrayBuffer ? Buffer.from(buffer) : buffer; + try { + return this.fs.writeSync(this.fd, buf, 0, buffer.byteLength, options.at || 0); + } catch (error) { + if (error instanceof DOMException) throw error; + if (error && typeof error === 'object') { + switch (error.code) { + case 'EBADF': { + throw new DOMException('File handle already closed.', 'InvalidStateError'); + } + } + } + throw error; + } + } } export interface FileSystemReadWriteOptions { diff --git a/src/node-to-fsa/__tests__/NodeFileSystemSyncAccessHandle.test.ts b/src/node-to-fsa/__tests__/NodeFileSystemSyncAccessHandle.test.ts index 4d4b65ddb..871700963 100644 --- a/src/node-to-fsa/__tests__/NodeFileSystemSyncAccessHandle.test.ts +++ b/src/node-to-fsa/__tests__/NodeFileSystemSyncAccessHandle.test.ts @@ -124,4 +124,60 @@ maybe('NodeFileSystemSyncAccessHandle', () => { expect(res).toBe(undefined); }); }); + + describe('.write()', () => { + test('can write to the file', async () => { + const { dir, fs } = setup({ + 'file.txt': '0123456789', + }); + const entry = await dir.getFileHandle('file.txt'); + const sync = await entry.createSyncAccessHandle!(); + const res = await sync.write(Buffer.from('Hello')); + expect(res).toBe(5); + expect(fs.readFileSync('/file.txt', 'utf8')).toBe('Hello56789'); + }); + + test('can write at an offset', async () => { + const { dir, fs } = setup({ + 'file.txt': '0123456789', + }); + const entry = await dir.getFileHandle('file.txt'); + const sync = await entry.createSyncAccessHandle!(); + const res = await sync.write(Buffer.from('Hello'), {at: 7}); + expect(res).toBe(5); + expect(fs.readFileSync('/file.txt', 'utf8')).toBe('0123456Hello'); + }); + + test('throws "InvalidStateError" DOMException if file descriptor is already closed', async () => { + const { dir, fs } = setup({ + 'file.txt': '0123456789', + }); + const entry = await dir.getFileHandle('file.txt'); + const sync = await entry.createSyncAccessHandle!(); + await sync.write(Buffer.from('a')); + await sync.close(); + try { + await sync.write(Buffer.from('b')); + throw new Error('No error was thrown'); + } catch (error) { + expect(error).toBeInstanceOf(DOMException); + expect(error.name).toBe('InvalidStateError'); + } + }); + + // TODO: Need to find out what is the correct behavior here. + xtest('writing at offset past file size', async () => { + const { dir, fs } = setup({ + 'file.txt': '0123456789', + }); + const entry = await dir.getFileHandle('file.txt'); + const sync = await entry.createSyncAccessHandle!(); + try { + await sync.write(Buffer.from('a'), {at: 100}); + // ? + } catch (error) { + // ? + } + }); + }); });