shell-dsl 0.0.29 → 0.0.31

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 (40) hide show
  1. package/README.md +33 -7
  2. package/dist/cjs/package.json +1 -1
  3. package/dist/cjs/src/fs/index.cjs +4 -4
  4. package/dist/cjs/src/fs/index.cjs.map +3 -3
  5. package/dist/cjs/src/fs/memfs-adapter.cjs +7 -108
  6. package/dist/cjs/src/fs/memfs-adapter.cjs.map +3 -3
  7. package/dist/cjs/src/fs/real-fs.cjs +27 -123
  8. package/dist/cjs/src/fs/real-fs.cjs.map +3 -3
  9. package/dist/cjs/src/fs/{opfs-fs.cjs → web-fs.cjs} +61 -13
  10. package/dist/cjs/src/fs/web-fs.cjs.map +10 -0
  11. package/dist/cjs/src/index.cjs +4 -3
  12. package/dist/cjs/src/index.cjs.map +3 -3
  13. package/dist/cjs/src/utils/glob.cjs +129 -0
  14. package/dist/cjs/src/utils/glob.cjs.map +10 -0
  15. package/dist/cjs/src/utils/index.cjs +3 -1
  16. package/dist/cjs/src/utils/index.cjs.map +3 -3
  17. package/dist/mjs/package.json +1 -1
  18. package/dist/mjs/src/fs/index.mjs +4 -4
  19. package/dist/mjs/src/fs/index.mjs.map +2 -2
  20. package/dist/mjs/src/fs/memfs-adapter.mjs +7 -108
  21. package/dist/mjs/src/fs/memfs-adapter.mjs.map +3 -3
  22. package/dist/mjs/src/fs/real-fs.mjs +27 -123
  23. package/dist/mjs/src/fs/real-fs.mjs.map +3 -3
  24. package/dist/mjs/src/fs/{opfs-fs.mjs → web-fs.mjs} +58 -10
  25. package/dist/mjs/src/fs/web-fs.mjs.map +10 -0
  26. package/dist/mjs/src/index.mjs +7 -6
  27. package/dist/mjs/src/index.mjs.map +3 -3
  28. package/dist/mjs/src/utils/glob.mjs +89 -0
  29. package/dist/mjs/src/utils/glob.mjs.map +10 -0
  30. package/dist/mjs/src/utils/index.mjs +3 -1
  31. package/dist/mjs/src/utils/index.mjs.map +3 -3
  32. package/dist/types/src/fs/index.d.ts +2 -2
  33. package/dist/types/src/fs/real-fs.d.ts +12 -5
  34. package/dist/types/src/fs/{opfs-fs.d.ts → web-fs.d.ts} +2 -2
  35. package/dist/types/src/index.d.ts +3 -2
  36. package/dist/types/src/utils/glob.d.ts +6 -0
  37. package/dist/types/src/utils/index.d.ts +1 -0
  38. package/package.json +1 -1
  39. package/dist/cjs/src/fs/opfs-fs.cjs.map +0 -10
  40. package/dist/mjs/src/fs/opfs-fs.mjs.map +0 -10
package/README.md CHANGED
@@ -616,6 +616,32 @@ interface VirtualFS {
616
616
  }
