shell-dsl 0.0.31 → 0.0.32
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.
- package/README.md +1 -1
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/commands/mv/mv.cjs +7 -1
- package/dist/cjs/src/commands/mv/mv.cjs.map +3 -3
- package/dist/cjs/src/commands/tree/tree.cjs +64 -27
- package/dist/cjs/src/commands/tree/tree.cjs.map +3 -3
- package/dist/cjs/src/fs/memfs-adapter.cjs +26 -3
- package/dist/cjs/src/fs/memfs-adapter.cjs.map +3 -3
- package/dist/cjs/src/fs/real-fs.cjs +32 -1
- package/dist/cjs/src/fs/real-fs.cjs.map +3 -3
- package/dist/cjs/src/fs/special-files.cjs +98 -0
- package/dist/cjs/src/fs/special-files.cjs.map +10 -0
- package/dist/cjs/src/interpreter/interpreter.cjs +9 -8
- package/dist/cjs/src/interpreter/interpreter.cjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/src/commands/mv/mv.mjs +7 -1
- package/dist/mjs/src/commands/mv/mv.mjs.map +3 -3
- package/dist/mjs/src/commands/tree/tree.mjs +64 -27
- package/dist/mjs/src/commands/tree/tree.mjs.map +3 -3
- package/dist/mjs/src/fs/memfs-adapter.mjs +32 -3
- package/dist/mjs/src/fs/memfs-adapter.mjs.map +3 -3
- package/dist/mjs/src/fs/real-fs.mjs +38 -1
- package/dist/mjs/src/fs/real-fs.mjs.map +3 -3
- package/dist/mjs/src/fs/special-files.mjs +58 -0
- package/dist/mjs/src/fs/special-files.mjs.map +10 -0
- package/dist/mjs/src/interpreter/interpreter.mjs +9 -8
- package/dist/mjs/src/interpreter/interpreter.mjs.map +3 -3
- package/dist/types/src/fs/special-files.d.ts +8 -0
- package/package.json +1 -1
|
@@ -8,11 +8,19 @@ var spec = {
|
|
|
8
8
|
{ short: "d" },
|
|
9
9
|
{ short: "L", takesValue: true },
|
|
10
10
|
{ short: "I", takesValue: true },
|
|
11
|
-
{ long: "dirsfirst" }
|
|
11
|
+
{ long: "dirsfirst" },
|
|
12
|
+
{ long: "prune" }
|
|
12
13
|
],
|
|
13
|
-
usage: "tree [-adI] [-L level] [-I pattern] [--dirsfirst] [directory ...]"
|
|
14
|
+
usage: "tree [-adI] [-L level] [-I pattern] [--dirsfirst] [--prune] [directory ...]"
|
|
15
|
+
};
|
|
16
|
+
var defaults = {
|
|
17
|
+
all: false,
|
|
18
|
+
directoriesOnly: false,
|
|
19
|
+
maxDepth: Infinity,
|
|
20
|
+
dirsfirst: true,
|
|
21
|
+
prune: false,
|
|
22
|
+
ignorePatterns: []
|
|
14
23
|
};
|
|
15
|
-
var defaults = { all: false, directoriesOnly: false, maxDepth: Infinity, dirsfirst: true, ignorePatterns: [] };
|
|
16
24
|
var handlerResult = {};
|
|
17
25
|
var handler = (flags, flag, value) => {
|
|
18
26
|
if (flag.short === "a")
|
|
@@ -21,6 +29,8 @@ var handler = (flags, flag, value) => {
|
|
|
21
29
|
flags.directoriesOnly = true;
|
|
22
30
|
if (flag.long === "dirsfirst")
|
|
23
31
|
flags.dirsfirst = true;
|
|
32
|
+
if (flag.long === "prune")
|
|
33
|
+
flags.prune = true;
|
|
24
34
|
if (flag.short === "I" && value) {
|
|
25
35
|
if (flags.ignorePatterns === defaults.ignorePatterns) {
|
|
26
36
|
flags.ignorePatterns = [];
|
|
@@ -50,7 +60,7 @@ var tree = async (ctx) => {
|
|
|
50
60
|
await ctx.stderr.writeText(handlerResult.error);
|
|
51
61
|
return 1;
|
|
52
62
|
}
|
|
53
|
-
const { all: showAll, directoriesOnly, maxDepth, ignorePatterns } = result.flags;
|
|
63
|
+
const { all: showAll, directoriesOnly, maxDepth, prune, ignorePatterns } = result.flags;
|
|
54
64
|
const targetPath = result.args[0] ?? ".";
|
|
55
65
|
if (maxDepth < 1) {
|
|
56
66
|
await ctx.stderr.writeText(`tree: Invalid level, must be greater than 0
|
|
@@ -75,11 +85,14 @@ var tree = async (ctx) => {
|
|
|
75
85
|
}
|
|
76
86
|
let dirCount = 0;
|
|
77
87
|
let fileCount = 0;
|
|
88
|
+
const entriesCache = new Map;
|
|
89
|
+
const visibleContentCache = new Map;
|
|
78
90
|
await ctx.stdout.writeText(targetPath + `
|
|
79
91
|
`);
|
|
80
|
-
async function
|
|
81
|
-
|
|
82
|
-
|
|
92
|
+
async function getEntries(path) {
|
|
93
|
+
const cached = entriesCache.get(path);
|
|
94
|
+
if (cached)
|
|
95
|
+
return cached;
|
|
83
96
|
let entries = await ctx.fs.readdir(path);
|
|
84
97
|
if (!showAll) {
|
|
85
98
|
entries = entries.filter((e) => !e.startsWith("."));
|
|
@@ -88,39 +101,63 @@ var tree = async (ctx) => {
|
|
|
88
101
|
entries = entries.filter((e) => !ignorePatterns.some((p) => matchGlob(p, e)));
|
|
89
102
|
}
|
|
90
103
|
entries.sort();
|
|
104
|
+
const resolvedEntries = [];
|
|
105
|
+
for (const name of entries) {
|
|
106
|
+
const entryPath = ctx.fs.resolve(path, name);
|
|
107
|
+
try {
|
|
108
|
+
const entryStat = await ctx.fs.stat(entryPath);
|
|
109
|
+
resolvedEntries.push({ name, path: entryPath, isDir: entryStat.isDirectory() });
|
|
110
|
+
} catch {}
|
|
111
|
+
}
|
|
112
|
+
entriesCache.set(path, resolvedEntries);
|
|
113
|
+
return resolvedEntries;
|
|
114
|
+
}
|
|
115
|
+
async function hasVisibleContent(path) {
|
|
116
|
+
const cached = visibleContentCache.get(path);
|
|
117
|
+
if (cached !== undefined)
|
|
118
|
+
return cached;
|
|
119
|
+
const entries = await getEntries(path);
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
if (!entry.isDir) {
|
|
122
|
+
visibleContentCache.set(path, true);
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
if (await hasVisibleContent(entry.path)) {
|
|
126
|
+
visibleContentCache.set(path, true);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
visibleContentCache.set(path, false);
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
async function printTree(path, prefix, depth) {
|
|
134
|
+
if (depth > maxDepth)
|
|
135
|
+
return;
|
|
136
|
+
const entries = await getEntries(path);
|
|
91
137
|
const dirEntries = [];
|
|
92
138
|
const fileEntries = [];
|
|
93
139
|
for (const entry of entries) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (entryStat.isDirectory()) {
|
|
98
|
-
dirEntries.push(entry);
|
|
99
|
-
} else {
|
|
100
|
-
fileEntries.push(entry);
|
|
140
|
+
if (entry.isDir) {
|
|
141
|
+
if (prune && !await hasVisibleContent(entry.path)) {
|
|
142
|
+
continue;
|
|
101
143
|
}
|
|
102
|
-
|
|
144
|
+
dirEntries.push(entry);
|
|
145
|
+
} else {
|
|
146
|
+
fileEntries.push(entry);
|
|
147
|
+
}
|
|
103
148
|
}
|
|
104
149
|
const sortedEntries = directoriesOnly ? dirEntries : [...dirEntries, ...fileEntries];
|
|
105
150
|
for (let i = 0;i < sortedEntries.length; i++) {
|
|
106
151
|
const entry = sortedEntries[i];
|
|
107
152
|
const isLast = i === sortedEntries.length - 1;
|
|
108
153
|
const connector = isLast ? "└── " : "├── ";
|
|
109
|
-
|
|
110
|
-
let isDir = false;
|
|
111
|
-
try {
|
|
112
|
-
const entryStat = await ctx.fs.stat(entryPath);
|
|
113
|
-
isDir = entryStat.isDirectory();
|
|
114
|
-
} catch {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
await ctx.stdout.writeText(prefix + connector + entry + `
|
|
154
|
+
await ctx.stdout.writeText(prefix + connector + entry.name + `
|
|
118
155
|
`);
|
|
119
|
-
if (isDir) {
|
|
156
|
+
if (entry.isDir) {
|
|
120
157
|
dirCount++;
|
|
121
158
|
if (depth < maxDepth) {
|
|
122
159
|
const newPrefix = prefix + (isLast ? " " : "│ ");
|
|
123
|
-
await printTree(
|
|
160
|
+
await printTree(entry.path, newPrefix, depth + 1);
|
|
124
161
|
}
|
|
125
162
|
} else {
|
|
126
163
|
fileCount++;
|
|
@@ -139,4 +176,4 @@ export {
|
|
|
139
176
|
tree
|
|
140
177
|
};
|
|
141
178
|
|
|
142
|
-
//# debugId=
|
|
179
|
+
//# debugId=CEDB38313AEAA77264756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/commands/tree/tree.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { Command } from \"../../types.mjs\";\nimport { createFlagParser, type FlagDefinition } from \"../../utils/flag-parser.mjs\";\nimport { matchGlob } from \"../../utils/match-glob.mjs\";\n\ninterface TreeFlags {\n all: boolean;\n directoriesOnly: boolean;\n maxDepth: number;\n dirsfirst: boolean;\n ignorePatterns: string[];\n}\n\nconst spec = {\n name: \"tree\",\n flags: [\n { short: \"a\", long: \"all\" },\n { short: \"d\" },\n { short: \"L\", takesValue: true },\n { short: \"I\", takesValue: true },\n { long: \"dirsfirst\" },\n ] as FlagDefinition[],\n usage: \"tree [-adI] [-L level] [-I pattern] [--dirsfirst] [directory ...]\",\n};\n\nconst defaults: TreeFlags = {
|
|
5
|
+
"import type { Command } from \"../../types.mjs\";\nimport { createFlagParser, type FlagDefinition } from \"../../utils/flag-parser.mjs\";\nimport { matchGlob } from \"../../utils/match-glob.mjs\";\n\ninterface TreeFlags {\n all: boolean;\n directoriesOnly: boolean;\n maxDepth: number;\n dirsfirst: boolean;\n prune: boolean;\n ignorePatterns: string[];\n}\n\nconst spec = {\n name: \"tree\",\n flags: [\n { short: \"a\", long: \"all\" },\n { short: \"d\" },\n { short: \"L\", takesValue: true },\n { short: \"I\", takesValue: true },\n { long: \"dirsfirst\" },\n { long: \"prune\" },\n ] as FlagDefinition[],\n usage: \"tree [-adI] [-L level] [-I pattern] [--dirsfirst] [--prune] [directory ...]\",\n};\n\nconst defaults: TreeFlags = {\n all: false,\n directoriesOnly: false,\n maxDepth: Infinity,\n dirsfirst: true,\n prune: false,\n ignorePatterns: [],\n};\n\ninterface HandlerResult {\n error?: string;\n}\n\nlet handlerResult: HandlerResult = {};\n\nconst handler = (flags: TreeFlags, flag: FlagDefinition, value?: string) => {\n if (flag.short === \"a\") flags.all = true;\n if (flag.short === \"d\") flags.directoriesOnly = true;\n if (flag.long === \"dirsfirst\") flags.dirsfirst = true;\n if (flag.long === \"prune\") flags.prune = true;\n if (flag.short === \"I\" && value) {\n if (flags.ignorePatterns === defaults.ignorePatterns) {\n flags.ignorePatterns = [];\n }\n flags.ignorePatterns.push(...value.split(\"|\"));\n }\n if (flag.short === \"L\" && value) {\n const depth = parseInt(value, 10);\n if (isNaN(depth) || !/^\\d+$/.test(value)) {\n handlerResult.error = `tree: -L option requires a numeric argument\\nusage: ${spec.usage}\\n`;\n } else {\n flags.maxDepth = depth;\n }\n }\n};\n\nconst parser = createFlagParser(spec, defaults, handler);\n\nexport const tree: Command = async (ctx) => {\n // Reset handler result for each invocation\n handlerResult = {};\n\n const result = parser.parse(ctx.args);\n\n if (result.error) {\n await parser.writeError(result.error, ctx.stderr);\n return 1;\n }\n\n if (handlerResult.error) {\n await ctx.stderr.writeText(handlerResult.error);\n return 1;\n }\n\n const { all: showAll, directoriesOnly, maxDepth, prune, ignorePatterns } = result.flags;\n const targetPath = result.args[0] ?? \".\";\n\n // Validate maxDepth\n if (maxDepth < 1) {\n await ctx.stderr.writeText(\"tree: Invalid level, must be greater than 0\\n\");\n return 1;\n }\n\n const resolvedPath = ctx.fs.resolve(ctx.cwd, targetPath);\n\n // Check if path exists\n let stat;\n try {\n stat = await ctx.fs.stat(resolvedPath);\n } catch {\n await ctx.stderr.writeText(`tree: ${targetPath}: No such file or directory\\n`);\n return 1;\n }\n\n // If it's a file, just print the filename\n if (stat.isFile()) {\n await ctx.stdout.writeText(targetPath + \"\\n\\n0 directories, 1 file\\n\");\n return 0;\n }\n\n let dirCount = 0;\n let fileCount = 0;\n const entriesCache = new Map<string, { name: string; path: string; isDir: boolean }[]>();\n const visibleContentCache = new Map<string, boolean>();\n\n // Print root\n await ctx.stdout.writeText(targetPath + \"\\n\");\n\n async function getEntries(path: string): Promise<{ name: string; path: string; isDir: boolean }[]> {\n const cached = entriesCache.get(path);\n if (cached) return cached;\n\n let entries = await ctx.fs.readdir(path);\n\n // Filter hidden files unless -a\n if (!showAll) {\n entries = entries.filter((e) => !e.startsWith(\".\"));\n }\n\n // Filter by -I ignore patterns\n if (ignorePatterns.length > 0) {\n entries = entries.filter((e) => !ignorePatterns.some((p) => matchGlob(p, e)));\n }\n\n // Sort entries\n entries.sort();\n\n const resolvedEntries: { name: string; path: string; isDir: boolean }[] = [];\n\n for (const name of entries) {\n const entryPath = ctx.fs.resolve(path, name);\n try {\n const entryStat = await ctx.fs.stat(entryPath);\n resolvedEntries.push({ name, path: entryPath, isDir: entryStat.isDirectory() });\n } catch {\n // Skip entries we can't stat\n }\n }\n\n entriesCache.set(path, resolvedEntries);\n return resolvedEntries;\n }\n\n async function hasVisibleContent(path: string): Promise<boolean> {\n const cached = visibleContentCache.get(path);\n if (cached !== undefined) return cached;\n\n const entries = await getEntries(path);\n\n for (const entry of entries) {\n if (!entry.isDir) {\n visibleContentCache.set(path, true);\n return true;\n }\n\n if (await hasVisibleContent(entry.path)) {\n visibleContentCache.set(path, true);\n return true;\n }\n }\n\n visibleContentCache.set(path, false);\n return false;\n }\n\n // Recursive function to build tree\n async function printTree(path: string, prefix: string, depth: number): Promise<void> {\n if (depth > maxDepth) return;\n\n const entries = await getEntries(path);\n\n // Separate dirs and files, dirs first\n const dirEntries: { name: string; path: string; isDir: boolean }[] = [];\n const fileEntries: { name: string; path: string; isDir: boolean }[] = [];\n\n for (const entry of entries) {\n if (entry.isDir) {\n if (prune && !(await hasVisibleContent(entry.path))) {\n continue;\n }\n dirEntries.push(entry);\n } else {\n fileEntries.push(entry);\n }\n }\n\n // Combine: directories first, then files (unless directoriesOnly)\n const sortedEntries = directoriesOnly\n ? dirEntries\n : [...dirEntries, ...fileEntries];\n\n for (let i = 0; i < sortedEntries.length; i++) {\n const entry = sortedEntries[i]!;\n const isLast = i === sortedEntries.length - 1;\n const connector = isLast ? \"└── \" : \"├── \";\n\n await ctx.stdout.writeText(prefix + connector + entry.name + \"\\n\");\n\n if (entry.isDir) {\n dirCount++;\n if (depth < maxDepth) {\n const newPrefix = prefix + (isLast ? \" \" : \"│ \");\n await printTree(entry.path, newPrefix, depth + 1);\n }\n } else {\n fileCount++;\n }\n }\n }\n\n await printTree(resolvedPath, \"\", 1);\n\n // Print summary\n const dirWord = dirCount === 1 ? \"directory\" : \"directories\";\n const fileWord = fileCount === 1 ? \"file\" : \"files\";\n await ctx.stdout.writeText(`\\n${dirCount} ${dirWord}, ${fileCount} ${fileWord}\\n`);\n\n return 0;\n};\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AACA;AACA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "CEDB38313AEAA77264756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
// src/fs/memfs-adapter.ts
|
|
2
2
|
import * as pathModule from "path";
|
|
3
3
|
import { globVirtualFS } from "../utils/glob.mjs";
|
|
4
|
+
import {
|
|
5
|
+
discardsSpecialFileWrites,
|
|
6
|
+
existsSpecialFile,
|
|
7
|
+
getSpecialPathError,
|
|
8
|
+
readSpecialFile,
|
|
9
|
+
statSpecialFile
|
|
10
|
+
} from "./special-files.mjs";
|
|
4
11
|
function createVirtualFS(memfs) {
|
|
5
12
|
const { promises: fs } = memfs;
|
|
6
13
|
return {
|
|
7
14
|
readFile: async (path, encoding) => {
|
|
8
|
-
|
|
9
|
-
|
|
15
|
+
const specialContent = readSpecialFile(path, encoding);
|
|
16
|
+
if (specialContent !== undefined)
|
|
17
|
+
return specialContent;
|
|
10
18
|
const content = await fs.readFile(path);
|
|
11
19
|
const buf = Buffer.from(content);
|
|
12
20
|
return encoding ? buf.toString(encoding) : buf;
|
|
13
21
|
},
|
|
14
22
|
async readdir(path) {
|
|
23
|
+
const specialError = getSpecialPathError(path, "readdir");
|
|
24
|
+
if (specialError)
|
|
25
|
+
throw specialError;
|
|
15
26
|
const entries = await fs.readdir(path);
|
|
16
27
|
return entries.map(String);
|
|
17
28
|
},
|
|
18
29
|
async stat(path) {
|
|
30
|
+
const specialStat = statSpecialFile(path);
|
|
31
|
+
if (specialStat)
|
|
32
|
+
return specialStat;
|
|
19
33
|
const stats = await fs.stat(path);
|
|
20
34
|
return {
|
|
21
35
|
isFile: () => stats.isFile(),
|
|
@@ -25,6 +39,9 @@ function createVirtualFS(memfs) {
|
|
|
25
39
|
};
|
|
26
40
|
},
|
|
27
41
|
async exists(path) {
|
|
42
|
+
const specialExists = existsSpecialFile(path);
|
|
43
|
+
if (specialExists !== undefined)
|
|
44
|
+
return specialExists;
|
|
28
45
|
try {
|
|
29
46
|
await fs.stat(path);
|
|
30
47
|
return true;
|
|
@@ -33,15 +50,27 @@ function createVirtualFS(memfs) {
|
|
|
33
50
|
}
|
|
34
51
|
},
|
|
35
52
|
async writeFile(path, data) {
|
|
53
|
+
if (discardsSpecialFileWrites(path)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
36
56
|
await fs.writeFile(path, data);
|
|
37
57
|
},
|
|
38
58
|
async appendFile(path, data) {
|
|
59
|
+
if (discardsSpecialFileWrites(path)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
39
62
|
await fs.appendFile(path, data);
|
|
40
63
|
},
|
|
41
64
|
async mkdir(path, opts) {
|
|
65
|
+
const specialError = getSpecialPathError(path, "mkdir");
|
|
66
|
+
if (specialError)
|
|
67
|
+
throw specialError;
|
|
42
68
|
await fs.mkdir(path, opts);
|
|
43
69
|
},
|
|
44
70
|
async rm(path, opts) {
|
|
71
|
+
const specialError = getSpecialPathError(path, "rm");
|
|
72
|
+
if (specialError)
|
|
73
|
+
throw specialError;
|
|
45
74
|
try {
|
|
46
75
|
const stats = await fs.stat(path);
|
|
47
76
|
if (stats.isDirectory()) {
|
|
@@ -77,4 +106,4 @@ export {
|
|
|
77
106
|
createVirtualFS
|
|
78
107
|
};
|
|
79
108
|
|
|
80
|
-
//# debugId=
|
|
109
|
+
//# debugId=DF93BEF2761EBF0164756E2164756E21
|
|
@@ -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.mjs\";\nimport * as pathModule from \"path\";\nimport { globVirtualFS } from \"../utils/glob.mjs\";\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
|
|
5
|
+
"import type { IFs } from \"memfs\";\nimport type { VirtualFS, FileStat } from \"../types.mjs\";\nimport * as pathModule from \"path\";\nimport { globVirtualFS } from \"../utils/glob.mjs\";\nimport {\n discardsSpecialFileWrites,\n existsSpecialFile,\n getSpecialPathError,\n readSpecialFile,\n statSpecialFile,\n} from \"./special-files.mjs\";\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 const specialContent = readSpecialFile(path, encoding);\n if (specialContent !== undefined) return specialContent;\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 specialError = getSpecialPathError(path, \"readdir\");\n if (specialError) throw specialError;\n const entries = await fs.readdir(path);\n return entries.map(String);\n },\n\n async stat(path: string): Promise<FileStat> {\n const specialStat = statSpecialFile(path);\n if (specialStat) return specialStat;\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 const specialExists = existsSpecialFile(path);\n if (specialExists !== undefined) return specialExists;\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 if (discardsSpecialFileWrites(path)) {\n return;\n }\n await fs.writeFile(path, data);\n },\n\n async appendFile(path: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(path)) {\n return;\n }\n await fs.appendFile(path, data);\n },\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(path, \"mkdir\");\n if (specialError) throw specialError;\n await fs.mkdir(path, opts);\n },\n\n async rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(path, \"rm\");\n if (specialError) throw specialError;\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": ";AAEA;AACA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "DF93BEF2761EBF0164756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import * as nodeFs from "node:fs/promises";
|
|
4
4
|
import { globVirtualFS } from "../utils/glob.mjs";
|
|
5
|
+
import {
|
|
6
|
+
discardsSpecialFileWrites,
|
|
7
|
+
existsSpecialFile,
|
|
8
|
+
getSpecialPathError,
|
|
9
|
+
readSpecialFile,
|
|
10
|
+
statSpecialFile
|
|
11
|
+
} from "./special-files.mjs";
|
|
5
12
|
var defaultFS = { promises: nodeFs };
|
|
6
13
|
var nodePathOps = {
|
|
7
14
|
separator: path.sep,
|
|
@@ -100,6 +107,10 @@ class FileSystem {
|
|
|
100
107
|
return resolved;
|
|
101
108
|
}
|
|
102
109
|
async readFile(filePath, encoding) {
|
|
110
|
+
const specialContent = readSpecialFile(filePath, encoding);
|
|
111
|
+
if (specialContent !== undefined) {
|
|
112
|
+
return specialContent;
|
|
113
|
+
}
|
|
103
114
|
this.checkPermission(filePath, "read");
|
|
104
115
|
const realPath = this.resolveSafePath(filePath);
|
|
105
116
|
const content = await this.underlyingFs.promises.readFile(realPath);
|
|
@@ -107,12 +118,20 @@ class FileSystem {
|
|
|
107
118
|
return encoding ? buf.toString(encoding) : buf;
|
|
108
119
|
}
|
|
109
120
|
async readdir(dirPath) {
|
|
121
|
+
const specialError = getSpecialPathError(dirPath, "readdir");
|
|
122
|
+
if (specialError) {
|
|
123
|
+
throw specialError;
|
|
124
|
+
}
|
|
110
125
|
this.checkPermission(dirPath, "read");
|
|
111
126
|
const realPath = this.resolveSafePath(dirPath);
|
|
112
127
|
const entries = await this.underlyingFs.promises.readdir(realPath);
|
|
113
128
|
return entries.map(String);
|
|
114
129
|
}
|
|
115
130
|
async stat(filePath) {
|
|
131
|
+
const specialStat = statSpecialFile(filePath);
|
|
132
|
+
if (specialStat) {
|
|
133
|
+
return specialStat;
|
|
134
|
+
}
|
|
116
135
|
this.checkPermission(filePath, "read");
|
|
117
136
|
const realPath = this.resolveSafePath(filePath);
|
|
118
137
|
const stats = await this.underlyingFs.promises.stat(realPath);
|
|
@@ -124,6 +143,10 @@ class FileSystem {
|
|
|
124
143
|
};
|
|
125
144
|
}
|
|
126
145
|
async exists(filePath) {
|
|
146
|
+
const specialExists = existsSpecialFile(filePath);
|
|
147
|
+
if (specialExists !== undefined) {
|
|
148
|
+
return specialExists;
|
|
149
|
+
}
|
|
127
150
|
try {
|
|
128
151
|
this.checkPermission(filePath, "read");
|
|
129
152
|
const realPath = this.resolveSafePath(filePath);
|
|
@@ -134,21 +157,35 @@ class FileSystem {
|
|
|
134
157
|
}
|
|
135
158
|
}
|
|
136
159
|
async writeFile(filePath, data) {
|
|
160
|
+
if (discardsSpecialFileWrites(filePath)) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
137
163
|
this.checkPermission(filePath, "write");
|
|
138
164
|
const realPath = this.resolveSafePath(filePath);
|
|
139
165
|
await this.underlyingFs.promises.writeFile(realPath, data);
|
|
140
166
|
}
|
|
141
167
|
async appendFile(filePath, data) {
|
|
168
|
+
if (discardsSpecialFileWrites(filePath)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
142
171
|
this.checkPermission(filePath, "write");
|
|
143
172
|
const realPath = this.resolveSafePath(filePath);
|
|
144
173
|
await this.underlyingFs.promises.appendFile(realPath, data);
|
|
145
174
|
}
|
|
146
175
|
async mkdir(dirPath, opts) {
|
|
176
|
+
const specialError = getSpecialPathError(dirPath, "mkdir");
|
|
177
|
+
if (specialError) {
|
|
178
|
+
throw specialError;
|
|
179
|
+
}
|
|
147
180
|
this.checkPermission(dirPath, "write");
|
|
148
181
|
const realPath = this.resolveSafePath(dirPath);
|
|
149
182
|
await this.underlyingFs.promises.mkdir(realPath, opts);
|
|
150
183
|
}
|
|
151
184
|
async rm(filePath, opts) {
|
|
185
|
+
const specialError = getSpecialPathError(filePath, "rm");
|
|
186
|
+
if (specialError) {
|
|
187
|
+
throw specialError;
|
|
188
|
+
}
|
|
152
189
|
this.checkPermission(filePath, "write");
|
|
153
190
|
const realPath = this.resolveSafePath(filePath);
|
|
154
191
|
await this.underlyingFs.promises.rm(realPath, opts);
|
|
@@ -172,4 +209,4 @@ export {
|
|
|
172
209
|
FileSystem
|
|
173
210
|
};
|
|
174
211
|
|
|
175
|
-
//# debugId=
|
|
212
|
+
//# debugId=9ACC9F274DA10B5E64756E2164756E21
|
|
@@ -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.mjs\";\nimport { globVirtualFS } from \"../utils/glob.mjs\";\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"
|
|
5
|
+
"import * as path from \"path\";\nimport * as nodeFs from \"node:fs/promises\";\nimport type { VirtualFS, FileStat } from \"../types.mjs\";\nimport { globVirtualFS } from \"../utils/glob.mjs\";\nimport {\n discardsSpecialFileWrites,\n existsSpecialFile,\n getSpecialPathError,\n readSpecialFile,\n statSpecialFile,\n} from \"./special-files.mjs\";\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 const specialContent = readSpecialFile(filePath, encoding);\n if (specialContent !== undefined) {\n return specialContent;\n }\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 const specialError = getSpecialPathError(dirPath, \"readdir\");\n if (specialError) {\n throw specialError;\n }\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 const specialStat = statSpecialFile(filePath);\n if (specialStat) {\n return specialStat;\n }\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 const specialExists = existsSpecialFile(filePath);\n if (specialExists !== undefined) {\n return specialExists;\n }\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 if (discardsSpecialFileWrites(filePath)) {\n return;\n }\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 if (discardsSpecialFileWrites(filePath)) {\n return;\n }\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 const specialError = getSpecialPathError(dirPath, \"mkdir\");\n if (specialError) {\n throw specialError;\n }\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 const specialError = getSpecialPathError(filePath, \"rm\");\n if (specialError) {\n throw specialError;\n }\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": ";AAAA;AACA;AAEA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyCA,IAAM,YAA0B,EAAE,UAAU,OAAO;AACnD,IAAM,cAAuB;AAAA,EAC3B,WAAgB;AAAA,EAChB,SAAS,IAAI,UAAe,aAAQ,GAAG,KAAK;AAAA,EAC5C,WAAW,CAAC,aAAkB,eAAU,QAAQ;AAAA,EAChD,MAAM,IAAI,UAAe,UAAK,GAAG,KAAK;AAAA,EACtC,UAAU,CAAC,MAAM,OAAY,cAAS,MAAM,EAAE;AAAA,EAC9C,YAAY,CAAC,aAAkB,gBAAW,QAAQ;AAAA,EAClD,SAAS,CAAC,aAAkB,aAAQ,QAAQ;AAAA,EAC5C,UAAU,CAAC,aAAkB,cAAS,QAAQ;AAChD;AAAA;AAQO,MAAM,WAAgC;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACE;AAAA,EAEnB,WAAW,CAAC,WAAoB,aAA+B,IAAmB;AAAA,IAChF,MAAM,eAAe,MAAM;AAAA,IAC3B,KAAK,UAAU,aAAa,WAAW;AAAA,IACvC,KAAK,YAAY,YAAY,KAAK,QAAQ,QAAQ,SAAS,IAAI;AAAA,IAC/D,KAAK,QAAQ,KAAK,aAAa,eAAe,CAAC,CAAC;AAAA,IAChD,KAAK,eAAe;AAAA;AAAA,EAGd,YAAY,CAAC,aAA8C;AAAA,IACjE,OAAO,OAAO,QAAQ,WAAW,EAC9B,IAAI,EAAE,SAAS,iBAAiB;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,aAAa,KAAK,qBAAqB,OAAO;AAAA,IAChD,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,EAAE,WAAW;AAAA;AAAA,EAGzC,oBAAoB,CAAC,SAAyB;AAAA,IACpD,MAAM,WAAW,QAAQ,MAAM,GAAG,EAAE,OAAO,OAAO;AAAA,IAClD,IAAI,QAAQ,SAAS,SAAS;AAAA,IAE9B,WAAW,OAAO,UAAU;AAAA,MAC1B,IAAI,QAAQ;AAAA,QAAM,SAAS;AAAA,MACtB,SAAI,IAAI,SAAS,GAAG;AAAA,QAAG,SAAS;AAAA,MAChC;AAAA,iBAAS;AAAA,IAChB;AAAA,IACA,OAAO;AAAA;AAAA,EAGF,aAAa,CAAC,aAAiC;AAAA,IACpD,MAAM,aAAa,YAAY,QAAQ,QAAQ,EAAE;AAAA,IAEjD,WAAW,QAAQ,KAAK,OAAO;AAAA,MAC7B,IAAI,KAAK,UAAU,KAAK,SAAS,UAAU,GAAG;AAAA,QAC5C,OAAO,KAAK;AAAA,MACd;AAAA,IACF;AAAA,IACA,OAAO;AAAA;AAAA,EAGD,SAAS,CAAC,SAAiB,UAA2B;AAAA,IAG5D,MAAM,QAAQ,QACX,MAAM,GAAG,EACT,IAAI,CAAC,QAAQ;AAAA,MACZ,IAAI,QAAQ;AAAA,QAAM,OAAO;AAAA,MACzB,OAAO,IAAI,QAAQ,OAAO,OAAO,EAAE,QAAQ,OAAO,MAAM;AAAA,KACzD,EACA,KAAK,GAAG;AAAA,IACX,OAAO,IAAI,OAAO,IAAI,QAAQ,EAAE,KAAK,QAAQ;AAAA;AAAA,EAGxC,eAAe,CAAC,aAAqB,WAAmC;AAAA,IAC7E,MAAM,OAAO,KAAK,cAAc,WAAW;AAAA,IAE3C,IAAI,SAAS,YAAY;AAAA,MACvB,MAAM,IAAI,MAAM,mBAAmB,0BAA0B;AAAA,IAC/D;AAAA,IACA,IAAI,cAAc,WAAW,SAAS,aAAa;AAAA,MACjD,MAAM,IAAI,MAAM,mBAAmB,2BAA2B;AAAA,IAChE;AAAA;AAAA,EAGM,eAAe,CAAC,aAA6B;AAAA,IACnD,IAAI,KAAK,cAAc,MAAM;AAAA,MAC3B,OAAO,KAAK,QAAQ,QAAQ,WAAW;AAAA,IACzC;AAAA,IAGA,MAAM,WAAW,YAAY,MAAM,GAAG,EAAE,OAAO,OAAO;AAAA,IACtD,IAAI,QAAQ;AAAA,IACZ,WAAW,OAAO,UAAU;AAAA,MAC1B,IAAI,QAAQ,MAAM;AAAA,QAChB;AAAA,QACA,IAAI,QAAQ,GAAG;AAAA,UACb,MAAM,IAAI,MAAM,4BAA4B,kCAAkC;AAAA,QAChF;AAAA,MACF,EAAO,SAAI,QAAQ,KAAK;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,aAAa,KAAK,QAAQ,UAAU,WAAW;AAAA,IACrD,MAAM,eAAe,WAAW,WAAW,GAAG,IAAI,WAAW,MAAM,CAAC,IAAI;AAAA,IACxE,MAAM,WAAW,KAAK,QAAQ,KAAK,KAAK,WAAW,YAAY;AAAA,IAC/D,MAAM,WAAW,KAAK,QAAQ,QAAQ,QAAQ;AAAA,IAG9C,MAAM,oBAAoB,KAAK,QAAQ,SAAS,KAAK,WAAW,QAAQ;AAAA,IACxE,MAAM,eACJ,sBAAsB,QACtB,kBAAkB,WAAW,KAAK,KAAK,QAAQ,WAAW,KAC1D,KAAK,QAAQ,WAAW,iBAAiB;AAAA,IAC3C,IAAI,cAAc;AAAA,MAChB,MAAM,IAAI,MAAM,4BAA4B,kCAAkC;AAAA,IAChF;AAAA,IAEA,OAAO;AAAA;AAAA,OAMH,SAAQ,CAAC,UAAkB,UAAqD;AAAA,IACpF,MAAM,iBAAiB,gBAAgB,UAAU,QAAQ;AAAA,IACzD,IAAI,mBAAmB,WAAW;AAAA,MAChC,OAAO;AAAA,IACT;AAAA,IACA,KAAK,gBAAgB,UAAU,MAAM;AAAA,IACrC,MAAM,WAAW,KAAK,gBAAgB,QAAQ;AAAA,IAC9C,MAAM,UAAU,MAAM,KAAK,aAAa,SAAS,SAAS,QAAQ;AAAA,IAClE,MAAM,MAAM,OAAO,KAAK,OAAO;AAAA,IAC/B,OAAO,WAAW,IAAI,SAAS,QAAQ,IAAI;AAAA;AAAA,OAGvC,QAAO,CAAC,SAAoC;AAAA,IAChD,MAAM,eAAe,oBAAoB,SAAS,SAAS;AAAA,IAC3D,IAAI,cAAc;AAAA,MAChB,MAAM;AAAA,IACR;AAAA,IACA,KAAK,gBAAgB,SAAS,MAAM;AAAA,IACpC,MAAM,WAAW,KAAK,gBAAgB,OAAO;AAAA,IAC7C,MAAM,UAAU,MAAM,KAAK,aAAa,SAAS,QAAQ,QAAQ;AAAA,IACjE,OAAO,QAAQ,IAAI,MAAM;AAAA;AAAA,OAGrB,KAAI,CAAC,UAAqC;AAAA,IAC9C,MAAM,cAAc,gBAAgB,QAAQ;AAAA,IAC5C,IAAI,aAAa;AAAA,MACf,OAAO;AAAA,IACT;AAAA,IACA,KAAK,gBAAgB,UAAU,MAAM;AAAA,IACrC,MAAM,WAAW,KAAK,gBAAgB,QAAQ;AAAA,IAC9C,MAAM,QAAQ,MAAM,KAAK,aAAa,SAAS,KAAK,QAAQ;AAAA,IAC5D,OAAO;AAAA,MACL,QAAQ,MAAM,MAAM,OAAO;AAAA,MAC3B,aAAa,MAAM,MAAM,YAAY;AAAA,MACrC,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,IACf;AAAA;AAAA,OAGI,OAAM,CAAC,UAAoC;AAAA,IAC/C,MAAM,gBAAgB,kBAAkB,QAAQ;AAAA,IAChD,IAAI,kBAAkB,WAAW;AAAA,MAC/B,OAAO;AAAA,IACT;AAAA,IACA,IAAI;AAAA,MACF,KAAK,gBAAgB,UAAU,MAAM;AAAA,MACrC,MAAM,WAAW,KAAK,gBAAgB,QAAQ;AAAA,MAC9C,MAAM,KAAK,aAAa,SAAS,KAAK,QAAQ;AAAA,MAC9C,OAAO;AAAA,MACP,MAAM;AAAA,MACN,OAAO;AAAA;AAAA;AAAA,OAKL,UAAS,CAAC,UAAkB,MAAsC;AAAA,IACtE,IAAI,0BAA0B,QAAQ,GAAG;AAAA,MACvC;AAAA,IACF;AAAA,IACA,KAAK,gBAAgB,UAAU,OAAO;AAAA,IACtC,MAAM,WAAW,KAAK,gBAAgB,QAAQ;AAAA,IAC9C,MAAM,KAAK,aAAa,SAAS,UAAU,UAAU,IAAI;AAAA;AAAA,OAGrD,WAAU,CAAC,UAAkB,MAAsC;AAAA,IACvE,IAAI,0BAA0B,QAAQ,GAAG;AAAA,MACvC;AAAA,IACF;AAAA,IACA,KAAK,gBAAgB,UAAU,OAAO;AAAA,IACtC,MAAM,WAAW,KAAK,gBAAgB,QAAQ;AAAA,IAC9C,MAAM,KAAK,aAAa,SAAS,WAAW,UAAU,IAAI;AAAA;AAAA,OAGtD,MAAK,CAAC,SAAiB,MAA+C;AAAA,IAC1E,MAAM,eAAe,oBAAoB,SAAS,OAAO;AAAA,IACzD,IAAI,cAAc;AAAA,MAChB,MAAM;AAAA,IACR;AAAA,IACA,KAAK,gBAAgB,SAAS,OAAO;AAAA,IACrC,MAAM,WAAW,KAAK,gBAAgB,OAAO;AAAA,IAC7C,MAAM,KAAK,aAAa,SAAS,MAAM,UAAU,IAAI;AAAA;AAAA,OAGjD,GAAE,CAAC,UAAkB,MAAgE;AAAA,IACzF,MAAM,eAAe,oBAAoB,UAAU,IAAI;AAAA,IACvD,IAAI,cAAc;AAAA,MAChB,MAAM;AAAA,IACR;AAAA,IACA,KAAK,gBAAgB,UAAU,OAAO;AAAA,IACtC,MAAM,WAAW,KAAK,gBAAgB,QAAQ;AAAA,IAC9C,MAAM,KAAK,aAAa,SAAS,GAAG,UAAU,IAAI;AAAA;AAAA,EAIpD,OAAO,IAAI,OAAyB;AAAA,IAClC,OAAO,KAAK,QAAQ,QAAQ,KAAK,GAAG,KAAK;AAAA;AAAA,EAG3C,OAAO,CAAC,UAA0B;AAAA,IAChC,OAAO,KAAK,QAAQ,QAAQ,QAAQ;AAAA;AAAA,EAGtC,QAAQ,CAAC,UAA0B;AAAA,IACjC,OAAO,KAAK,QAAQ,SAAS,QAAQ;AAAA;AAAA,OAIjC,KAAI,CAAC,SAAiB,MAA4C;AAAA,IACtE,MAAM,MAAM,MAAM,OAAO;AAAA,IACzB,KAAK,gBAAgB,KAAK,MAAM;AAAA,IAChC,OAAO,cAAc,MAAM,SAAS,EAAE,IAAI,CAAC;AAAA;AAE/C;",
|
|
8
|
+
"debugId": "9ACC9F274DA10B5E64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/fs/special-files.ts
|
|
2
|
+
var DEV_NULL_PATH = "/dev/null";
|
|
3
|
+
function normalizeSpecialPath(path) {
|
|
4
|
+
return path.replace(/\\/g, "/");
|
|
5
|
+
}
|
|
6
|
+
function isDevNullPath(path) {
|
|
7
|
+
return normalizeSpecialPath(path) === DEV_NULL_PATH;
|
|
8
|
+
}
|
|
9
|
+
function readSpecialFile(path, encoding) {
|
|
10
|
+
if (!isDevNullPath(path)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
return encoding ? "" : Buffer.alloc(0);
|
|
14
|
+
}
|
|
15
|
+
function statSpecialFile(path) {
|
|
16
|
+
if (!isDevNullPath(path)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
isFile: () => true,
|
|
21
|
+
isDirectory: () => false,
|
|
22
|
+
size: 0,
|
|
23
|
+
mtime: new Date(0)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function existsSpecialFile(path) {
|
|
27
|
+
if (!isDevNullPath(path)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
function discardsSpecialFileWrites(path) {
|
|
33
|
+
return isDevNullPath(path);
|
|
34
|
+
}
|
|
35
|
+
function getSpecialPathError(path, operation) {
|
|
36
|
+
if (!isDevNullPath(path)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
switch (operation) {
|
|
40
|
+
case "mkdir":
|
|
41
|
+
return new Error(`EEXIST: file already exists, mkdir '${DEV_NULL_PATH}'`);
|
|
42
|
+
case "readdir":
|
|
43
|
+
return new Error(`ENOTDIR: not a directory, scandir '${DEV_NULL_PATH}'`);
|
|
44
|
+
case "rm":
|
|
45
|
+
return new Error(`EPERM: operation not permitted, rm '${DEV_NULL_PATH}'`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
statSpecialFile,
|
|
50
|
+
readSpecialFile,
|
|
51
|
+
isDevNullPath,
|
|
52
|
+
getSpecialPathError,
|
|
53
|
+
existsSpecialFile,
|
|
54
|
+
discardsSpecialFileWrites,
|
|
55
|
+
DEV_NULL_PATH
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
//# debugId=5E984ABB0F12E5E964756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/fs/special-files.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { FileStat } from \"../types.mjs\";\n\nexport const DEV_NULL_PATH = \"/dev/null\";\n\nfunction normalizeSpecialPath(path: string): string {\n return path.replace(/\\\\/g, \"/\");\n}\n\nexport function isDevNullPath(path: string): boolean {\n return normalizeSpecialPath(path) === DEV_NULL_PATH;\n}\n\nexport function readSpecialFile(\n path: string,\n encoding?: BufferEncoding\n): Buffer | string | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n return encoding ? \"\" : Buffer.alloc(0);\n}\n\nexport function statSpecialFile(path: string): FileStat | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n return {\n isFile: () => true,\n isDirectory: () => false,\n size: 0,\n mtime: new Date(0),\n };\n}\n\nexport function existsSpecialFile(path: string): boolean | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n return true;\n}\n\nexport function discardsSpecialFileWrites(path: string): boolean {\n return isDevNullPath(path);\n}\n\nexport function getSpecialPathError(\n path: string,\n operation: \"mkdir\" | \"readdir\" | \"rm\"\n): Error | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n switch (operation) {\n case \"mkdir\":\n return new Error(`EEXIST: file already exists, mkdir '${DEV_NULL_PATH}'`);\n case \"readdir\":\n return new Error(`ENOTDIR: not a directory, scandir '${DEV_NULL_PATH}'`);\n case \"rm\":\n return new Error(`EPERM: operation not permitted, rm '${DEV_NULL_PATH}'`);\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AAEO,IAAM,gBAAgB;AAE7B,SAAS,oBAAoB,CAAC,MAAsB;AAAA,EAClD,OAAO,KAAK,QAAQ,OAAO,GAAG;AAAA;AAGzB,SAAS,aAAa,CAAC,MAAuB;AAAA,EACnD,OAAO,qBAAqB,IAAI,MAAM;AAAA;AAGjC,SAAS,eAAe,CAC7B,MACA,UAC6B;AAAA,EAC7B,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,OAAO,WAAW,KAAK,OAAO,MAAM,CAAC;AAAA;AAGhC,SAAS,eAAe,CAAC,MAAoC;AAAA,EAClE,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,OAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM;AAAA,IACnB,MAAM;AAAA,IACN,OAAO,IAAI,KAAK,CAAC;AAAA,EACnB;AAAA;AAGK,SAAS,iBAAiB,CAAC,MAAmC;AAAA,EACnE,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,OAAO;AAAA;AAGF,SAAS,yBAAyB,CAAC,MAAuB;AAAA,EAC/D,OAAO,cAAc,IAAI;AAAA;AAGpB,SAAS,mBAAmB,CACjC,MACA,WACmB;AAAA,EACnB,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,QAAQ;AAAA,SACD;AAAA,MACH,OAAO,IAAI,MAAM,uCAAuC,gBAAgB;AAAA,SACrE;AAAA,MACH,OAAO,IAAI,MAAM,sCAAsC,gBAAgB;AAAA,SACpE;AAAA,MACH,OAAO,IAAI,MAAM,uCAAuC,gBAAgB;AAAA;AAAA;",
|
|
8
|
+
"debugId": "5E984ABB0F12E5E964756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|