Repository: streamich/memfs
Pull request #1083 (2024-12-31 03:35:22)

feat: support `UInt8Array` in place of `Buffer`
This seems reasonable, considering memfs supports the browser, and users are more likely to have instances of Uint8Array there.

Resolves https://github.com/streamich/memfs/issues/733

G-Rath: I feel like this probably is a fix for #733 - would you mind double checking that for me, and throwing in a test or two as/when you think is needed? (if you don't think it's worth having any, I'm ok with that too 🙂)
kylecarbs: @G-Rath seems to fix #733! I added tests before, but they don't do much - the structure is the same for Uint8Array. I'm happy to add it if you'd prefer, though!
G-Rath: Awesome! Mind sticking a "resolves" line to your description?

Re tests it doesn't surprise me but would still be good if we had at least one where were explicitly passing in a 8uintarry
kylecarbs: @G-Rath added to the description! I can add one before merge - no biggie.
kylecarbs: @G-Rath fixed. I also removed a `debugger;` line that was causing me some issues, just assuming that's fine.
kylecarbs: Seems like there are some unrelated flakes failing - but test I added should be good.
G-Rath: >  I also removed a debugger;

Yeah that should never have been landed - I've opened #1086 to remove it and another from a test, just so we've got a dedicated changelog entry as it's a whole bug of its own

> Seems like there are some unrelated flakes failing

