pwd-fs 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/powered-file-system/append.d.ts +9 -6
  4. package/dist/powered-file-system/append.js +4 -4
  5. package/dist/powered-file-system/chmod.d.ts +4 -4
  6. package/dist/powered-file-system/chmod.js +9 -10
  7. package/dist/powered-file-system/chown.d.ts +7 -4
  8. package/dist/powered-file-system/chown.js +18 -8
  9. package/dist/powered-file-system/copy.d.ts +10 -7
  10. package/dist/powered-file-system/copy.js +11 -12
  11. package/dist/powered-file-system/empty-dir.d.ts +4 -4
  12. package/dist/powered-file-system/empty-dir.js +9 -10
  13. package/dist/powered-file-system/mkdir.d.ts +9 -6
  14. package/dist/powered-file-system/mkdir.js +9 -10
  15. package/dist/powered-file-system/read.d.ts +10 -6
  16. package/dist/powered-file-system/read.js +9 -5
  17. package/dist/powered-file-system/readdir.d.ts +11 -6
  18. package/dist/powered-file-system/readdir.js +8 -6
  19. package/dist/powered-file-system/readlink.d.ts +9 -4
  20. package/dist/powered-file-system/readlink.js +8 -6
  21. package/dist/powered-file-system/realpath.d.ts +9 -4
  22. package/dist/powered-file-system/realpath.js +8 -6
  23. package/dist/powered-file-system/remove.d.ts +5 -5
  24. package/dist/powered-file-system/remove.js +11 -20
  25. package/dist/powered-file-system/rename.d.ts +5 -5
  26. package/dist/powered-file-system/rename.js +11 -7
  27. package/dist/powered-file-system/stat.d.ts +4 -4
  28. package/dist/powered-file-system/stat.js +8 -6
  29. package/dist/powered-file-system/symlink.d.ts +4 -4
  30. package/dist/powered-file-system/symlink.js +11 -4
  31. package/dist/powered-file-system/test.d.ts +10 -5
  32. package/dist/powered-file-system/test.js +8 -5
  33. package/dist/powered-file-system/write.d.ts +10 -7
  34. package/dist/powered-file-system/write.js +9 -6
  35. package/dist/powered-file-system.d.ts +103 -56
  36. package/dist/powered-file-system.js +5 -48
  37. package/dist/powered-file-system.test.js +25 -0
  38. package/dist/recurse-io-sync.d.ts +1 -1
  39. package/dist/recurse-io-sync.js +11 -25
  40. package/dist/recurse-io.d.ts +1 -1
  41. package/dist/recurse-io.js +31 -49
  42. package/dist/suite.test.js +1 -1
  43. package/package.json +11 -16
  44. package/readme.md +30 -11
@@ -1,7 +1,7 @@
1
- import type { PoweredFileSystem, Stats } from '../powered-file-system';
1
+ import type { AsyncOption, MaybeSyncOption, PoweredFileSystem, Stats, SyncOption } from '../powered-file-system';
2
2
  /**
3
3
  * Returns `lstat` data so symlinks are reported as links instead of followed targets.
4
4
  */
