FileHandle.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.FileHandle = void 0;
  4. const util_1 = require("./util");
  5. const node_events_1 = require("node:events");
  6. class FileHandle extends node_events_1.EventEmitter {
  7. constructor(fs, fd) {
  8. super();
  9. this.refs = 1;
  10. this.closePromise = null;
  11. this.position = 0;
  12. this.readableWebStreamLocked = false;
  13. this.fs = fs;
  14. this.fd = fd;
  15. }
  16. getAsyncId() {
  17. // Return a unique async ID for this file handle
  18. // In a real implementation, this would be provided by the underlying system
  19. return this.fd;
  20. }
  21. appendFile(data, options) {
  22. return (0, util_1.promisify)(this.fs, 'appendFile')(this.fd, data, options);
  23. }
  24. chmod(mode) {
  25. return (0, util_1.promisify)(this.fs, 'fchmod')(this.fd, mode);
  26. }
  27. chown(uid, gid) {
  28. return (0, util_1.promisify)(this.fs, 'fchown')(this.fd, uid, gid);
  29. }
  30. close() {
  31. if (this.fd === -1) {
  32. return Promise.resolve();
  33. }
  34. if (this.closePromise) {
  35. return this.closePromise;
  36. }
  37. this.refs--;
  38. if (this.refs === 0) {
  39. const currentFd = this.fd;
  40. this.fd = -1;
  41. this.closePromise = (0, util_1.promisify)(this.fs, 'close')(currentFd).finally(() => {
  42. this.closePromise = null;
  43. });
  44. }
  45. else {
  46. this.closePromise = new Promise((resolve, reject) => {
  47. this.closeResolve = resolve;
  48. this.closeReject = reject;
  49. }).finally(() => {
  50. this.closePromise = null;
  51. this.closeReject = undefined;
  52. this.closeResolve = undefined;
  53. });
  54. }
  55. this.emit('close');
  56. return this.closePromise;
  57. }
  58. datasync() {
  59. return (0, util_1.promisify)(this.fs, 'fdatasync')(this.fd);
  60. }
  61. createReadStream(options) {
  62. return this.fs.createReadStream('', { ...options, fd: this });
  63. }
  64. createWriteStream(options) {
  65. return this.fs.createWriteStream('', { ...options, fd: this });
  66. }
  67. readableWebStream(options = {}) {
  68. const { type = 'bytes', autoClose = false } = options;
  69. let position = 0;
  70. if (this.fd === -1) {
  71. throw new Error('The FileHandle is closed');
  72. }
  73. if (this.closePromise) {
  74. throw new Error('The FileHandle is closing');
  75. }
  76. if (this.readableWebStreamLocked) {
  77. throw new Error('An error will be thrown if this method is called more than once or is called after the FileHandle is closed or closing.');
  78. }
  79. this.readableWebStreamLocked = true;
  80. this.ref();
  81. const unlockAndCleanup = () => {
  82. this.readableWebStreamLocked = false;
  83. this.unref();
  84. if (autoClose) {
  85. this.close().catch(() => {
  86. // Ignore close errors in cleanup
  87. });
  88. }
  89. };
  90. return new ReadableStream({
  91. type: type === 'bytes' ? 'bytes' : undefined,
  92. autoAllocateChunkSize: 16384,
  93. pull: async (controller) => {
  94. try {
  95. const view = controller.byobRequest?.view;
  96. if (!view) {
  97. // Fallback for when BYOB is not available
  98. const buffer = new Uint8Array(16384);
  99. const result = await this.read(buffer, 0, buffer.length, position);
  100. if (result.bytesRead === 0) {
  101. controller.close();
  102. unlockAndCleanup();
  103. return;
  104. }
  105. position += result.bytesRead;
  106. controller.enqueue(buffer.slice(0, result.bytesRead));
  107. return;
  108. }
  109. const result = await this.read(view, view.byteOffset, view.byteLength, position);
  110. if (result.bytesRead === 0) {
  111. controller.close();
  112. unlockAndCleanup();
  113. return;
  114. }
  115. position += result.bytesRead;
  116. controller.byobRequest.respond(result.bytesRead);
  117. }
  118. catch (error) {
  119. controller.error(error);
  120. unlockAndCleanup();
  121. }
  122. },
  123. cancel: async () => {
  124. unlockAndCleanup();
  125. },
  126. });
  127. }
  128. async read(buffer, offset, length, position) {
  129. const readPosition = position !== null && position !== undefined ? position : this.position;
  130. const result = await (0, util_1.promisify)(this.fs, 'read', bytesRead => ({ bytesRead, buffer }))(this.fd, buffer, offset, length, readPosition);
  131. // Update internal position only if position was null/undefined
  132. if (position === null || position === undefined) {
  133. this.position += result.bytesRead;
  134. }
  135. return result;
  136. }
  137. readv(buffers, position) {
  138. return (0, util_1.promisify)(this.fs, 'readv', bytesRead => ({ bytesRead, buffers }))(this.fd, buffers, position);
  139. }
  140. readFile(options) {
  141. return (0, util_1.promisify)(this.fs, 'readFile')(this.fd, options);
  142. }
  143. stat(options) {
  144. return (0, util_1.promisify)(this.fs, 'fstat')(this.fd, options);
  145. }
  146. sync() {
  147. return (0, util_1.promisify)(this.fs, 'fsync')(this.fd);
  148. }
  149. truncate(len) {
  150. return (0, util_1.promisify)(this.fs, 'ftruncate')(this.fd, len);
  151. }
  152. utimes(atime, mtime) {
  153. return (0, util_1.promisify)(this.fs, 'futimes')(this.fd, atime, mtime);
  154. }
  155. async write(buffer, offset, length, position) {
  156. const useInternalPosition = typeof position !== 'number';
  157. const writePosition = useInternalPosition ? this.position : position;
  158. const result = await (0, util_1.promisify)(this.fs, 'write', bytesWritten => ({ bytesWritten, buffer }))(this.fd, buffer, offset, length, writePosition);
  159. // Update internal position only if position was null/undefined
  160. if (useInternalPosition) {
  161. this.position += result.bytesWritten;
  162. }
  163. return result;
  164. }
  165. writev(buffers, position) {
  166. return (0, util_1.promisify)(this.fs, 'writev', bytesWritten => ({ bytesWritten, buffers }))(this.fd, buffers, position);
  167. }
  168. writeFile(data, options) {
  169. return (0, util_1.promisify)(this.fs, 'writeFile')(this.fd, data, options);
  170. }
  171. // Implement Symbol.asyncDispose if available (ES2023+)
  172. async [Symbol.asyncDispose]() {
  173. await this.close();
  174. }
  175. ref() {
  176. this.refs++;
  177. }
  178. unref() {
  179. this.refs--;
  180. if (this.refs === 0) {
  181. this.fd = -1;
  182. if (this.closeResolve) {
  183. (0, util_1.promisify)(this.fs, 'close')(this.fd).then(this.closeResolve, this.closeReject);
  184. }
  185. }
  186. }
  187. }
  188. exports.FileHandle = FileHandle;
  189. //# sourceMappingURL=FileHandle.js.map