A couple of the watch ones are very slightly flakey, but thankfully only slightly so yes after rerunning you've got green across the board 🎉 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b245a4c..6bcb68df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## [4.15.4](https://github.com/streamich/memfs/compare/v4.15.3...v4.15.4) (2025-01-09)
+
+
+### Bug Fixes
+
+* remove `debugger` statement ([#1086](https://github.com/streamich/memfs/issues/1086)) ([6489172](https://github.com/streamich/memfs/commit/648917202d0832908ec57b72400f4c772e15a624))
+
 ## [4.15.3](https://github.com/streamich/memfs/compare/v4.15.2...v4.15.3) (2025-01-01)
 
 
diff --git a/package.json b/package.json
index 442fd1ca..ecdd92b6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "memfs",
-  "version": "4.15.3",
+  "version": "4.15.4",
   "description": "In-memory file-system with Node's fs API.",
   "keywords": [
     "fs",
diff --git a/src/node/constants.ts b/src/node/constants.ts
index 3efb05e4..8105c54f 100644
--- a/src/node/constants.ts
+++ b/src/node/constants.ts
@@ -8,7 +8,7 @@ export const enum MODE {
 }
 
 export const ERRSTR = {
-  PATH_STR: 'path must be a string or Buffer',
+  PATH_STR: 'path must be a string, Buffer, or Uint8Array',
   // FD:             'file descriptor must be a unsigned 32-bit integer',
   FD: 'fd must be a file descriptor',
   MODE_INT: 'mode must be an int',
diff --git a/src/node/types/misc.ts b/src/node/types/misc.ts
index 36f5b5a0..c0d6615d 100644
--- a/src/node/types/misc.ts
+++ b/src/node/types/misc.ts
@@ -1,4 +1,4 @@
-import type { PathLike, symlink } from 'fs';
+import type { PathLike as NodePathLike, symlink } from 'fs';
 import type { constants } from '../../constants';
 import type { EventEmitter } from 'events';
 import type { TSetTimeout } from '../../setTimeoutUnref';
@@ -13,8 +13,9 @@ import type {
 } from './options';
 import type { Readable, Writable } from 'stream';
 
-export { PathLike, symlink };
+export { symlink };
 
+export type PathLike = NodePathLike | Uint8Array; // For browser support we add Uint8Array.
 export type TDataOut = string | Buffer; // Data formats we give back to users.
 export type TEncodingExtended = BufferEncoding | 'buffer';
 export type TFileId = PathLike | number; // Number is used as a file descriptor.
diff --git a/src/node/util.ts b/src/node/util.ts
index 09e6c682..c4976516 100644
--- a/src/node/util.ts
+++ b/src/node/util.ts
@@ -72,6 +72,9 @@ function getPathFromURLPosix(url): string {
 }
 
 export function pathToFilename(path: misc.PathLike): string {
+  if (path instanceof Uint8Array) {
+    path = bufferFrom(path);
+  }
   if (typeof path !== 'string' && !Buffer.isBuffer(path)) {
     try {
       if (!(path instanceof require('url').URL)) throw new TypeError(ERRSTR.PATH_STR);
diff --git a/src/volume.ts b/src/volume.ts
index 92972a6a..93d135f2 100644
--- a/src/volume.ts
+++ b/src/volume.ts
@@ -58,9 +58,8 @@ import {
   getWriteSyncArgs,
   unixify,
 } from './node/util';
-import type { PathLike, symlink } from 'fs';
+import type { PathLike, symlink } from './node/types/misc';
 import type { FsPromisesApi, FsSynchronousApi } from './node/types';
-import { fsSynchronousApiList } from './node/lists/fsSynchronousApiList';
 import { Dir } from './Dir';
 
 const resolveCrossPlatform = pathModule.resolve;
@@ -1327,7 +1326,6 @@ export class Volume implements FsCallbackApi, FsSynchronousApi {
   }
 
   private realpathBase(filename: string, encoding: TEncodingExtended | undefined): TDataOut {
-    debugger;
     const realLink = this.getResolvedLinkOrThrow(filename, 'realpath');
 
     return strToEncoding(realLink.getPath() || '/', encoding);


Test patch:
diff --git a/src/__tests__/volume.test.ts b/src/__tests__/volume.test.ts
index 461b03a0..2ecd4843 100644
--- a/src/__tests__/volume.test.ts
+++ b/src/__tests__/volume.test.ts
@@ -390,6 +390,12 @@ describe('volume', () => {
         expect(fd).toBeGreaterThan(0);
         expect(oldMtime).not.toBe(newMtime);
       });
+      it('Create new file with Uint8Array path', () => {
+        const path = new TextEncoder().encode('/test.txt');
+        const fd = vol.openSync(path, 'w');
+        expect(typeof fd).toBe('number');
+        expect(fd).toBeGreaterThan(0);
+      });
       it('Error on file not found', () => {
         try {
           vol.openSync('/non-existing-file.txt', 'r');
@@ -404,7 +410,7 @@ describe('volume', () => {
           throw Error('This should not throw');
         } catch (err) {
           expect(err).toBeInstanceOf(TypeError);
-          expect(err.message).toBe('path must be a string or Buffer');
+          expect(err.message).toBe('path must be a string, Buffer, or Uint8Array');
         }
       });
       it('Invalid flags correct error code', () => {
@@ -477,7 +483,7 @@ describe('volume', () => {
           throw Error('This should not throw');
         } catch (err) {
           expect(err).toBeInstanceOf(TypeError);
-          expect(err.message).toBe('path must be a string or Buffer');
+          expect(err.message).toBe('path must be a string, Buffer, or Uint8Array');
           done();
         }
       });
diff --git a/src/__tests__/volume/__snapshots__/renameSync.test.ts.snap b/src/__tests__/volume/__snapshots__/renameSync.test.ts.snap
index 60e16bdf..e89ab86a 100644
--- a/src/__tests__/volume/__snapshots__/renameSync.test.ts.snap
+++ b/src/__tests__/volume/__snapshots__/renameSync.test.ts.snap
@@ -1,7 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`renameSync(fromPath, toPath) Throws if path is of wrong type 1`] = `"path must be a string or Buffer"`;
+exports[`renameSync(fromPath, toPath) Throws if path is of wrong type 1`] = `"path must be a string, Buffer, or Uint8Array"`;
 
-exports[`renameSync(fromPath, toPath) Throws on no params 1`] = `"path must be a string or Buffer"`;
+exports[`renameSync(fromPath, toPath) Throws on no params 1`] = `"path must be a string, Buffer, or Uint8Array"`;
 
-exports[`renameSync(fromPath, toPath) Throws on only one param 1`] = `"path must be a string or Buffer"`;
+exports[`renameSync(fromPath, toPath) Throws on only one param 1`] = `"path must be a string, Buffer, or Uint8Array"`;
diff --git a/src/__tests__/volume/mkdirSync.test.ts b/src/__tests__/volume/mkdirSync.test.ts
index 46d4bf7c..1db611bf 100644
--- a/src/__tests__/volume/mkdirSync.test.ts
+++ b/src/__tests__/volume/mkdirSync.test.ts
@@ -121,7 +121,6 @@ describe('mkdirSync', () => {
     it('throws ENOTDIR when trying to create under something that is not a directory', () => {
       const vol = create({ '/a': 'I am a file' });
       expect(() => {
-        debugger;
         vol.mkdirSync('/a/b/c', { recursive: true });
       }).toThrow(/ENOTDIR/);
     });