5
- export declare function stat<T extends boolean = false>(this: PoweredFileSystem, src: string, options?: {
6
- sync?: T;
7
- }): T extends true ? Stats : Promise<Stats>;
5
+ export declare function stat(this: PoweredFileSystem, src: string, options: SyncOption): Stats;
6
+ export declare function stat(this: PoweredFileSystem, src: string, options?: AsyncOption): Promise<Stats>;
7
+ export declare function stat(this: PoweredFileSystem, src: string, options?: MaybeSyncOption): Stats | Promise<Stats>;
@@ -5,17 +5,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.stat = stat;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
- const node_path_1 = __importDefault(require("node:path"));
9
- /**
10
- * Returns `lstat` data so symlinks are reported as links instead of followed targets.
11
- */
12
8
  function stat(src, options) {
13
9
  const { sync = false } = options ?? {};
14
- src = node_path_1.default.resolve(this.pwd, src);
15
10
  if (sync) {
16
- return node_fs_1.default.lstatSync(src);
11
+ return node_fs_1.default.lstatSync(this.resolve(src));
17
12
  }
18
13
  return new Promise((resolve, reject) => {
14
+ try {
15
+ src = this.resolve(src);
16
+ }
17
+ catch (err) {
18
+ reject(err);
19
+ return;
20
+ }
19
21
  node_fs_1.default.lstat(src, (err, stats) => {
20
22
  if (err) {
21
23
  return reject(err);
@@ -1,4 +1,4 @@
1
- import type { PoweredFileSystem } from '../powered-file-system';
2
- export declare function symlink<T extends boolean = false>(this: PoweredFileSystem, src: string, dest: string, options?: {
3
- sync?: T;
4
- }): T extends true ? void : Promise<void>;
1
+ import type { AsyncOption, MaybeSyncOption, PoweredFileSystem, SyncOption } from '../powered-file-system';
2
+ export declare function symlink(this: PoweredFileSystem, src: string, dest: string, options: SyncOption): void;
3
+ export declare function symlink(this: PoweredFileSystem, src: string, dest: string, options?: AsyncOption): Promise<void>;
4
+ export declare function symlink(this: PoweredFileSystem, src: string, dest: string, options?: MaybeSyncOption): void | Promise<void>;
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.symlink = symlink;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
- const node_path_1 = __importDefault(require("node:path"));
9
8
  /**
10
9
  * Windows requires an explicit link type. Non-Windows platforms infer it.
11
10
  */
@@ -17,15 +16,23 @@ function resolveSymlinkType(src) {
17
16
  return stats.isDirectory() ? 'junction' : 'file';
18
17
  }
19
18
  function symlink(src, dest, options) {
20
- src = node_path_1.default.resolve(this.pwd, src);
21
- dest = node_path_1.default.resolve(this.pwd, dest);
22
19
  const { sync = false } = options ?? {};
23
20
  if (sync) {
21
+ src = this.resolve(src);
22
+ dest = this.resolve(dest);
24
23
  const type = resolveSymlinkType(src);
25
24
  node_fs_1.default.symlinkSync(src, dest, type);
26
- return undefined;
25
+ return;
27
26
  }
28
27
  return new Promise((resolve, reject) => {
28
+ try {
29
+ src = this.resolve(src);
30
+ dest = this.resolve(dest);
31
+ }
32
+ catch (err) {
33
+ reject(err);
34
+ return;
35
+ }
29
36
  if (process.platform === 'win32') {
30
37
  node_fs_1.default.lstat(src, (err, stats) => {
31
38
  if (err) {
@@ -1,8 +1,13 @@
1
- import type { Mode, PoweredFileSystem } from '../powered-file-system';
1
+ import type { AsyncOption, MaybeSyncOption, Mode, PoweredFileSystem, SyncOption } from '../powered-file-system';
2
2
  /**
3
- * Thin wrapper around `fs.access` that resolves paths against the instance root.
3
+ * Thin wrapper around `fs.access` that resolves paths against the instance base path.
4
4
  */
5
- export declare function test<T extends boolean = false>(this: PoweredFileSystem, src: string, options?: {
6
- sync?: T;
5
+ export declare function test(this: PoweredFileSystem, src: string, options: SyncOption & {
7
6
  flag?: Mode;
8
- }): T extends true ? boolean : Promise<boolean>;
7
+ }): boolean;
8
+ export declare function test(this: PoweredFileSystem, src: string, options?: AsyncOption & {
9
+ flag?: Mode;
10
+ }): Promise<boolean>;
11
+ export declare function test(this: PoweredFileSystem, src: string, options?: MaybeSyncOption & {
12
+ flag?: Mode;
13
+ }): boolean | Promise<boolean>;
@@ -5,16 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.test = test;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
- const node_path_1 = __importDefault(require("node:path"));
9
- /**
10
- * Thin wrapper around `fs.access` that resolves paths against the instance root.
11
- */
12
8
  function test(src, options) {
13
9
  const { sync = false, flag = 'e' } = options ?? {};
14
10
  const mode = this.constants[flag];
15
- src = node_path_1.default.resolve(this.pwd, src);
16
11
  if (sync) {
17
12
  try {
13
+ src = this.resolve(src);
18
14
  node_fs_1.default.accessSync(src, mode);
19
15
  return true;
20
16
  }
@@ -23,6 +19,13 @@ function test(src, options) {
23
19
  }
24
20
  }
25
21
  return new Promise((resolve) => {
22
+ try {
23
+ src = this.resolve(src);
24
+ }
25
+ catch {
26
+ resolve(false);
27
+ return;
28
+ }
26
29
  node_fs_1.default.access(src, mode, (err) => {
27
30
  resolve(!err);
28
31
  });
@@ -1,10 +1,13 @@
1
- import type { Flag, PoweredFileSystem } from '../powered-file-system';
2
- /**
3
- * Writes a file relative to `pwd` and then reapplies the computed permissions explicitly.
4
- */
5
- export declare function write<T extends boolean = false>(this: PoweredFileSystem, src: string, data: Buffer | string, options?: {
6
- sync?: T;
1
+ import type { AsyncOption, Flag, MaybeSyncOption, PoweredFileSystem, SyncOption } from '../powered-file-system';
2
+ type WriteOptions = {
7
3
  encoding?: BufferEncoding | null;
8
4
  umask?: number;
9
5
  flag?: Flag;
10
- }): T extends true ? void : Promise<void>;
6
+ };
7
+ /**
8
+ * Writes a file relative to `pwd` and then reapplies the computed permissions explicitly.
9
+ */
10
+ export declare function write(this: PoweredFileSystem, src: string, data: Buffer | string, options: SyncOption & WriteOptions): void;
11
+ export declare function write(this: PoweredFileSystem, src: string, data: Buffer | string, options?: AsyncOption & WriteOptions): Promise<void>;
12
+ export declare function write(this: PoweredFileSystem, src: string, data: Buffer | string, options?: MaybeSyncOption & WriteOptions): void | Promise<void>;
13
+ export {};
@@ -5,21 +5,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.write = write;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
- const node_path_1 = __importDefault(require("node:path"));
9
- /**
10
- * Writes a file relative to `pwd` and then reapplies the computed permissions explicitly.
11
- */
12
8
  function write(src, data, options) {
13
9
  const { sync = false, encoding = 'utf8', umask = 0o000, flag = 'w', } = options ?? {};
14
- src = node_path_1.default.resolve(this.pwd, src);
15
10
  const mode = 0o666 & ~umask;
16
11
  if (sync) {
12
+ src = this.resolve(src);
17
13
  // Apply chmod explicitly so the final mode is deterministic across runtimes.
18
14
  node_fs_1.default.writeFileSync(src, data, { encoding, mode, flag });
19
15
  node_fs_1.default.chmodSync(src, mode);
20
- return undefined;
16
+ return;
21
17
  }
22
18
  return new Promise((resolve, reject) => {
19
+ try {
20
+ src = this.resolve(src);
21
+ }
22
+ catch (err) {
23
+ reject(err);
24
+ return;
25
+ }
23
26
  node_fs_1.default.writeFile(src, data, { encoding, mode, flag }, (err) => {
24
27
  if (err) {
25
28
  return reject(err);
@@ -7,6 +7,27 @@ export type Mode = keyof IConstants;
7
7
  export type Flag = Extract<fs.OpenMode, string>;
8
8
  export type Stats = fs.Stats;
9
9
  export type CopyFilter = (src: string, dest: string) => boolean;
10
+ export type SyncResult<T extends boolean, TResult> = T extends true ? TResult : Promise<TResult>;
11
+ export type SyncOption = {
12
+ sync: true;
13
+ };
14
+ export type AsyncOption = {
15
+ sync?: false;
16
+ };
17
+ export type MaybeSyncOption = {
18
+ sync?: boolean;
19
+ };
20
+ export type ReadResult<TEncoding> = TEncoding extends null ? Buffer : string;
21
+ export type ReaddirResult<TEncoding> = TEncoding extends null ? Buffer[] : string[];
22
+ export type ReadOptions<TSync extends boolean = boolean, TEncoding extends BufferEncoding | null = BufferEncoding> = {
23
+ sync?: TSync;
24
+ encoding?: TEncoding;
25
+ flag?: Flag;
26
+ };
27
+ export type ReaddirOptions<TSync extends boolean = boolean, TEncoding extends BufferEncoding | null = BufferEncoding> = {
28
+ sync?: TSync;
29
+ encoding?: TEncoding;
30
+ };
10
31
  export * from './bitmask';
11
32
  export interface IConstants {
12
33
  e: number;
@@ -17,8 +38,7 @@ export interface IConstants {
17
38
  /**
18
39
  * Path-aware wrapper around Node's file system APIs.
19
40
  *
20
- * All relative paths are resolved against `pwd`, which makes the instance
21
- * suitable for sandboxed or virtual working-directory workflows.
41
+ * Relative paths are resolved against `pwd`; absolute paths are preserved.
22
42
  */
23
43
  export declare class PoweredFileSystem {
24
44
  readonly pwd: string;
@@ -34,117 +54,144 @@ export declare class PoweredFileSystem {
34
54
  * @param pwd Base directory used to resolve all relative paths.
35
55
  */
36
56
  constructor(pwd?: string);
57
+ /**
58
+ * Resolves relative paths against `pwd` while preserving absolute paths.
59
+ */
60
+ resolve(src: string): string;
37
61
  /**
38
62
  * Checks whether the given path is accessible with the requested mode.
39
63
  */
40
- test<T extends boolean = false>(src: string, options?: {
41
- sync?: T;
64
+ test(src: string, options: SyncOption & {
65
+ flag?: Mode;
66
+ }): boolean;
67
+ test(src: string, options?: AsyncOption & {
42
68
  flag?: Mode;
43
- }): T extends true ? boolean : Promise<boolean>;
69
+ }): Promise<boolean>;
44
70
  /**
45
71
  * Returns `lstat` information for a path.
46
72
  */
47
- stat<T extends boolean = false>(src: string, options?: {
48
- sync?: T;
49
- }): T extends true ? Stats : Promise<Stats>;
73
+ stat(src: string, options: SyncOption): Stats;
74
+ stat(src: string, options?: AsyncOption): Promise<Stats>;
50
75
  /**
51
76
  * Applies a mode recursively to a file or directory tree.
52
77
  */
53
- chmod<T extends boolean = false>(src: string, mode: number, options?: {
54
- sync?: T;
55
- }): T extends true ? void : Promise<void>;
78
+ chmod(src: string, mode: number, options: SyncOption): void;
79
+ chmod(src: string, mode: number, options?: AsyncOption): Promise<void>;
56
80
  /**
57
81
  * Applies ownership recursively to a file or directory tree.
58
82
  */
59
- chown<T extends boolean = false>(src: string, options?: {
60
- sync?: T;
83
+ chown(src: string, options: SyncOption & {
84
+ uid?: number;
85
+ gid?: number;
86
+ }): void;
87
+ chown(src: string, options?: AsyncOption & {
61
88
  uid?: number;
62
89
  gid?: number;
63
- }): T extends true ? void : Promise<void>;
90
+ }): Promise<void>;
64
91
  /**
65
92
  * Creates a symbolic link from `dest` to `src`.
66
93
  */
67
- symlink<T extends boolean = false>(src: string, dest: string, options?: {
68
- sync?: T;
69
- }): T extends true ? void : Promise<void>;
94
+ symlink(src: string, dest: string, options: SyncOption): void;
95
+ symlink(src: string, dest: string, options?: AsyncOption): Promise<void>;
70
96
  /**
71
97
  * Copies `src` into the destination directory.
72
98
  */
73
- copy<T extends boolean = false>(src: string, dest: string, options?: {
74
- sync?: T;
99
+ copy(src: string, dest: string, options: SyncOption & {
75
100
  umask?: number;
76
101
  overwrite?: boolean;
77
102
  filter?: CopyFilter;
78
- }): T extends true ? void : Promise<void>;
103
+ }): void;
104
+ copy(src: string, dest: string, options?: AsyncOption & {
105
+ umask?: number;
106
+ overwrite?: boolean;
107
+ filter?: CopyFilter;
108
+ }): Promise<void>;
79
109
  /**
80
110
  * Renames or moves a file system node.
81
111
  */
82
- rename<T extends boolean = false>(src: string, dest: string, options?: {
83
- sync?: T;
84
- }): T extends true ? void : Promise<void>;
112
+ rename(src: string, dest: string, options: SyncOption): void;
113
+ rename(src: string, dest: string, options?: AsyncOption): Promise<void>;
85
114
  /**
86
115
  * Removes a file system node recursively.
87
116
  */
88
- remove<T extends boolean = false>(src: string, options?: {
89
- sync?: T;
90
- }): T extends true ? void : Promise<void>;
117
+ remove(src: string, options: SyncOption): void;
118
+ remove(src: string, options?: AsyncOption): Promise<void>;
91
119
  /**
92
120
  * Removes all directory entries while preserving the directory itself.
93
121
  */
94
- emptyDir<T extends boolean = false>(src: string, options?: {
95
- sync?: T;
96
- }): T extends true ? void : Promise<void>;
122
+ emptyDir(src: string, options: SyncOption): void;
123
+ emptyDir(src: string, options?: AsyncOption): Promise<void>;
97
124
  /**
98
- * Reads a file relative to the current instance root.
125
+ * Reads a file relative to the current instance path.
99
126
  */
100
- read<T extends boolean = false>(src: string, options?: {
101
- sync?: T;
102
- encoding?: BufferEncoding | null;
103
- flag?: Flag;
104
- }): T extends true ? string | Buffer : Promise<string | Buffer>;
127
+ read(src: string, options: SyncOption & ReadOptions<true, null> & {
128
+ encoding: null;
129
+ }): Buffer;
130
+ read(src: string, options: SyncOption & ReadOptions<true, BufferEncoding>): string;
131
+ read(src: string, options: AsyncOption & ReadOptions<false, null> & {
132
+ encoding: null;
133
+ }): Promise<Buffer>;
134
+ read(src: string, options?: AsyncOption & ReadOptions<false, BufferEncoding>): Promise<string>;
105
135
  /**
106
136
  * Writes a file and applies the resulting permissions explicitly.
107
137
  */
108
- write<T extends boolean = false>(src: string, data: Buffer | string, options?: {
109
- sync?: T;
138
+ write(src: string, data: Buffer | string, options: SyncOption & {
110
139
  encoding?: BufferEncoding | null;
111
140
  umask?: number;
112
141
  flag?: Flag;
113
- }): T extends true ? void : Promise<void>;
142
+ }): void;
143
+ write(src: string, data: Buffer | string, options?: AsyncOption & {
144
+ encoding?: BufferEncoding | null;
145
+ umask?: number;
146
+ flag?: Flag;
147
+ }): Promise<void>;
114
148
  /**
115
149
  * @deprecated Use `write(..., { flag: 'a' })` instead.
116
150
  */
117
- append<T extends boolean = false>(src: string, data: Buffer | string, options?: {
118
- sync?: T;
151
+ append(src: string, data: Buffer | string, options: SyncOption & {
152
+ encoding?: BufferEncoding | null;
153
+ umask?: number;
154
+ }): void;
155
+ append(src: string, data: Buffer | string, options?: AsyncOption & {
119
156
  encoding?: BufferEncoding | null;
120
157
  umask?: number;
121
- }): T extends true ? void : Promise<void>;
158
+ }): Promise<void>;
122
159
  /**
123
- * Lists directory entries relative to the current instance root.
160
+ * Lists directory entries relative to the current instance path.
124
161
  */
125
- readdir<T extends boolean = false>(dir: string, options?: {
126
- sync?: T;
127
- encoding?: BufferEncoding | null;
128
- }): T extends true ? string[] : Promise<string[]>;
162
+ readdir(dir: string, options: SyncOption & ReaddirOptions<true, null> & {
163
+ encoding: null;
164
+ }): Buffer[];
165
+ readdir(dir: string, options: SyncOption & ReaddirOptions<true, BufferEncoding>): string[];
166
+ readdir(dir: string, options: AsyncOption & ReaddirOptions<false, null> & {
167
+ encoding: null;
168
+ }): Promise<Buffer[]>;
169
+ readdir(dir: string, options?: AsyncOption & ReaddirOptions<false, BufferEncoding>): Promise<string[]>;
129
170
  /**
130
171
  * Resolves the target of a symbolic link.
131
172
  */
132
- readlink<T extends boolean = false>(src: string, options?: {
133
- sync?: T;
173
+ readlink(src: string, options: SyncOption & {
174
+ encoding?: BufferEncoding;
175
+ }): string;
176
+ readlink(src: string, options?: AsyncOption & {
134
177
  encoding?: BufferEncoding;
135
- }): T extends true ? string : Promise<string>;
178
+ }): Promise<string>;
136
179
  /**
137
180
  * Resolves a path to its canonical absolute location.
138
181
  */
139
- realpath<T extends boolean = false>(src: string, options?: {
140
- sync?: T;
182
+ realpath(src: string, options: SyncOption & {
141
183
  encoding?: BufferEncoding;
142
- }): T extends true ? string : Promise<string>;
184
+ }): string;
185
+ realpath(src: string, options?: AsyncOption & {
186
+ encoding?: BufferEncoding;
187
+ }): Promise<string>;
143
188
  /**
144
- * Creates a directory tree relative to the current instance root.
189
+ * Creates a directory tree relative to the current instance path.
145
190
  */
146
- mkdir<T extends boolean = false>(dir: string, options?: {
147
- sync?: T;
191
+ mkdir(dir: string, options: SyncOption & {
192
+ umask?: number;
193
+ }): void;
194
+ mkdir(dir: string, options?: AsyncOption & {
148
195
  umask?: number;
149
- }): T extends true ? void : Promise<void>;
196
+ }): Promise<void>;
150
197
  }
@@ -41,8 +41,7 @@ __exportStar(require("./bitmask"), exports);
41
41
  /**
42
42
  * Path-aware wrapper around Node's file system APIs.
43
43
  *
44
- * All relative paths are resolved against `pwd`, which makes the instance
45
- * suitable for sandboxed or virtual working-directory workflows.
44
+ * Relative paths are resolved against `pwd`; absolute paths are preserved.
46
45
  */
47
46
  class PoweredFileSystem {
48
47
  pwd;
@@ -66,98 +65,56 @@ class PoweredFileSystem {
66
65
  this.pwd = pwd ? node_path_1.default.resolve(pwd) : process.cwd();
67
66
  }
68
67
  /**
69
- * Checks whether the given path is accessible with the requested mode.
68
+ * Resolves relative paths against `pwd` while preserving absolute paths.
70
69
  */
70
+ resolve(src) {
71
+ return node_path_1.default.resolve(this.pwd, src);
72
+ }
71
73
  test(src, options) {
72
74
  return test_1.test.call(this, src, options);
73
75
  }
74
- /**
75
- * Returns `lstat` information for a path.
76
- */
77
76
  stat(src, options) {
78
77
  return stat_1.stat.call(this, src, options);
79
78
  }
80
- /**
81
- * Applies a mode recursively to a file or directory tree.
82
- */
83
79
  chmod(src, mode, options) {
84
80
  return chmod_1.chmod.call(this, src, mode, options);
85
81
  }
86
- /**
87
- * Applies ownership recursively to a file or directory tree.
88
- */
89
82
  chown(src, options) {
90
83
  return chown_1.chown.call(this, src, options);
91
84
  }
92
- /**
93
- * Creates a symbolic link from `dest` to `src`.
94
- */
95
85
  symlink(src, dest, options) {
96
86
  return symlink_1.symlink.call(this, src, dest, options);
97
87
  }
98
- /**
99
- * Copies `src` into the destination directory.
100
- */
101
88
  copy(src, dest, options) {
102
89
  return copy_1.copy.call(this, src, dest, options);
103
90
  }
104
- /**
105
- * Renames or moves a file system node.
106
- */
107
91
  rename(src, dest, options) {
108
92
  return rename_1.rename.call(this, src, dest, options);
109
93
  }
110
- /**
111
- * Removes a file system node recursively.
112
- */
113
94
  remove(src, options) {
114
95
  return remove_1.remove.call(this, src, options);
115
96
  }
116
- /**
117
- * Removes all directory entries while preserving the directory itself.
118
- */
119
97
  emptyDir(src, options) {
120
98
  return empty_dir_1.emptyDir.call(this, src, options);
121
99
  }
122
- /**
123
- * Reads a file relative to the current instance root.
124
- */
125
100
  read(src, options) {
126
101
  return read_1.read.call(this, src, options);
127
102
  }
128
- /**
129
- * Writes a file and applies the resulting permissions explicitly.
130
- */
131
103
  write(src, data, options) {
132
104
  return write_1.write.call(this, src, data, options);
133
105
  }
134
- /**
135
- * @deprecated Use `write(..., { flag: 'a' })` instead.
136
- */
137
106
  append(src, data, options) {
138
107
  return append_1.append.call(this, src, data, options);
139
108
  }
140
- /**
141
- * Lists directory entries relative to the current instance root.
142
- */
143
109
  readdir(dir, options) {
144
110
  return readdir_1.readdir.call(this, dir, options);
145
111
  }
146
- /**
147
- * Resolves the target of a symbolic link.
148
- */
149
112
  readlink(src, options) {
150
113
  return readlink_1.readlink.call(this, src, options);
151
114
  }
152
- /**
153
- * Resolves a path to its canonical absolute location.
154
- */
155
115
  realpath(src, options) {
156
116
  return realpath_1.realpath.call(this, src, options);
157
117
  }
158
- /**
159
- * Creates a directory tree relative to the current instance root.
160
- */
161
118
  mkdir(dir, options) {
162
119
  return mkdir_1.mkdir.call(this, dir, options);
163
120
  }
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const node_assert_1 = __importDefault(require("node:assert"));
7
+ const node_path_1 = __importDefault(require("node:path"));
7
8
  const node_test_1 = require("node:test");
8
9
  const index_1 = require("./index");
10
+ const test_utils_1 = require("./test-utils");
9
11
  /**
10
12
  * Verifies constructor path resolution semantics for the main API surface.
11
13
  */
@@ -18,4 +20,27 @@ const index_1 = require("./index");
18
20
  const { pwd } = new index_1.PoweredFileSystem(__dirname);
19
21
  (0, node_assert_1.default)(pwd === __dirname);
20
22
  });
23
+ (0, node_test_1.it)('Positive: Resolve should accept paths inside the working directory', () => {
24
+ const tmpDir = (0, test_utils_1.createTmpDir)();
25
+ try {
26
+ const pfs = new index_1.PoweredFileSystem(tmpDir);
27
+ const resolved = pfs.resolve('./nested/file.txt');
28
+ (0, node_assert_1.default)(resolved === node_path_1.default.join(tmpDir, 'nested', 'file.txt'));
29
+ }
30
+ finally {
31
+ (0, test_utils_1.restore)(tmpDir);
32
+ }
33
+ });
34
+ (0, node_test_1.it)('Positive: Resolve should preserve absolute paths outside the working directory', () => {
35
+ const tmpDir = (0, test_utils_1.createTmpDir)();
36
+ const outsidePath = node_path_1.default.resolve(node_path_1.default.dirname(tmpDir), 'outside.txt');
37
+ try {
38
+ const pfs = new index_1.PoweredFileSystem(tmpDir);
39
+ const resolved = pfs.resolve(outsidePath);
40
+ (0, node_assert_1.default)(resolved === outsidePath);
41
+ }
42
+ finally {
43
+ (0, test_utils_1.restore)(tmpDir);
44
+ }
45
+ });
21
46
  });
@@ -6,7 +6,7 @@ export declare function chmodSync(src: string, mode: number): void;
6
6
  /**
7
7
  * Synchronous counterpart of the recursive chown implementation.
8
8
  */
9
- export declare function chownSync(src: string, uid: number, gid: number): void;
9
+ export declare function chownSync(src: string, uid: number | undefined, gid: number | undefined): void;
10
10
  /**
11
11
  * Synchronously copies a file system node into the target directory.
12
12
  */
@@ -29,19 +29,16 @@ function chmodSync(src, mode) {
29
29
  */
30
30
  function chownSync(src, uid, gid) {
31
31
  const stats = node_fs_1.default.statSync(src);
32
- if (uid === 0) {
33
- uid = stats.uid;
34
- }
35
- if (gid === 0) {
36
- gid = stats.gid;
37
- }
32
+ // `0` is a valid uid/gid, so only nullish values mean "preserve current owner".
33
+ const nextUid = uid ?? stats.uid;
34
+ const nextGid = gid ?? stats.gid;
38
35
  if (stats.isDirectory()) {
39
36
  const list = node_fs_1.default.readdirSync(src);
40
37
  for (const loc of list) {
41
- chownSync(node_path_1.default.join(src, loc), uid, gid);
38
+ chownSync(node_path_1.default.join(src, loc), nextUid, nextGid);
42
39
  }
43
40
  }
44
- node_fs_1.default.chownSync(src, uid, gid);
41
+ node_fs_1.default.chownSync(src, nextUid, nextGid);
45
42
  }
46
43
  /**
47
44
  * Synchronously copies a file system node into the target directory.
@@ -59,6 +56,7 @@ function copySync(src, dir, options) {
59
56
  if (stat.isDirectory()) {
60
57
  const list = node_fs_1.default.readdirSync(src);
61
58
  const mode = 0o777 & ~options.umask;
59
+ // Overwrite is implemented as replace-before-copy to support directory targets.
62
60
  if (options.overwrite && node_fs_1.default.existsSync(dest)) {
63
61
  removeSync(dest);
64
62
  }
@@ -68,10 +66,12 @@ function copySync(src, dir, options) {
68
66
  }
69
67
  }
70
68
  else {
69
+ // Match directory behavior by replacing the existing target before writing.
71
70
  if (options.overwrite && node_fs_1.default.existsSync(dest)) {
72
71
  removeSync(dest);
73
72
  }
74
- node_fs_1.default.copyFileSync(src, dest);
73
+ const flags = options.overwrite ? 0 : node_fs_1.default.constants.COPYFILE_EXCL;
74
+ node_fs_1.default.copyFileSync(src, dest, flags);
75
75
  node_fs_1.default.chmodSync(dest, 0o666 & ~options.umask);
76
76
  }
77
77
  }
@@ -79,21 +79,7 @@ function copySync(src, dir, options) {
79
79
  * Synchronously removes files, directories, and symlinks without following links.
80
80
  */
81
81
  function removeSync(src) {
82
- const stats = node_fs_1.default.lstatSync(src);
83
- if (stats.isSymbolicLink()) {
84
- node_fs_1.default.unlinkSync(src);
85
- return;
86
- }
87
- if (stats.isDirectory()) {
88
- const list = node_fs_1.default.readdirSync(src);
89
- for (const loc of list) {
90
- removeSync(node_path_1.default.join(src, loc));
91
- }
92
- node_fs_1.default.rmdirSync(src);
93
- }
94
- else {
95
- node_fs_1.default.unlinkSync(src);
96
- }
82
+ node_fs_1.default.rmSync(src, { recursive: true, force: false });
97
83
  }
98
84
  /**
99
85
  * Synchronously removes all entries inside a directory while preserving it.
@@ -101,7 +87,7 @@ function removeSync(src) {
101
87
  function emptyDirSync(src) {
102
88
  const list = node_fs_1.default.readdirSync(src);
103
89
  for (const loc of list) {
104
- removeSync(node_path_1.default.join(src, loc));
90
+ node_fs_1.default.rmSync(node_path_1.default.join(src, loc), { recursive: true, force: false });
105
91
  }
106
92
  }
107
93
  /**