617
617
  ```
618
618
 
619
+ ### `globVirtualFS` Helper
620
+
621
+ If you're implementing a custom `VirtualFS`, especially a composite or mounted filesystem, you can reuse `globVirtualFS()` instead of writing glob traversal yourself:
622
+
623
+ ```ts
624
+ import { globVirtualFS, type VirtualFS } from "shell-dsl";
625
+
626
+ class CompositeFileSystem implements VirtualFS {
627
+ // ... implement readFile/readdir/stat/etc.
628
+
629
+ async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {
630
+ return globVirtualFS(this, pattern, opts);
631
+ }
632
+ }
633
+ ```
634
+
635
+ `globVirtualFS()` walks the visible virtual tree using only `readdir()`, `stat()`, and `resolve()`, so it works correctly for filesystems that mount different host directories under one virtual namespace.
636
+
637
+ It supports the same shell-style patterns used by the interpreter:
638
+
639
+ - `*.txt` for segment wildcards
640
+ - `**/*.ts` for recursive matches
641
+ - `file-?.md` for single-character matches
642
+ - `{a,b}.json` for brace expansion
643
+ - `[ab].txt` for character classes
644
+
619
645
  ## Real Filesystem Access
620
646
 
621
647
  For scenarios where you need to access the real filesystem with sandboxing, use `FileSystem` or `ReadOnlyFileSystem`:
@@ -695,27 +721,27 @@ Omit the mount path for unrestricted access, but this is the same as just passin
695
721
  const fs = new FileSystem(); // Full filesystem access same as fs from node:fs
696
722
  ```
697
723
 
698
- ### OPFS (Browser)
724
+ ### Web Filesystem
699
725
 
700
- Use `OPFSFileSystem` when you already have an OPFS root directory handle in the browser:
726
+ Use `WebFileSystem` when you already have a `FileSystemDirectoryHandle` in the browser, including an OPFS root from `navigator.storage.getDirectory()`:
701
727
 
702
728
  ```ts
703
- import { OPFSFileSystem } from "shell-dsl";
729
+ import { WebFileSystem } from "shell-dsl";
704
730
 
705
731
  const root = await navigator.storage.getDirectory();
706
- const fs = new OPFSFileSystem(root, {
732
+ const fs = new WebFileSystem(root, {
707
733
  "secrets/**": "excluded",
708
734
  "docs/**": "read-only",
709
735
  });
710
736
  ```
711
737
 
712
- For advanced use, you can inject the OPFS adapter into `FileSystem` directly:
738
+ For advanced use, you can inject the web adapter into `FileSystem` directly:
713
739
 
714
740
  ```ts
715
- import { FileSystem, createOPFSUnderlyingFS } from "shell-dsl";
741
+ import { FileSystem, createWebUnderlyingFS } from "shell-dsl";
716
742
 
717
743
  const root = await navigator.storage.getDirectory();
718
- const fs = new FileSystem("/", {}, createOPFSUnderlyingFS(root));
744
+ const fs = new FileSystem("/", {}, createWebUnderlyingFS(root));
719
745
  ```
720
746
 
721
747
 
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "shell-dsl",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "type": "commonjs"
5
5
  }
@@ -39,16 +39,16 @@ var __export = (target, all) => {
39
39
  // src/fs/index.ts
40
40
  var exports_fs = {};
41
41
  __export(exports_fs, {
42
+ createWebUnderlyingFS: () => import_web_fs.createWebUnderlyingFS,
42
43
  createVirtualFS: () => import_memfs_adapter.createVirtualFS,
43
- createOPFSUnderlyingFS: () => import_opfs_fs.createOPFSUnderlyingFS,
44
+ WebFileSystem: () => import_web_fs.WebFileSystem,
44
45
  ReadOnlyFileSystem: () => import_readonly_fs.ReadOnlyFileSystem,
45
- OPFSFileSystem: () => import_opfs_fs.OPFSFileSystem,
46
46
  FileSystem: () => import_real_fs.FileSystem
47
47
  });
48
48
  module.exports = __toCommonJS(exports_fs);
49
49
  var import_memfs_adapter = require("./memfs-adapter.cjs");
50
50
  var import_real_fs = require("./real-fs.cjs");
51
51
  var import_readonly_fs = require("./readonly-fs.cjs");
52
- var import_opfs_fs = require("./opfs-fs.cjs");
52
+ var import_web_fs = require("./web-fs.cjs");
53
53
 
54
- //# debugId=39C00A408BEE69A164756E2164756E21
54
+ //# debugId=C93B6DA797F0B48764756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/fs/index.ts"],
4
4
  "sourcesContent": [
5
- "export { createVirtualFS } from \"./memfs-adapter.cjs\";\nexport { FileSystem, type Permission, type PermissionRules, type UnderlyingFS } from \"./real-fs.cjs\";\nexport { ReadOnlyFileSystem } from \"./readonly-fs.cjs\";\nexport { OPFSFileSystem, createOPFSUnderlyingFS } from \"./opfs-fs.cjs\";\n"
5
+ "export { createVirtualFS } from \"./memfs-adapter.cjs\";\nexport { FileSystem, type PathOps, type Permission, type PermissionRules, type UnderlyingFS } from \"./real-fs.cjs\";\nexport { ReadOnlyFileSystem } from \"./readonly-fs.cjs\";\nexport { WebFileSystem, createWebUnderlyingFS } from \"./web-fs.cjs\";\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAgC,IAAhC;AACqF,IAArF;AACmC,IAAnC;AACuD,IAAvD;",
8
- "debugId": "39C00A408BEE69A164756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAgC,IAAhC;AACmG,IAAnG;AACmC,IAAnC;AACqD,IAArD;",
8
+ "debugId": "C93B6DA797F0B48764756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -67,6 +67,7 @@ __export(exports_memfs_adapter, {
67
67
  });
68
68
  module.exports = __toCommonJS(exports_memfs_adapter);
69
69
  var pathModule = __toESM(require("path"));
70
+ var import_glob = require("../utils/glob.cjs");
70
71
  function createVirtualFS(memfs) {
71
72
  const { promises: fs } = memfs;
72
73
  return {
@@ -131,115 +132,13 @@ function createVirtualFS(memfs) {
131
132
  },
132
133
  async glob(pattern, opts) {
133
134
  const cwd = opts?.cwd ?? "/";
134
- return expandGlob(memfs, pattern, cwd);
135
+ return import_glob.globVirtualFS({
136
+ readdir: (filePath) => this.readdir(filePath),
137
+ stat: (filePath) => this.stat(filePath),
138
+ resolve: (...paths) => this.resolve(...paths)
139
+ }, pattern, { cwd });
135
140
  }
136
141
  };
137
142
  }
138
- async function expandGlob(memfs, pattern, cwd) {
139
- const { promises: fs } = memfs;
140
- const patterns = expandBraces(pattern);
141
- const allMatches = [];
142
- for (const pat of patterns) {
143
- const matches = await matchPattern(fs, pat, cwd);
144
- allMatches.push(...matches);
145
- }
146
- return [...new Set(allMatches)].sort();
147
- }
148
- function expandBraces(pattern) {
149
- const braceMatch = pattern.match(/\{([^{}]+)\}/);
150
- if (!braceMatch)
151
- return [pattern];
152
- const before = pattern.slice(0, braceMatch.index);
153
- const after = pattern.slice(braceMatch.index + braceMatch[0].length);
154
- const options = braceMatch[1].split(",");
155
- const results = [];
156
- for (const opt of options) {
157
- const expanded = expandBraces(before + opt + after);
158
- results.push(...expanded);
159
- }
160
- return results;
161
- }
162
- async function matchPattern(fs, pattern, cwd) {
163
- const parts = pattern.split("/").filter((p) => p !== "");
164
- const isAbsolute = pattern.startsWith("/");
165
- const startDir = isAbsolute ? "/" : cwd;
166
- return matchParts(fs, parts, startDir, isAbsolute);
167
- }
168
- async function matchParts(fs, parts, currentPath, isAbsolute) {
169
- if (parts.length === 0) {
170
- return [currentPath];
171
- }
172
- const [part, ...rest] = parts;
173
- if (part === "**") {
174
- const results = [];
175
- const withoutStar = await matchParts(fs, rest, currentPath, isAbsolute);
176
- results.push(...withoutStar);
177
- try {
178
- const entries = await fs.readdir(currentPath);
179
- for (const entry of entries) {
180
- const entryPath = pathModule.join(currentPath, String(entry));
181
- try {
182
- const stat = await fs.stat(entryPath);
183
- if (stat.isDirectory()) {
184
- const subMatches = await matchParts(fs, parts, entryPath, isAbsolute);
185
- results.push(...subMatches);
186
- }
187
- } catch {}
188
- }
189
- } catch {}
190
- return results;
191
- }
192
- const regex = globToRegex(part);
193
- try {
194
- const entries = await fs.readdir(currentPath);
195
- const results = [];
196
- for (const entry of entries) {
197
- const entryName = String(entry);
198
- if (regex.test(entryName)) {
199
- const entryPath = pathModule.join(currentPath, entryName);
200
- if (rest.length === 0) {
201
- results.push(entryPath);
202
- } else {
203
- try {
204
- const stat = await fs.stat(entryPath);
205
- if (stat.isDirectory()) {
206
- const subMatches = await matchParts(fs, rest, entryPath, isAbsolute);
207
- results.push(...subMatches);
208
- }
209
- } catch {}
210
- }
211
- }
212
- }
213
- return results;
214
- } catch {
215
- return [];
216
- }
217
- }
218
- function globToRegex(pattern) {
219
- let regex = "^";
220
- for (let i = 0;i < pattern.length; i++) {
221
- const char = pattern[i];
222
- if (char === "*") {
223
- regex += "[^/]*";
224
- } else if (char === "?") {
225
- regex += "[^/]";
226
- } else if (char === "[") {
227
- let j = i + 1;
228
- let classContent = "";
229
- while (j < pattern.length && pattern[j] !== "]") {
230
- classContent += pattern[j];
231
- j++;
232
- }
233
- regex += `[${classContent}]`;
234
- i = j;
235
- } else if (".+^${}()|\\".includes(char)) {
236
- regex += "\\" + char;
237
- } else {
238
- regex += char;
239
- }
240
- }
241
- regex += "$";
242
- return new RegExp(regex);
243
- }
244
143
 
245
- //# debugId=8EA2A9013D6F90A464756E2164756E21
144
+ //# debugId=60E8B0C48B1EE27B64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/fs/memfs-adapter.ts"],
4
4
  "sourcesContent": [
5
- "import type { IFs } from \"memfs\";\nimport type { VirtualFS, FileStat } from \"../types.cjs\";\nimport * as pathModule from \"path\";\n\nexport function createVirtualFS(memfs: IFs): VirtualFS {\n const { promises: fs } = memfs;\n\n return {\n readFile: (async (path: string, encoding?: BufferEncoding): Promise<Buffer | string> => {\n if (path === \"/dev/null\") return encoding ? \"\" : Buffer.alloc(0);\n const content = await fs.readFile(path);\n const buf = Buffer.from(content);\n return encoding ? buf.toString(encoding) : buf;\n }) as VirtualFS[\"readFile\"],\n\n async readdir(path: string): Promise<string[]> {\n const entries = await fs.readdir(path);\n return entries.map(String);\n },\n\n async stat(path: string): Promise<FileStat> {\n const stats = await fs.stat(path);\n return {\n isFile: () => stats.isFile(),\n isDirectory: () => stats.isDirectory(),\n size: Number(stats.size),\n mtime: new Date(stats.mtime),\n };\n },\n\n async exists(path: string): Promise<boolean> {\n try {\n await fs.stat(path);\n return true;\n } catch {\n return false;\n }\n },\n\n async writeFile(path: string, data: Buffer | string): Promise<void> {\n await fs.writeFile(path, data);\n },\n\n async appendFile(path: string, data: Buffer | string): Promise<void> {\n await fs.appendFile(path, data);\n },\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n await fs.mkdir(path, opts);\n },\n\n async rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n try {\n const stats = await fs.stat(path);\n if (stats.isDirectory()) {\n await fs.rmdir(path, { recursive: opts?.recursive });\n } else {\n await fs.unlink(path);\n }\n } catch (err) {\n if (!opts?.force) throw err;\n }\n },\n\n resolve(...paths: string[]): string {\n return pathModule.resolve(...paths);\n },\n\n dirname(path: string): string {\n return pathModule.dirname(path);\n },\n\n basename(path: string): string {\n return pathModule.basename(path);\n },\n\n async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {\n const cwd = opts?.cwd ?? \"/\";\n return expandGlob(memfs, pattern, cwd);\n },\n };\n}\n\nasync function expandGlob(memfs: IFs, pattern: string, cwd: string): Promise<string[]> {\n const { promises: fs } = memfs;\n\n // Handle brace expansion first\n const patterns = expandBraces(pattern);\n const allMatches: string[] = [];\n\n for (const pat of patterns) {\n const matches = await matchPattern(fs, pat, cwd);\n allMatches.push(...matches);\n }\n\n // Remove duplicates and sort\n return [...new Set(allMatches)].sort();\n}\n\nfunction expandBraces(pattern: string): string[] {\n const braceMatch = pattern.match(/\\{([^{}]+)\\}/);\n if (!braceMatch) return [pattern];\n\n const before = pattern.slice(0, braceMatch.index);\n const after = pattern.slice(braceMatch.index! + braceMatch[0].length);\n const options = braceMatch[1]!.split(\",\");\n\n const results: string[] = [];\n for (const opt of options) {\n const expanded = expandBraces(before + opt + after);\n results.push(...expanded);\n }\n return results;\n}\n\nasync function matchPattern(\n fs: IFs[\"promises\"],\n pattern: string,\n cwd: string\n): Promise<string[]> {\n const parts = pattern.split(\"/\").filter((p) => p !== \"\");\n const isAbsolute = pattern.startsWith(\"/\");\n const startDir = isAbsolute ? \"/\" : cwd;\n\n return matchParts(fs, parts, startDir, isAbsolute);\n}\n\nasync function matchParts(\n fs: IFs[\"promises\"],\n parts: string[],\n currentPath: string,\n isAbsolute: boolean\n): Promise<string[]> {\n if (parts.length === 0) {\n return [currentPath];\n }\n\n const [part, ...rest] = parts;\n\n // Handle ** (recursive glob)\n if (part === \"**\") {\n const results: string[] = [];\n\n // Match current directory\n const withoutStar = await matchParts(fs, rest, currentPath, isAbsolute);\n results.push(...withoutStar);\n\n // Recurse into subdirectories\n try {\n const entries = await fs.readdir(currentPath);\n for (const entry of entries) {\n const entryPath = pathModule.join(currentPath, String(entry));\n try {\n const stat = await fs.stat(entryPath);\n if (stat.isDirectory()) {\n const subMatches = await matchParts(fs, parts, entryPath, isAbsolute);\n results.push(...subMatches);\n }\n } catch {\n // Skip inaccessible entries\n }\n }\n } catch {\n // Directory not readable\n }\n\n return results;\n }\n\n // Handle regular glob patterns\n const regex = globToRegex(part!);\n\n try {\n const entries = await fs.readdir(currentPath);\n const results: string[] = [];\n\n for (const entry of entries) {\n const entryName = String(entry);\n if (regex.test(entryName)) {\n const entryPath = pathModule.join(currentPath, entryName);\n if (rest.length === 0) {\n results.push(entryPath);\n } else {\n try {\n const stat = await fs.stat(entryPath);\n if (stat.isDirectory()) {\n const subMatches = await matchParts(fs, rest, entryPath, isAbsolute);\n results.push(...subMatches);\n }\n } catch {\n // Skip inaccessible entries\n }\n }\n }\n }\n\n return results;\n } catch {\n return [];\n }\n}\n\nfunction globToRegex(pattern: string): RegExp {\n let regex = \"^\";\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i]!;\n if (char === \"*\") {\n regex += \"[^/]*\";\n } else if (char === \"?\") {\n regex += \"[^/]\";\n } else if (char === \"[\") {\n // Character class\n let j = i + 1;\n let classContent = \"\";\n while (j < pattern.length && pattern[j] !== \"]\") {\n classContent += pattern[j];\n j++;\n }\n regex += `[${classContent}]`;\n i = j;\n } else if (\".+^${}()|\\\\\".includes(char)) {\n regex += \"\\\\\" + char;\n } else {\n regex += char;\n }\n }\n regex += \"$\";\n return new RegExp(regex);\n}\n"
5
+ "import type { IFs } from \"memfs\";\nimport type { VirtualFS, FileStat } from \"../types.cjs\";\nimport * as pathModule from \"path\";\nimport { globVirtualFS } from \"../utils/glob.cjs\";\n\nexport function createVirtualFS(memfs: IFs): VirtualFS {\n const { promises: fs } = memfs;\n\n return {\n readFile: (async (path: string, encoding?: BufferEncoding): Promise<Buffer | string> => {\n if (path === \"/dev/null\") return encoding ? \"\" : Buffer.alloc(0);\n const content = await fs.readFile(path);\n const buf = Buffer.from(content);\n return encoding ? buf.toString(encoding) : buf;\n }) as VirtualFS[\"readFile\"],\n\n async readdir(path: string): Promise<string[]> {\n const entries = await fs.readdir(path);\n return entries.map(String);\n },\n\n async stat(path: string): Promise<FileStat> {\n const stats = await fs.stat(path);\n return {\n isFile: () => stats.isFile(),\n isDirectory: () => stats.isDirectory(),\n size: Number(stats.size),\n mtime: new Date(stats.mtime),\n };\n },\n\n async exists(path: string): Promise<boolean> {\n try {\n await fs.stat(path);\n return true;\n } catch {\n return false;\n }\n },\n\n async writeFile(path: string, data: Buffer | string): Promise<void> {\n await fs.writeFile(path, data);\n },\n\n async appendFile(path: string, data: Buffer | string): Promise<void> {\n await fs.appendFile(path, data);\n },\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n await fs.mkdir(path, opts);\n },\n\n async rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n try {\n const stats = await fs.stat(path);\n if (stats.isDirectory()) {\n await fs.rmdir(path, { recursive: opts?.recursive });\n } else {\n await fs.unlink(path);\n }\n } catch (err) {\n if (!opts?.force) throw err;\n }\n },\n\n resolve(...paths: string[]): string {\n return pathModule.resolve(...paths);\n },\n\n dirname(path: string): string {\n return pathModule.dirname(path);\n },\n\n basename(path: string): string {\n return pathModule.basename(path);\n },\n\n async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {\n const cwd = opts?.cwd ?? \"/\";\n return globVirtualFS(\n {\n readdir: (filePath: string) => this.readdir(filePath),\n stat: (filePath: string) => this.stat(filePath),\n resolve: (...paths: string[]) => this.resolve(...paths),\n },\n pattern,\n { cwd }\n );\n },\n };\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "8EA2A9013D6F90A464756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE4B,IAA5B;AAC8B,IAA9B;AAEO,SAAS,eAAe,CAAC,OAAuB;AAAA,EACrD,QAAQ,UAAU,OAAO;AAAA,EAEzB,OAAO;AAAA,IACL,UAAW,OAAO,MAAc,aAAwD;AAAA,MACtF,IAAI,SAAS;AAAA,QAAa,OAAO,WAAW,KAAK,OAAO,MAAM,CAAC;AAAA,MAC/D,MAAM,UAAU,MAAM,GAAG,SAAS,IAAI;AAAA,MACtC,MAAM,MAAM,OAAO,KAAK,OAAO;AAAA,MAC/B,OAAO,WAAW,IAAI,SAAS,QAAQ,IAAI;AAAA;AAAA,SAGvC,QAAO,CAAC,MAAiC;AAAA,MAC7C,MAAM,UAAU,MAAM,GAAG,QAAQ,IAAI;AAAA,MACrC,OAAO,QAAQ,IAAI,MAAM;AAAA;AAAA,SAGrB,KAAI,CAAC,MAAiC;AAAA,MAC1C,MAAM,QAAQ,MAAM,GAAG,KAAK,IAAI;AAAA,MAChC,OAAO;AAAA,QACL,QAAQ,MAAM,MAAM,OAAO;AAAA,QAC3B,aAAa,MAAM,MAAM,YAAY;AAAA,QACrC,MAAM,OAAO,MAAM,IAAI;AAAA,QACvB,OAAO,IAAI,KAAK,MAAM,KAAK;AAAA,MAC7B;AAAA;AAAA,SAGI,OAAM,CAAC,MAAgC;AAAA,MAC3C,IAAI;AAAA,QACF,MAAM,GAAG,KAAK,IAAI;AAAA,QAClB,OAAO;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA;AAAA;AAAA,SAIL,UAAS,CAAC,MAAc,MAAsC;AAAA,MAClE,MAAM,GAAG,UAAU,MAAM,IAAI;AAAA;AAAA,SAGzB,WAAU,CAAC,MAAc,MAAsC;AAAA,MACnE,MAAM,GAAG,WAAW,MAAM,IAAI;AAAA;AAAA,SAG1B,MAAK,CAAC,MAAc,MAA+C;AAAA,MACvE,MAAM,GAAG,MAAM,MAAM,IAAI;AAAA;AAAA,SAGrB,GAAE,CAAC,MAAc,MAAgE;AAAA,MACrF,IAAI;AAAA,QACF,MAAM,QAAQ,MAAM,GAAG,KAAK,IAAI;AAAA,QAChC,IAAI,MAAM,YAAY,GAAG;AAAA,UACvB,MAAM,GAAG,MAAM,MAAM,EAAE,WAAW,MAAM,UAAU,CAAC;AAAA,QACrD,EAAO;AAAA,UACL,MAAM,GAAG,OAAO,IAAI;AAAA;AAAA,QAEtB,OAAO,KAAK;AAAA,QACZ,IAAI,CAAC,MAAM;AAAA,UAAO,MAAM;AAAA;AAAA;AAAA,IAI5B,OAAO,IAAI,OAAyB;AAAA,MAClC,OAAkB,mBAAQ,GAAG,KAAK;AAAA;AAAA,IAGpC,OAAO,CAAC,MAAsB;AAAA,MAC5B,OAAkB,mBAAQ,IAAI;AAAA;AAAA,IAGhC,QAAQ,CAAC,MAAsB;AAAA,MAC7B,OAAkB,oBAAS,IAAI;AAAA;AAAA,SAG3B,KAAI,CAAC,SAAiB,MAA4C;AAAA,MACtE,MAAM,MAAM,MAAM,OAAO;AAAA,MACzB,OAAO,0BACL;AAAA,QACE,SAAS,CAAC,aAAqB,KAAK,QAAQ,QAAQ;AAAA,QACpD,MAAM,CAAC,aAAqB,KAAK,KAAK,QAAQ;AAAA,QAC9C,SAAS,IAAI,UAAoB,KAAK,QAAQ,GAAG,KAAK;AAAA,MACxD,GACA,SACA,EAAE,IAAI,CACR;AAAA;AAAA,EAEJ;AAAA;",
8
+ "debugId": "60E8B0C48B1EE27B64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -68,16 +68,30 @@ __export(exports_real_fs, {
68
68
  module.exports = __toCommonJS(exports_real_fs);
69
69
  var path = __toESM(require("path"));
70
70
  var nodeFs = __toESM(require("node:fs/promises"));
71
+ var import_glob = require("../utils/glob.cjs");
71
72
  var defaultFS = { promises: nodeFs };
73
+ var nodePathOps = {
74
+ separator: path.sep,
75
+ resolve: (...paths) => path.resolve(...paths),
76
+ normalize: (filePath) => path.normalize(filePath),
77
+ join: (...paths) => path.join(...paths),
78
+ relative: (from, to) => path.relative(from, to),
79
+ isAbsolute: (filePath) => path.isAbsolute(filePath),
80
+ dirname: (filePath) => path.dirname(filePath),
81
+ basename: (filePath) => path.basename(filePath)
82
+ };
72
83
 
73
84
  class FileSystem {
74
85
  mountBase;
75
86
  rules;
87
+ pathOps;
76
88
  underlyingFs;
77
89
  constructor(mountPath, permissions, fs) {
78
- this.mountBase = mountPath ? path.resolve(mountPath) : null;
90
+ const underlyingFs = fs ?? defaultFS;
91
+ this.pathOps = underlyingFs.pathOps ?? nodePathOps;
92
+ this.mountBase = mountPath ? this.pathOps.resolve(mountPath) : null;
79
93
  this.rules = this.compileRules(permissions ?? {});
80
- this.underlyingFs = fs ?? defaultFS;
94
+ this.underlyingFs = underlyingFs;
81
95
  }
82
96
  compileRules(permissions) {
83
97
  return Object.entries(permissions).map(([pattern, permission]) => ({
@@ -127,7 +141,7 @@ class FileSystem {
127
141
  }
128
142
  resolveSafePath(virtualPath) {
129
143
  if (this.mountBase === null) {
130
- return path.resolve(virtualPath);
144
+ return this.pathOps.resolve(virtualPath);
131
145
  }
132
146
  const segments = virtualPath.split("/").filter(Boolean);
133
147
  let depth = 0;
@@ -141,12 +155,12 @@ class FileSystem {
141
155
  depth++;
142
156
  }
143
157
  }
144
- const normalized = path.normalize(virtualPath);
158
+ const normalized = this.pathOps.normalize(virtualPath);
145
159
  const relativePath = normalized.startsWith("/") ? normalized.slice(1) : normalized;
146
- const realPath = path.join(this.mountBase, relativePath);
147
- const resolved = path.resolve(realPath);
148
- const relativeFromMount = path.relative(this.mountBase, resolved);
149
- const escapesMount = relativeFromMount === ".." || relativeFromMount.startsWith(`..${path.sep}`) || path.isAbsolute(relativeFromMount);
160
+ const realPath = this.pathOps.join(this.mountBase, relativePath);
161
+ const resolved = this.pathOps.resolve(realPath);
162
+ const relativeFromMount = this.pathOps.relative(this.mountBase, resolved);
163
+ const escapesMount = relativeFromMount === ".." || relativeFromMount.startsWith(`..${this.pathOps.separator}`) || this.pathOps.isAbsolute(relativeFromMount);
150
164
  if (escapesMount) {
151
165
  throw new Error(`Path traversal blocked: "${virtualPath}" escapes mount point`);
152
166
  }
@@ -207,129 +221,19 @@ class FileSystem {
207
221
  await this.underlyingFs.promises.rm(realPath, opts);
208
222
  }
209
223
  resolve(...paths) {
210
- return path.resolve("/", ...paths);
224
+ return this.pathOps.resolve("/", ...paths);
211
225
  }
212
226
  dirname(filePath) {
213
- return path.dirname(filePath);
227
+ return this.pathOps.dirname(filePath);
214
228
  }
215
229
  basename(filePath) {
216
- return path.basename(filePath);
230
+ return this.pathOps.basename(filePath);
217
231
  }
218
232
  async glob(pattern, opts) {
219
233
  const cwd = opts?.cwd ?? "/";
220
234
  this.checkPermission(cwd, "read");
221
- const matches = await this.expandGlob(pattern, cwd);
222
- return matches.filter((p) => this.getPermission(p) !== "excluded").sort();
223
- }
224
- async expandGlob(pattern, cwd) {
225
- const patterns = this.expandBraces(pattern);
226
- const allMatches = [];
227
- for (const pat of patterns) {
228
- const matches = await this.matchPattern(pat, cwd);
229
- allMatches.push(...matches);
230
- }
231
- return [...new Set(allMatches)].sort();
232
- }
233
- expandBraces(pattern) {
234
- const braceMatch = pattern.match(/\{([^{}]+)\}/);
235
- if (!braceMatch)
236
- return [pattern];
237
- const before = pattern.slice(0, braceMatch.index);
238
- const after = pattern.slice(braceMatch.index + braceMatch[0].length);
239
- const options = braceMatch[1].split(",");
240
- const results = [];
241
- for (const opt of options) {
242
- const expanded = this.expandBraces(before + opt + after);
243
- results.push(...expanded);
244
- }
245
- return results;
246
- }
247
- async matchPattern(pattern, cwd) {
248
- const parts = pattern.split("/").filter((p) => p !== "");
249
- const isAbsolute2 = pattern.startsWith("/");
250
- const startDir = isAbsolute2 ? "/" : cwd;
251
- return this.matchParts(parts, startDir);
252
- }
253
- async matchParts(parts, currentPath) {
254
- if (parts.length === 0) {
255
- return [currentPath];
256
- }
257
- const [part, ...rest] = parts;
258
- if (part === "**") {
259
- const results = [];
260
- const withoutStar = await this.matchParts(rest, currentPath);
261
- results.push(...withoutStar);
262
- try {
263
- const realPath = this.resolveSafePath(currentPath);
264
- const entries = await this.underlyingFs.promises.readdir(realPath);
265
- for (const entry of entries) {
266
- const entryPath = path.posix.join(currentPath, String(entry));
267
- try {
268
- const entryRealPath = this.resolveSafePath(entryPath);
269
- const stat = await this.underlyingFs.promises.stat(entryRealPath);
270
- if (stat.isDirectory()) {
271
- const subMatches = await this.matchParts(parts, entryPath);
272
- results.push(...subMatches);
273
- }
274
- } catch {}
275
- }
276
- } catch {}
277
- return results;
278
- }
279
- const regex = this.globToRegex(part);
280
- try {
281
- const realPath = this.resolveSafePath(currentPath);
282
- const entries = await this.underlyingFs.promises.readdir(realPath);
283
- const results = [];
284
- for (const entry of entries) {
285
- const entryName = String(entry);
286
- if (regex.test(entryName)) {
287
- const entryPath = path.posix.join(currentPath, entryName);
288
- if (rest.length === 0) {
289
- results.push(entryPath);
290
- } else {
291
- try {
292
- const entryRealPath = this.resolveSafePath(entryPath);
293
- const stat = await this.underlyingFs.promises.stat(entryRealPath);
294
- if (stat.isDirectory()) {
295
- const subMatches = await this.matchParts(rest, entryPath);
296
- results.push(...subMatches);
297
- }
298
- } catch {}
299
- }
300
- }
301
- }
302
- return results;
303
- } catch {
304
- return [];
305
- }
306
- }
307
- globToRegex(pattern) {
308
- let regex = "^";
309
- for (let i = 0;i < pattern.length; i++) {
310
- const char = pattern[i];
311
- if (char === "*") {
312
- regex += "[^/]*";
313
- } else if (char === "?") {
314
- regex += "[^/]";
315
- } else if (char === "[") {
316
- let j = i + 1;
317
- let classContent = "";
318
- while (j < pattern.length && pattern[j] !== "]") {
319
- classContent += pattern[j];
320
- j++;
321
- }
322
- regex += `[${classContent}]`;
323
- i = j;
324
- } else if (".+^${}()|\\".includes(char)) {
325
- regex += "\\" + char;
326
- } else {
327
- regex += char;
328
- }
329
- }
330
- regex += "$";
331
- return new RegExp(regex);
235
+ return import_glob.globVirtualFS(this, pattern, { cwd });
332
236
  }
333
237
  }
334
238
 
335
- //# debugId=CB9942EB884530FB64756E2164756E21
239
+ //# debugId=6E371DB9ABEDFA8764756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/fs/real-fs.ts"],
4
4
  "sourcesContent": [
5
- "import * as path from \"path\";\nimport * as nodeFs from \"node:fs/promises\";\nimport type { VirtualFS, FileStat } from \"../types.cjs\";\n\nexport type Permission = \"read-write\" | \"read-only\" | \"excluded\";\nexport type PermissionRules = Record<string, Permission>;\n\n// Minimal interface for the underlying fs (compatible with node:fs and memfs)\nexport interface UnderlyingFS {\n promises: {\n readFile(path: string): Promise<Buffer | Uint8Array | string>;\n readdir(path: string): Promise<string[]>;\n stat(path: string): Promise<{\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n }>;\n writeFile(path: string, data: Buffer | string): Promise<void>;\n appendFile(path: string, data: Buffer | string): Promise<void>;\n mkdir(path: string, opts?: { recursive?: boolean }): Promise<string | undefined | void>;\n rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void>;\n };\n}\n\n// Default: use real node:fs\nconst defaultFS: UnderlyingFS = { promises: nodeFs };\n\ninterface CompiledRule {\n pattern: string;\n permission: Permission;\n specificity: number;\n}\n\nexport class FileSystem implements VirtualFS {\n private readonly mountBase: string | null;\n private readonly rules: CompiledRule[];\n protected readonly underlyingFs: UnderlyingFS;\n\n constructor(mountPath?: string, permissions?: PermissionRules, fs?: UnderlyingFS) {\n this.mountBase = mountPath ? path.resolve(mountPath) : null;\n this.rules = this.compileRules(permissions ?? {});\n this.underlyingFs = fs ?? defaultFS;\n }\n\n private compileRules(permissions: PermissionRules): CompiledRule[] {\n return Object.entries(permissions)\n .map(([pattern, permission]) => ({\n pattern,\n permission,\n specificity: this.calculateSpecificity(pattern),\n }))\n .sort((a, b) => b.specificity - a.specificity); // highest first\n }\n\n private calculateSpecificity(pattern: string): number {\n const segments = pattern.split(\"/\").filter(Boolean);\n let score = segments.length * 1000; // segment count is primary\n\n for (const seg of segments) {\n if (seg === \"**\") score += 0;\n else if (seg.includes(\"*\")) score += 1;\n else score += 10; // literal segment\n }\n return score;\n }\n\n public getPermission(virtualPath: string): Permission {\n const normalized = virtualPath.replace(/^\\/+/, \"\"); // strip leading slashes\n\n for (const rule of this.rules) {\n if (this.matchGlob(rule.pattern, normalized)) {\n return rule.permission;\n }\n }\n return \"read-write\"; // default\n }\n\n private matchGlob(pattern: string, filePath: string): boolean {\n // Convert glob to regex\n // ** matches any path segments, * matches within segment\n const regex = pattern\n .split(\"/\")\n .map((seg) => {\n if (seg === \"**\") return \".*\";\n return seg.replace(/\\*/g, \"[^/]*\").replace(/\\?/g, \"[^/]\");\n })\n .join(\"/\");\n return new RegExp(`^${regex}$`).test(filePath);\n }\n\n public checkPermission(virtualPath: string, operation: \"read\" | \"write\"): void {\n const perm = this.getPermission(virtualPath);\n\n if (perm === \"excluded\") {\n throw new Error(`Access denied: \"${virtualPath}\" is excluded`);\n }\n if (operation === \"write\" && perm === \"read-only\") {\n throw new Error(`Access denied: \"${virtualPath}\" is read-only`);\n }\n }\n\n private resolveSafePath(virtualPath: string): string {\n if (this.mountBase === null) {\n return path.resolve(virtualPath);\n }\n\n // Check for path traversal by tracking depth\n const segments = virtualPath.split(\"/\").filter(Boolean);\n let depth = 0;\n for (const seg of segments) {\n if (seg === \"..\") {\n depth--;\n if (depth < 0) {\n throw new Error(`Path traversal blocked: \"${virtualPath}\" escapes mount point`);\n }\n } else if (seg !== \".\") {\n depth++;\n }\n }\n\n const normalized = path.normalize(virtualPath);\n const relativePath = normalized.startsWith(\"/\") ? normalized.slice(1) : normalized;\n const realPath = path.join(this.mountBase, relativePath);\n const resolved = path.resolve(realPath);\n\n // Double-check containment (defense in depth), including root mounts.\n const relativeFromMount = path.relative(this.mountBase, resolved);\n const escapesMount =\n relativeFromMount === \"..\" ||\n relativeFromMount.startsWith(`..${path.sep}`) ||\n path.isAbsolute(relativeFromMount);\n if (escapesMount) {\n throw new Error(`Path traversal blocked: \"${virtualPath}\" escapes mount point`);\n }\n\n return resolved;\n }\n\n // Read operations\n async readFile(filePath: string): Promise<Buffer>;\n async readFile(filePath: string, encoding: BufferEncoding): Promise<string>;\n async readFile(filePath: string, encoding?: BufferEncoding): Promise<Buffer | string> {\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n const content = await this.underlyingFs.promises.readFile(realPath);\n const buf = Buffer.from(content);\n return encoding ? buf.toString(encoding) : buf;\n }\n\n async readdir(dirPath: string): Promise<string[]> {\n this.checkPermission(dirPath, \"read\");\n const realPath = this.resolveSafePath(dirPath);\n const entries = await this.underlyingFs.promises.readdir(realPath);\n return entries.map(String);\n }\n\n async stat(filePath: string): Promise<FileStat> {\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n const stats = await this.underlyingFs.promises.stat(realPath);\n return {\n isFile: () => stats.isFile(),\n isDirectory: () => stats.isDirectory(),\n size: stats.size,\n mtime: stats.mtime,\n };\n }\n\n async exists(filePath: string): Promise<boolean> {\n try {\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.stat(realPath);\n return true;\n } catch {\n return false;\n }\n }\n\n // Write operations\n async writeFile(filePath: string, data: Buffer | string): Promise<void> {\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.writeFile(realPath, data);\n }\n\n async appendFile(filePath: string, data: Buffer | string): Promise<void> {\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.appendFile(realPath, data);\n }\n\n async mkdir(dirPath: string, opts?: { recursive?: boolean }): Promise<void> {\n this.checkPermission(dirPath, \"write\");\n const realPath = this.resolveSafePath(dirPath);\n await this.underlyingFs.promises.mkdir(realPath, opts);\n }\n\n async rm(filePath: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.rm(realPath, opts);\n }\n\n // Path utilities (no permission check needed)\n resolve(...paths: string[]): string {\n return path.resolve(\"/\", ...paths);\n }\n\n dirname(filePath: string): string {\n return path.dirname(filePath);\n }\n\n basename(filePath: string): string {\n return path.basename(filePath);\n }\n\n // Glob with permission filtering\n async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {\n const cwd = opts?.cwd ?? \"/\";\n this.checkPermission(cwd, \"read\");\n\n const matches = await this.expandGlob(pattern, cwd);\n\n // Filter out excluded paths\n return matches.filter((p) => this.getPermission(p) !== \"excluded\").sort();\n }\n\n // Glob expansion (similar to memfs-adapter implementation)\n private async expandGlob(pattern: string, cwd: string): Promise<string[]> {\n // Handle brace expansion first\n const patterns = this.expandBraces(pattern);\n const allMatches: string[] = [];\n\n for (const pat of patterns) {\n const matches = await this.matchPattern(pat, cwd);\n allMatches.push(...matches);\n }\n\n // Remove duplicates and sort\n return [...new Set(allMatches)].sort();\n }\n\n private expandBraces(pattern: string): string[] {\n const braceMatch = pattern.match(/\\{([^{}]+)\\}/);\n if (!braceMatch) return [pattern];\n\n const before = pattern.slice(0, braceMatch.index);\n const after = pattern.slice(braceMatch.index! + braceMatch[0].length);\n const options = braceMatch[1]!.split(\",\");\n\n const results: string[] = [];\n for (const opt of options) {\n const expanded = this.expandBraces(before + opt + after);\n results.push(...expanded);\n }\n return results;\n }\n\n private async matchPattern(pattern: string, cwd: string): Promise<string[]> {\n const parts = pattern.split(\"/\").filter((p) => p !== \"\");\n const isAbsolute = pattern.startsWith(\"/\");\n const startDir = isAbsolute ? \"/\" : cwd;\n\n return this.matchParts(parts, startDir);\n }\n\n private async matchParts(parts: string[], currentPath: string): Promise<string[]> {\n if (parts.length === 0) {\n return [currentPath];\n }\n\n const [part, ...rest] = parts;\n\n // Handle ** (recursive glob)\n if (part === \"**\") {\n const results: string[] = [];\n\n // Match current directory\n const withoutStar = await this.matchParts(rest, currentPath);\n results.push(...withoutStar);\n\n // Recurse into subdirectories\n try {\n const realPath = this.resolveSafePath(currentPath);\n const entries = await this.underlyingFs.promises.readdir(realPath);\n for (const entry of entries) {\n const entryPath = path.posix.join(currentPath, String(entry));\n try {\n const entryRealPath = this.resolveSafePath(entryPath);\n const stat = await this.underlyingFs.promises.stat(entryRealPath);\n if (stat.isDirectory()) {\n const subMatches = await this.matchParts(parts, entryPath);\n results.push(...subMatches);\n }\n } catch {\n // Skip inaccessible entries\n }\n }\n } catch {\n // Directory not readable\n }\n\n return results;\n }\n\n // Handle regular glob patterns\n const regex = this.globToRegex(part!);\n\n try {\n const realPath = this.resolveSafePath(currentPath);\n const entries = await this.underlyingFs.promises.readdir(realPath);\n const results: string[] = [];\n\n for (const entry of entries) {\n const entryName = String(entry);\n if (regex.test(entryName)) {\n const entryPath = path.posix.join(currentPath, entryName);\n if (rest.length === 0) {\n results.push(entryPath);\n } else {\n try {\n const entryRealPath = this.resolveSafePath(entryPath);\n const stat = await this.underlyingFs.promises.stat(entryRealPath);\n if (stat.isDirectory()) {\n const subMatches = await this.matchParts(rest, entryPath);\n results.push(...subMatches);\n }\n } catch {\n // Skip inaccessible entries\n }\n }\n }\n }\n\n return results;\n } catch {\n return [];\n }\n }\n\n private globToRegex(pattern: string): RegExp {\n let regex = \"^\";\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i]!;\n if (char === \"*\") {\n regex += \"[^/]*\";\n } else if (char === \"?\") {\n regex += \"[^/]\";\n } else if (char === \"[\") {\n // Character class\n let j = i + 1;\n let classContent = \"\";\n while (j < pattern.length && pattern[j] !== \"]\") {\n classContent += pattern[j];\n j++;\n }\n regex += `[${classContent}]`;\n i = j;\n } else if (\".+^${}()|\\\\\".includes(char)) {\n regex += \"\\\\\" + char;\n } else {\n regex += char;\n }\n }\n regex += \"$\";\n return new RegExp(regex);\n }\n}\n"
5
+ "import * as path from \"path\";\nimport * as nodeFs from \"node:fs/promises\";\nimport type { VirtualFS, FileStat } from \"../types.cjs\";\nimport { globVirtualFS } from \"../utils/glob.cjs\";\n\nexport type Permission = \"read-write\" | \"read-only\" | \"excluded\";\nexport type PermissionRules = Record<string, Permission>;\nexport interface PathOps {\n readonly separator: string;\n resolve(...paths: string[]): string;\n normalize(path: string): string;\n join(...paths: string[]): string;\n relative(from: string, to: string): string;\n isAbsolute(path: string): boolean;\n dirname(path: string): string;\n basename(path: string): string;\n}\n\n// Minimal interface for the underlying fs (compatible with node:fs and memfs)\nexport interface UnderlyingFS {\n pathOps?: PathOps;\n promises: {\n readFile(path: string): Promise<Buffer | Uint8Array | string>;\n readdir(path: string): Promise<string[]>;\n stat(path: string): Promise<{\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n }>;\n writeFile(path: string, data: Buffer | string): Promise<void>;\n appendFile(path: string, data: Buffer | string): Promise<void>;\n mkdir(path: string, opts?: { recursive?: boolean }): Promise<string | undefined | void>;\n rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void>;\n };\n}\n\n// Default: use real node:fs\nconst defaultFS: UnderlyingFS = { promises: nodeFs };\nconst nodePathOps: PathOps = {\n separator: path.sep,\n resolve: (...paths) => path.resolve(...paths),\n normalize: (filePath) => path.normalize(filePath),\n join: (...paths) => path.join(...paths),\n relative: (from, to) => path.relative(from, to),\n isAbsolute: (filePath) => path.isAbsolute(filePath),\n dirname: (filePath) => path.dirname(filePath),\n basename: (filePath) => path.basename(filePath),\n};\n\ninterface CompiledRule {\n pattern: string;\n permission: Permission;\n specificity: number;\n}\n\nexport class FileSystem implements VirtualFS {\n private readonly mountBase: string | null;\n private readonly rules: CompiledRule[];\n private readonly pathOps: PathOps;\n protected readonly underlyingFs: UnderlyingFS;\n\n constructor(mountPath?: string, permissions?: PermissionRules, fs?: UnderlyingFS) {\n const underlyingFs = fs ?? defaultFS;\n this.pathOps = underlyingFs.pathOps ?? nodePathOps;\n this.mountBase = mountPath ? this.pathOps.resolve(mountPath) : null;\n this.rules = this.compileRules(permissions ?? {});\n this.underlyingFs = underlyingFs;\n }\n\n private compileRules(permissions: PermissionRules): CompiledRule[] {\n return Object.entries(permissions)\n .map(([pattern, permission]) => ({\n pattern,\n permission,\n specificity: this.calculateSpecificity(pattern),\n }))\n .sort((a, b) => b.specificity - a.specificity); // highest first\n }\n\n private calculateSpecificity(pattern: string): number {\n const segments = pattern.split(\"/\").filter(Boolean);\n let score = segments.length * 1000; // segment count is primary\n\n for (const seg of segments) {\n if (seg === \"**\") score += 0;\n else if (seg.includes(\"*\")) score += 1;\n else score += 10; // literal segment\n }\n return score;\n }\n\n public getPermission(virtualPath: string): Permission {\n const normalized = virtualPath.replace(/^\\/+/, \"\"); // strip leading slashes\n\n for (const rule of this.rules) {\n if (this.matchGlob(rule.pattern, normalized)) {\n return rule.permission;\n }\n }\n return \"read-write\"; // default\n }\n\n private matchGlob(pattern: string, filePath: string): boolean {\n // Convert glob to regex\n // ** matches any path segments, * matches within segment\n const regex = pattern\n .split(\"/\")\n .map((seg) => {\n if (seg === \"**\") return \".*\";\n return seg.replace(/\\*/g, \"[^/]*\").replace(/\\?/g, \"[^/]\");\n })\n .join(\"/\");\n return new RegExp(`^${regex}$`).test(filePath);\n }\n\n public checkPermission(virtualPath: string, operation: \"read\" | \"write\"): void {\n const perm = this.getPermission(virtualPath);\n\n if (perm === \"excluded\") {\n throw new Error(`Access denied: \"${virtualPath}\" is excluded`);\n }\n if (operation === \"write\" && perm === \"read-only\") {\n throw new Error(`Access denied: \"${virtualPath}\" is read-only`);\n }\n }\n\n private resolveSafePath(virtualPath: string): string {\n if (this.mountBase === null) {\n return this.pathOps.resolve(virtualPath);\n }\n\n // Check for path traversal by tracking depth\n const segments = virtualPath.split(\"/\").filter(Boolean);\n let depth = 0;\n for (const seg of segments) {\n if (seg === \"..\") {\n depth--;\n if (depth < 0) {\n throw new Error(`Path traversal blocked: \"${virtualPath}\" escapes mount point`);\n }\n } else if (seg !== \".\") {\n depth++;\n }\n }\n\n const normalized = this.pathOps.normalize(virtualPath);\n const relativePath = normalized.startsWith(\"/\") ? normalized.slice(1) : normalized;\n const realPath = this.pathOps.join(this.mountBase, relativePath);\n const resolved = this.pathOps.resolve(realPath);\n\n // Double-check containment (defense in depth), including root mounts.\n const relativeFromMount = this.pathOps.relative(this.mountBase, resolved);\n const escapesMount =\n relativeFromMount === \"..\" ||\n relativeFromMount.startsWith(`..${this.pathOps.separator}`) ||\n this.pathOps.isAbsolute(relativeFromMount);\n if (escapesMount) {\n throw new Error(`Path traversal blocked: \"${virtualPath}\" escapes mount point`);\n }\n\n return resolved;\n }\n\n // Read operations\n async readFile(filePath: string): Promise<Buffer>;\n async readFile(filePath: string, encoding: BufferEncoding): Promise<string>;\n async readFile(filePath: string, encoding?: BufferEncoding): Promise<Buffer | string> {\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n const content = await this.underlyingFs.promises.readFile(realPath);\n const buf = Buffer.from(content);\n return encoding ? buf.toString(encoding) : buf;\n }\n\n async readdir(dirPath: string): Promise<string[]> {\n this.checkPermission(dirPath, \"read\");\n const realPath = this.resolveSafePath(dirPath);\n const entries = await this.underlyingFs.promises.readdir(realPath);\n return entries.map(String);\n }\n\n async stat(filePath: string): Promise<FileStat> {\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n const stats = await this.underlyingFs.promises.stat(realPath);\n return {\n isFile: () => stats.isFile(),\n isDirectory: () => stats.isDirectory(),\n size: stats.size,\n mtime: stats.mtime,\n };\n }\n\n async exists(filePath: string): Promise<boolean> {\n try {\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.stat(realPath);\n return true;\n } catch {\n return false;\n }\n }\n\n // Write operations\n async writeFile(filePath: string, data: Buffer | string): Promise<void> {\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.writeFile(realPath, data);\n }\n\n async appendFile(filePath: string, data: Buffer | string): Promise<void> {\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.appendFile(realPath, data);\n }\n\n async mkdir(dirPath: string, opts?: { recursive?: boolean }): Promise<void> {\n this.checkPermission(dirPath, \"write\");\n const realPath = this.resolveSafePath(dirPath);\n await this.underlyingFs.promises.mkdir(realPath, opts);\n }\n\n async rm(filePath: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.rm(realPath, opts);\n }\n\n // Path utilities (no permission check needed)\n resolve(...paths: string[]): string {\n return this.pathOps.resolve(\"/\", ...paths);\n }\n\n dirname(filePath: string): string {\n return this.pathOps.dirname(filePath);\n }\n\n basename(filePath: string): string {\n return this.pathOps.basename(filePath);\n }\n\n // Glob expansion\n async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {\n const cwd = opts?.cwd ?? \"/\";\n this.checkPermission(cwd, \"read\");\n return globVirtualFS(this, pattern, { cwd });\n }\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "CB9942EB884530FB64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "6E371DB9ABEDFA8764756E2164756E21",
9
9
  "names": []
10
10
  }