shell-dsl 0.0.34 → 0.0.36
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 +41 -5
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/fs/memfs-adapter.cjs +56 -2
- package/dist/cjs/src/fs/memfs-adapter.cjs.map +3 -3
- package/dist/cjs/src/fs/real-fs.cjs +134 -3
- package/dist/cjs/src/fs/real-fs.cjs.map +3 -3
- package/dist/cjs/src/fs/special-files.cjs +3 -2
- package/dist/cjs/src/fs/special-files.cjs.map +3 -3
- package/dist/cjs/src/fs/web-fs.cjs +72 -3
- package/dist/cjs/src/fs/web-fs.cjs.map +3 -3
- package/dist/cjs/src/index.cjs +2 -7
- package/dist/cjs/src/index.cjs.map +3 -3
- package/dist/cjs/src/interpreter/interpreter.cjs +186 -68
- package/dist/cjs/src/interpreter/interpreter.cjs.map +3 -3
- package/dist/cjs/src/parser/ast.cjs +5 -25
- package/dist/cjs/src/parser/ast.cjs.map +3 -3
- package/dist/cjs/src/parser/index.cjs +2 -7
- package/dist/cjs/src/parser/index.cjs.map +3 -3
- package/dist/cjs/src/parser/parser.cjs +132 -82
- package/dist/cjs/src/parser/parser.cjs.map +3 -3
- package/dist/cjs/src/types.cjs.map +2 -2
- package/dist/cjs/src/vcs/content.cjs +106 -0
- package/dist/cjs/src/vcs/content.cjs.map +10 -0
- package/dist/cjs/src/vcs/diff.cjs +72 -28
- package/dist/cjs/src/vcs/diff.cjs.map +3 -3
- package/dist/cjs/src/vcs/index.cjs.map +1 -1
- package/dist/cjs/src/vcs/objects.cjs +141 -0
- package/dist/cjs/src/vcs/objects.cjs.map +10 -0
- package/dist/cjs/src/vcs/rules.cjs +6 -3
- package/dist/cjs/src/vcs/rules.cjs.map +3 -3
- package/dist/cjs/src/vcs/snapshot.cjs +89 -39
- package/dist/cjs/src/vcs/snapshot.cjs.map +3 -3
- package/dist/cjs/src/vcs/storage.cjs +44 -3
- package/dist/cjs/src/vcs/storage.cjs.map +3 -3
- package/dist/cjs/src/vcs/text-diff.cjs +219 -0
- package/dist/cjs/src/vcs/text-diff.cjs.map +10 -0
- package/dist/cjs/src/vcs/vcs.cjs +108 -61
- package/dist/cjs/src/vcs/vcs.cjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/src/fs/memfs-adapter.mjs +57 -2
- package/dist/mjs/src/fs/memfs-adapter.mjs.map +3 -3
- package/dist/mjs/src/fs/real-fs.mjs +135 -3
- package/dist/mjs/src/fs/real-fs.mjs.map +3 -3
- package/dist/mjs/src/fs/special-files.mjs +3 -2
- package/dist/mjs/src/fs/special-files.mjs.map +3 -3
- package/dist/mjs/src/fs/web-fs.mjs +72 -3
- package/dist/mjs/src/fs/web-fs.mjs.map +3 -3
- package/dist/mjs/src/index.mjs +4 -14
- package/dist/mjs/src/index.mjs.map +3 -3
- package/dist/mjs/src/interpreter/interpreter.mjs +186 -68
- package/dist/mjs/src/interpreter/interpreter.mjs.map +3 -3
- package/dist/mjs/src/parser/ast.mjs +5 -25
- package/dist/mjs/src/parser/ast.mjs.map +3 -3
- package/dist/mjs/src/parser/index.mjs +4 -14
- package/dist/mjs/src/parser/index.mjs.map +3 -3
- package/dist/mjs/src/parser/parser.mjs +132 -82
- package/dist/mjs/src/parser/parser.mjs.map +3 -3
- package/dist/mjs/src/types.mjs.map +2 -2
- package/dist/mjs/src/vcs/content.mjs +66 -0
- package/dist/mjs/src/vcs/content.mjs.map +10 -0
- package/dist/mjs/src/vcs/diff.mjs +72 -28
- package/dist/mjs/src/vcs/diff.mjs.map +3 -3
- package/dist/mjs/src/vcs/index.mjs.map +1 -1
- package/dist/mjs/src/vcs/objects.mjs +106 -0
- package/dist/mjs/src/vcs/objects.mjs.map +10 -0
- package/dist/mjs/src/vcs/rules.mjs +6 -3
- package/dist/mjs/src/vcs/rules.mjs.map +3 -3
- package/dist/mjs/src/vcs/snapshot.mjs +89 -39
- package/dist/mjs/src/vcs/snapshot.mjs.map +3 -3
- package/dist/mjs/src/vcs/storage.mjs +45 -3
- package/dist/mjs/src/vcs/storage.mjs.map +3 -3
- package/dist/mjs/src/vcs/text-diff.mjs +179 -0
- package/dist/mjs/src/vcs/text-diff.mjs.map +10 -0
- package/dist/mjs/src/vcs/vcs.mjs +115 -63
- package/dist/mjs/src/vcs/vcs.mjs.map +3 -3
- package/dist/types/src/fs/real-fs.d.ts +12 -1
- package/dist/types/src/index.d.ts +4 -4
- package/dist/types/src/interpreter/interpreter.d.ts +15 -1
- package/dist/types/src/parser/ast.d.ts +34 -38
- package/dist/types/src/parser/index.d.ts +2 -2
- package/dist/types/src/parser/parser.d.ts +4 -1
- package/dist/types/src/types.d.ts +10 -0
- package/dist/types/src/vcs/content.d.ts +6 -0
- package/dist/types/src/vcs/diff.d.ts +10 -7
- package/dist/types/src/vcs/index.d.ts +1 -1
- package/dist/types/src/vcs/objects.d.ts +22 -0
- package/dist/types/src/vcs/snapshot.d.ts +13 -8
- package/dist/types/src/vcs/storage.d.ts +7 -1
- package/dist/types/src/vcs/text-diff.d.ts +1 -0
- package/dist/types/src/vcs/types.d.ts +20 -5
- package/dist/types/src/vcs/vcs.d.ts +6 -0
- package/package.json +7 -2
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
// src/fs/real-fs.ts
|
|
2
2
|
import * as path from "path";
|
|
3
|
+
import * as nodeStreamFs from "node:fs";
|
|
3
4
|
import * as nodeFs from "node:fs/promises";
|
|
4
5
|
import { globVirtualFS } from "../utils/glob.mjs";
|
|
5
6
|
import {
|
|
6
7
|
discardsSpecialFileWrites,
|
|
7
8
|
existsSpecialFile,
|
|
8
9
|
getSpecialPathError,
|
|
10
|
+
isDevNullPath,
|
|
9
11
|
readSpecialFile,
|
|
10
12
|
statSpecialFile
|
|
11
13
|
} from "./special-files.mjs";
|
|
12
|
-
var defaultFS = {
|
|
14
|
+
var defaultFS = {
|
|
15
|
+
promises: nodeFs,
|
|
16
|
+
streams: {
|
|
17
|
+
createReadStream(realPath) {
|
|
18
|
+
return createNodeReadIterable(realPath);
|
|
19
|
+
},
|
|
20
|
+
createWriteStream(realPath, opts) {
|
|
21
|
+
return createNodeWritable(realPath, opts);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
13
25
|
var nodePathOps = {
|
|
14
26
|
separator: path.sep,
|
|
15
27
|
resolve: (...paths) => path.resolve(...paths),
|
|
@@ -117,6 +129,18 @@ class FileSystem {
|
|
|
117
129
|
const buf = Buffer.from(content);
|
|
118
130
|
return encoding ? buf.toString(encoding) : buf;
|
|
119
131
|
}
|
|
132
|
+
readStream(filePath) {
|
|
133
|
+
if (isDevNullPath(filePath)) {
|
|
134
|
+
return emptyIterable();
|
|
135
|
+
}
|
|
136
|
+
this.checkPermission(filePath, "read");
|
|
137
|
+
const realPath = this.resolveSafePath(filePath);
|
|
138
|
+
const nativeStream = this.underlyingFs.streams?.createReadStream?.(realPath);
|
|
139
|
+
if (nativeStream) {
|
|
140
|
+
return normalizeChunks(nativeStream);
|
|
141
|
+
}
|
|
142
|
+
return bufferToIterable(this.readFile(filePath));
|
|
143
|
+
}
|
|
120
144
|
async readdir(dirPath) {
|
|
121
145
|
const specialError = getSpecialPathError(dirPath, "readdir");
|
|
122
146
|
if (specialError) {
|
|
@@ -139,7 +163,8 @@ class FileSystem {
|
|
|
139
163
|
isFile: () => stats.isFile(),
|
|
140
164
|
isDirectory: () => stats.isDirectory(),
|
|
141
165
|
size: stats.size,
|
|
142
|
-
mtime: stats.mtime
|
|
166
|
+
mtime: stats.mtime,
|
|
167
|
+
mtimeMs: stats.mtimeMs ?? stats.mtime.getTime()
|
|
143
168
|
};
|
|
144
169
|
}
|
|
145
170
|
async exists(filePath) {
|
|
@@ -172,6 +197,24 @@ class FileSystem {
|
|
|
172
197
|
const realPath = this.resolveSafePath(filePath);
|
|
173
198
|
await this.underlyingFs.promises.appendFile(realPath, data);
|
|
174
199
|
}
|
|
200
|
+
async writeStream(filePath, opts) {
|
|
201
|
+
if (discardsSpecialFileWrites(filePath)) {
|
|
202
|
+
return createDiscardingWritable();
|
|
203
|
+
}
|
|
204
|
+
this.checkPermission(filePath, "write");
|
|
205
|
+
const realPath = this.resolveSafePath(filePath);
|
|
206
|
+
const nativeWriter = this.underlyingFs.streams?.createWriteStream?.(realPath, opts);
|
|
207
|
+
if (nativeWriter) {
|
|
208
|
+
return nativeWriter;
|
|
209
|
+
}
|
|
210
|
+
return createBufferedWritable(async (buffer) => {
|
|
211
|
+
if (opts?.append) {
|
|
212
|
+
await this.underlyingFs.promises.appendFile(realPath, buffer);
|
|
213
|
+
} else {
|
|
214
|
+
await this.underlyingFs.promises.writeFile(realPath, buffer);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
175
218
|
async mkdir(dirPath, opts) {
|
|
176
219
|
const specialError = getSpecialPathError(dirPath, "mkdir");
|
|
177
220
|
if (specialError) {
|
|
@@ -205,8 +248,97 @@ class FileSystem {
|
|
|
205
248
|
return globVirtualFS(this, pattern, { cwd });
|
|
206
249
|
}
|
|
207
250
|
}
|
|
251
|
+
function emptyIterable() {
|
|
252
|
+
return {
|
|
253
|
+
async* [Symbol.asyncIterator]() {}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function bufferToIterable(bufferPromise) {
|
|
257
|
+
return {
|
|
258
|
+
async* [Symbol.asyncIterator]() {
|
|
259
|
+
const buffer = await bufferPromise;
|
|
260
|
+
yield buffer;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
async function* normalizeChunks(source) {
|
|
265
|
+
for await (const chunk of source) {
|
|
266
|
+
yield chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function createDiscardingWritable() {
|
|
270
|
+
return {
|
|
271
|
+
async write(_chunk) {},
|
|
272
|
+
async close() {},
|
|
273
|
+
async abort(_reason) {}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function createBufferedWritable(onClose) {
|
|
277
|
+
const chunks = [];
|
|
278
|
+
let closed = false;
|
|
279
|
+
return {
|
|
280
|
+
async write(chunk) {
|
|
281
|
+
if (closed) {
|
|
282
|
+
throw new Error("stream is closed");
|
|
283
|
+
}
|
|
284
|
+
chunks.push(Buffer.from(chunk));
|
|
285
|
+
},
|
|
286
|
+
async close() {
|
|
287
|
+
if (closed)
|
|
288
|
+
return;
|
|
289
|
+
closed = true;
|
|
290
|
+
await onClose(Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))));
|
|
291
|
+
},
|
|
292
|
+
async abort(_reason) {
|
|
293
|
+
closed = true;
|
|
294
|
+
chunks.length = 0;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
async function* createNodeReadIterable(realPath) {
|
|
299
|
+
const stream = nodeStreamFs.createReadStream(realPath);
|
|
300
|
+
for await (const chunk of stream) {
|
|
301
|
+
yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function createNodeWritable(realPath, opts) {
|
|
305
|
+
const stream = nodeStreamFs.createWriteStream(realPath, {
|
|
306
|
+
flags: opts?.append ? "a" : "w"
|
|
307
|
+
});
|
|
308
|
+
const ready = new Promise((resolve2, reject) => {
|
|
309
|
+
stream.once("open", () => resolve2());
|
|
310
|
+
stream.once("error", reject);
|
|
311
|
+
});
|
|
312
|
+
return {
|
|
313
|
+
async write(chunk) {
|
|
314
|
+
await ready;
|
|
315
|
+
await new Promise((resolve2, reject) => {
|
|
316
|
+
stream.write(chunk, (error) => {
|
|
317
|
+
if (error)
|
|
318
|
+
reject(error);
|
|
319
|
+
else
|
|
320
|
+
resolve2();
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
},
|
|
324
|
+
async close() {
|
|
325
|
+
await ready;
|
|
326
|
+
await new Promise((resolve2, reject) => {
|
|
327
|
+
stream.end((error) => {
|
|
328
|
+
if (error)
|
|
329
|
+
reject(error);
|
|
330
|
+
else
|
|
331
|
+
resolve2();
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
},
|
|
335
|
+
async abort(reason) {
|
|
336
|
+
stream.destroy(reason instanceof Error ? reason : undefined);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
208
340
|
export {
|
|
209
341
|
FileSystem
|
|
210
342
|
};
|
|
211
343
|
|
|
212
|
-
//# debugId=
|
|
344
|
+
//# debugId=4D82D1C6073DF96D64756E2164756E21
|
|
@@ -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\";\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"
|
|
5
|
+
"import * as path from \"path\";\nimport * as nodeStreamFs from \"node:fs\";\nimport * as nodeFs from \"node:fs/promises\";\nimport type { VirtualFS, VirtualFSWritable, FileStat } from \"../types.mjs\";\nimport { globVirtualFS } from \"../utils/glob.mjs\";\nimport {\n discardsSpecialFileWrites,\n existsSpecialFile,\n getSpecialPathError,\n isDevNullPath,\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 mtimeMs?: number;\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 streams?: {\n createReadStream?(path: string): AsyncIterable<Uint8Array>;\n createWriteStream?(\n path: string,\n opts?: { append?: boolean },\n ): Promise<VirtualFSWritable> | VirtualFSWritable;\n };\n}\n\n// Default: use real node:fs\nconst defaultFS: UnderlyingFS = {\n promises: nodeFs,\n streams: {\n createReadStream(realPath: string): AsyncIterable<Uint8Array> {\n return createNodeReadIterable(realPath);\n },\n createWriteStream(realPath: string, opts?: { append?: boolean }): VirtualFSWritable {\n return createNodeWritable(realPath, opts);\n },\n },\n};\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 readStream(filePath: string): AsyncIterable<Uint8Array> {\n if (isDevNullPath(filePath)) {\n return emptyIterable();\n }\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n const nativeStream = this.underlyingFs.streams?.createReadStream?.(realPath);\n if (nativeStream) {\n return normalizeChunks(nativeStream);\n }\n return bufferToIterable(this.readFile(filePath) as Promise<Buffer>);\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 mtimeMs: stats.mtimeMs ?? stats.mtime.getTime(),\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 writeStream(filePath: string, opts?: { append?: boolean }): Promise<VirtualFSWritable> {\n if (discardsSpecialFileWrites(filePath)) {\n return createDiscardingWritable();\n }\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n const nativeWriter = this.underlyingFs.streams?.createWriteStream?.(realPath, opts);\n if (nativeWriter) {\n return nativeWriter;\n }\n return createBufferedWritable(async (buffer) => {\n if (opts?.append) {\n await this.underlyingFs.promises.appendFile(realPath, buffer);\n } else {\n await this.underlyingFs.promises.writeFile(realPath, buffer);\n }\n });\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\nfunction emptyIterable(): AsyncIterable<Uint8Array> {\n return {\n async *[Symbol.asyncIterator]() {},\n };\n}\n\nfunction bufferToIterable(bufferPromise: Promise<Buffer>): AsyncIterable<Uint8Array> {\n return {\n async *[Symbol.asyncIterator]() {\n const buffer = await bufferPromise;\n yield buffer;\n },\n };\n}\n\nasync function* normalizeChunks(source: AsyncIterable<Uint8Array>): AsyncIterable<Uint8Array> {\n for await (const chunk of source) {\n yield chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n }\n}\n\nfunction createDiscardingWritable(): VirtualFSWritable {\n return {\n async write(_chunk: Uint8Array): Promise<void> {},\n async close(): Promise<void> {},\n async abort(_reason?: unknown): Promise<void> {},\n };\n}\n\nfunction createBufferedWritable(\n onClose: (buffer: Buffer) => Promise<void>,\n): VirtualFSWritable {\n const chunks: Uint8Array[] = [];\n let closed = false;\n\n return {\n async write(chunk: Uint8Array): Promise<void> {\n if (closed) {\n throw new Error(\"stream is closed\");\n }\n chunks.push(Buffer.from(chunk));\n },\n async close(): Promise<void> {\n if (closed) return;\n closed = true;\n await onClose(Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))));\n },\n async abort(_reason?: unknown): Promise<void> {\n closed = true;\n chunks.length = 0;\n },\n };\n}\n\nasync function* createNodeReadIterable(realPath: string): AsyncIterable<Uint8Array> {\n const stream = nodeStreamFs.createReadStream(realPath);\n for await (const chunk of stream) {\n yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n }\n}\n\nfunction createNodeWritable(\n realPath: string,\n opts?: { append?: boolean },\n): VirtualFSWritable {\n const stream = nodeStreamFs.createWriteStream(realPath, {\n flags: opts?.append ? \"a\" : \"w\",\n });\n const ready = new Promise<void>((resolve, reject) => {\n stream.once(\"open\", () => resolve());\n stream.once(\"error\", reject);\n });\n\n return {\n async write(chunk: Uint8Array): Promise<void> {\n await ready;\n await new Promise<void>((resolve, reject) => {\n stream.write(chunk, (error) => {\n if (error) reject(error);\n else resolve();\n });\n });\n },\n async close(): Promise<void> {\n await ready;\n await new Promise<void>((resolve, reject) => {\n stream.end((error?: Error | null) => {\n if (error) reject(error);\n else resolve();\n });\n });\n },\n async abort(reason?: unknown): Promise<void> {\n stream.destroy(reason instanceof Error ? reason : undefined);\n },\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "4D82D1C6073DF96D64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -20,7 +20,8 @@ function statSpecialFile(path) {
|
|
|
20
20
|
isFile: () => true,
|
|
21
21
|
isDirectory: () => false,
|
|
22
22
|
size: 0,
|
|
23
|
-
mtime: new Date(0)
|
|
23
|
+
mtime: new Date(0),
|
|
24
|
+
mtimeMs: 0
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
27
|
function existsSpecialFile(path) {
|
|
@@ -55,4 +56,4 @@ export {
|
|
|
55
56
|
DEV_NULL_PATH
|
|
56
57
|
};
|
|
57
58
|
|
|
58
|
-
//# debugId=
|
|
59
|
+
//# debugId=B26D9A26BDC0990864756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/fs/special-files.ts"],
|
|
4
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"
|
|
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 mtimeMs: 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
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,
|
|
8
|
-
"debugId": "
|
|
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,IACjB,SAAS;AAAA,EACX;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": "B26D9A26BDC0990864756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -81,7 +81,8 @@ function createWebUnderlyingFS(root) {
|
|
|
81
81
|
isFile: () => true,
|
|
82
82
|
isDirectory: () => false,
|
|
83
83
|
size: file.size,
|
|
84
|
-
mtime: new Date(file.lastModified ?? 0)
|
|
84
|
+
mtime: new Date(file.lastModified ?? 0),
|
|
85
|
+
mtimeMs: file.lastModified ?? 0
|
|
85
86
|
};
|
|
86
87
|
} catch (error) {
|
|
87
88
|
if (!isNotFoundOrTypeMismatch(error))
|
|
@@ -150,6 +151,57 @@ function createWebUnderlyingFS(root) {
|
|
|
150
151
|
throw error;
|
|
151
152
|
}
|
|
152
153
|
}
|
|
154
|
+
},
|
|
155
|
+
streams: {
|
|
156
|
+
createReadStream(path) {
|
|
157
|
+
return {
|
|
158
|
+
async* [Symbol.asyncIterator]() {
|
|
159
|
+
const { parentSegments, name } = splitParent(path);
|
|
160
|
+
const parent = await walkDirectory(root, parentSegments, false);
|
|
161
|
+
const fileHandle = await parent.getFileHandle(name, { create: false });
|
|
162
|
+
const file = await fileHandle.getFile();
|
|
163
|
+
for await (const chunk of streamToAsyncIterable(file.stream())) {
|
|
164
|
+
yield chunk;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
async createWriteStream(path, opts) {
|
|
170
|
+
const { parentSegments, name } = splitParent(path);
|
|
171
|
+
const parent = await walkDirectory(root, parentSegments, false);
|
|
172
|
+
const fileHandle = await parent.getFileHandle(name, { create: true });
|
|
173
|
+
const existingFile = opts?.append ? await fileHandle.getFile() : null;
|
|
174
|
+
const writable = await fileHandle.createWritable({ keepExistingData: !!opts?.append });
|
|
175
|
+
let position = opts?.append ? existingFile?.size ?? 0 : 0;
|
|
176
|
+
let closed = false;
|
|
177
|
+
return {
|
|
178
|
+
async write(chunk) {
|
|
179
|
+
if (closed) {
|
|
180
|
+
throw new Error("stream is closed");
|
|
181
|
+
}
|
|
182
|
+
const bytes = new Uint8Array(chunk.byteLength);
|
|
183
|
+
bytes.set(chunk, 0);
|
|
184
|
+
await writable.write({
|
|
185
|
+
type: "write",
|
|
186
|
+
position,
|
|
187
|
+
data: bytes
|
|
188
|
+
});
|
|
189
|
+
position += bytes.byteLength;
|
|
190
|
+
},
|
|
191
|
+
async close() {
|
|
192
|
+
if (closed)
|
|
193
|
+
return;
|
|
194
|
+
closed = true;
|
|
195
|
+
await writable.close();
|
|
196
|
+
},
|
|
197
|
+
async abort(_reason) {
|
|
198
|
+
if (closed)
|
|
199
|
+
return;
|
|
200
|
+
closed = true;
|
|
201
|
+
await writable.abort?.();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
153
205
|
}
|
|
154
206
|
};
|
|
155
207
|
}
|
|
@@ -164,7 +216,8 @@ function createDirectoryStat() {
|
|
|
164
216
|
isFile: () => false,
|
|
165
217
|
isDirectory: () => true,
|
|
166
218
|
size: 0,
|
|
167
|
-
mtime: DIRECTORY_MTIME
|
|
219
|
+
mtime: DIRECTORY_MTIME,
|
|
220
|
+
mtimeMs: DIRECTORY_MTIME.getTime()
|
|
168
221
|
};
|
|
169
222
|
}
|
|
170
223
|
function normalizeWebPath(path) {
|
|
@@ -224,6 +277,22 @@ async function entryExists(dir, name) {
|
|
|
224
277
|
return false;
|
|
225
278
|
}
|
|
226
279
|
}
|
|
280
|
+
async function* streamToAsyncIterable(stream) {
|
|
281
|
+
const reader = stream.getReader();
|
|
282
|
+
try {
|
|
283
|
+
while (true) {
|
|
284
|
+
const { done, value } = await reader.read();
|
|
285
|
+
if (done) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (value) {
|
|
289
|
+
yield value;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} finally {
|
|
293
|
+
reader.releaseLock();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
227
296
|
function isNotFoundOrTypeMismatch(error) {
|
|
228
297
|
return isNotFoundError(error) || isTypeMismatchError(error);
|
|
229
298
|
}
|
|
@@ -252,4 +321,4 @@ export {
|
|
|
252
321
|
WebFileSystem
|
|
253
322
|
};
|
|
254
323
|
|
|
255
|
-
//# debugId=
|
|
324
|
+
//# debugId=D6530F8F37F8735C64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/fs/web-fs.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { FileSystem, type PathOps, type PermissionRules, type UnderlyingFS } from \"./real-fs.mjs\";\n\nconst DIRECTORY_MTIME = new Date(0);\nconst WEB_PATH_OPS: PathOps = {\n separator: \"/\",\n resolve(...paths: string[]): string {\n return normalizeWebPath(paths.join(\"/\"));\n },\n normalize: normalizeWebPath,\n join(...paths: string[]): string {\n const nonEmpty = paths.filter((segment) => segment.length > 0);\n if (nonEmpty.length === 0) {\n return \".\";\n }\n return normalizeWebPath(nonEmpty.join(\"/\"));\n },\n relative(from: string, to: string): string {\n const fromSegments = getPathSegments(from);\n const toSegments = getPathSegments(to);\n let shared = 0;\n\n while (\n shared < fromSegments.length &&\n shared < toSegments.length &&\n fromSegments[shared] === toSegments[shared]\n ) {\n shared++;\n }\n\n const up = new Array(fromSegments.length - shared).fill(\"..\");\n const down = toSegments.slice(shared);\n return [...up, ...down].join(\"/\");\n },\n isAbsolute(path: string): boolean {\n return path.startsWith(\"/\");\n },\n dirname(path: string): string {\n const normalized = normalizeWebPath(path);\n if (normalized === \"/\") {\n return \"/\";\n }\n\n const segments = getPathSegments(normalized);\n if (segments.length <= 1) {\n return \"/\";\n }\n\n return `/${segments.slice(0, -1).join(\"/\")}`;\n },\n basename(path: string): string {\n const normalized = normalizeWebPath(path);\n if (normalized === \"/\") {\n return \"\";\n }\n\n const segments = getPathSegments(normalized);\n return segments[segments.length - 1] ?? \"\";\n },\n};\n\nexport function createWebUnderlyingFS(root: FileSystemDirectoryHandle): UnderlyingFS {\n return {\n pathOps: WEB_PATH_OPS,\n promises: {\n async readFile(path: string): Promise<Buffer> {\n const { parentSegments, name } = splitParent(path);\n const parent = await walkDirectory(root, parentSegments, false);\n const fileHandle = await parent.getFileHandle(name, { create: false });\n const file = await fileHandle.getFile();\n return Buffer.from(await file.arrayBuffer());\n },\n\n async readdir(path: string): Promise<string[]> {\n const dir = await walkDirectory(root, getPathSegments(path), false);\n const entries: string[] = [];\n for await (const [name] of dir.entries()) {\n entries.push(name);\n }\n return entries;\n },\n\n async stat(path: string): Promise<{\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n }> {\n const segments = getPathSegments(path);\n\n if (segments.length === 0) {\n return createDirectoryStat();\n }\n\n const { parentSegments, name } = splitParent(path);\n const parent = await walkDirectory(root, parentSegments, false);\n\n try {\n const fileHandle = await parent.getFileHandle(name, { create: false });\n const file = await fileHandle.getFile();\n return {\n isFile: () => true,\n isDirectory: () => false,\n size: file.size,\n mtime: new Date(file.lastModified ?? 0),\n };\n } catch (error) {\n if (!isNotFoundOrTypeMismatch(error)) throw error;\n }\n\n try {\n await parent.getDirectoryHandle(name, { create: false });\n return createDirectoryStat();\n } catch (error) {\n if (!isNotFoundOrTypeMismatch(error)) throw error;\n throw error;\n }\n },\n\n async writeFile(path: string, data: Buffer | string): Promise<void> {\n const { parentSegments, name } = splitParent(path);\n const parent = await walkDirectory(root, parentSegments, false);\n const fileHandle = await parent.getFileHandle(name, { create: true });\n const writable = await fileHandle.createWritable();\n await writable.write(toWritableData(data));\n await writable.close();\n },\n\n async appendFile(path: string, data: Buffer | string): Promise<void> {\n const { parentSegments, name } = splitParent(path);\n const parent = await walkDirectory(root, parentSegments, false);\n const fileHandle = await parent.getFileHandle(name, { create: true });\n const file = await fileHandle.getFile();\n const writable = await fileHandle.createWritable({ keepExistingData: true });\n await writable.write({\n type: \"write\",\n position: file.size,\n data: toWritableData(data),\n });\n await writable.close();\n },\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const segments = getPathSegments(path);\n if (segments.length === 0) {\n return;\n }\n\n if (opts?.recursive) {\n await walkDirectory(root, segments, true);\n return;\n }\n\n const parent = await walkDirectory(root, segments.slice(0, -1), false);\n const name = segments[segments.length - 1]!;\n const exists = await entryExists(parent, name);\n if (exists) {\n throw new Error(`EEXIST: file already exists, mkdir '${normalizeWebPath(path)}'`);\n }\n await parent.getDirectoryHandle(name, { create: true });\n },\n\n async rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n const segments = getPathSegments(path);\n if (segments.length === 0) {\n throw new Error(\"EPERM: operation not permitted, rm '/'\");\n }\n\n const parent = await walkDirectory(root, segments.slice(0, -1), false);\n const name = segments[segments.length - 1]!;\n try {\n await parent.removeEntry(name, { recursive: opts?.recursive });\n } catch (error) {\n if (opts?.force && isNotFoundError(error)) {\n return;\n }\n throw error;\n }\n },\n },\n };\n}\n\nexport class WebFileSystem extends FileSystem {\n constructor(root: FileSystemDirectoryHandle, permissions?: PermissionRules) {\n super(\"/\", permissions, createWebUnderlyingFS(root));\n }\n}\n\nfunction createDirectoryStat() {\n return {\n isFile: () => false,\n isDirectory: () => true,\n size: 0,\n mtime: DIRECTORY_MTIME,\n };\n}\n\nfunction normalizeWebPath(path: string): string {\n const normalized = path.replace(/\\\\/g, \"/\");\n const rawSegments = (normalized.startsWith(\"/\") ? normalized : `/${normalized}`)\n .split(\"/\")\n .filter(Boolean);\n\n const segments: string[] = [];\n for (const segment of rawSegments) {\n if (segment === \".\") continue;\n if (segment === \"..\") {\n segments.pop();\n continue;\n }\n segments.push(segment);\n }\n\n return `/${segments.join(\"/\")}`;\n}\n\nfunction getPathSegments(path: string): string[] {\n const normalized = normalizeWebPath(path);\n return normalized.split(\"/\").filter(Boolean);\n}\n\nfunction splitParent(path: string): { parentSegments: string[]; name: string } {\n const segments = getPathSegments(path);\n if (segments.length === 0) {\n throw new Error(`Invalid file path: \"${path}\"`);\n }\n return {\n parentSegments: segments.slice(0, -1),\n name: segments[segments.length - 1]!,\n };\n}\n\nasync function walkDirectory(\n root: FileSystemDirectoryHandle,\n segments: string[],\n create: boolean\n): Promise<FileSystemDirectoryHandle> {\n let current = root;\n for (const segment of segments) {\n current = await current.getDirectoryHandle(segment, { create });\n }\n return current;\n}\n\nasync function entryExists(dir: FileSystemDirectoryHandle, name: string): Promise<boolean> {\n try {\n await dir.getFileHandle(name, { create: false });\n return true;\n } catch (error) {\n if (isTypeMismatchError(error)) return true;\n if (!isNotFoundError(error)) throw error;\n }\n\n try {\n await dir.getDirectoryHandle(name, { create: false });\n return true;\n } catch (error) {\n if (isTypeMismatchError(error)) return true;\n if (!isNotFoundError(error)) throw error;\n return false;\n }\n}\n\nfunction isNotFoundOrTypeMismatch(error: unknown): boolean {\n return isNotFoundError(error) || isTypeMismatchError(error);\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n return getErrorName(error) === \"NotFoundError\";\n}\n\nfunction isTypeMismatchError(error: unknown): boolean {\n return getErrorName(error) === \"TypeMismatchError\";\n}\n\nfunction getErrorName(error: unknown): string | undefined {\n if (!error || typeof error !== \"object\") return undefined;\n const named = error as { name?: unknown };\n return typeof named.name === \"string\" ? named.name : undefined;\n}\n\nfunction toWritableData(data: Buffer | string): string | ArrayBuffer {\n if (typeof data === \"string\") {\n return data;\n }\n const out = new Uint8Array(data.length);\n out.set(data);\n return out.buffer;\n}\n"
|
|
5
|
+
"import type { VirtualFSWritable } from \"../types.mjs\";\nimport { FileSystem, type PathOps, type PermissionRules, type UnderlyingFS } from \"./real-fs.mjs\";\n\nconst DIRECTORY_MTIME = new Date(0);\nconst WEB_PATH_OPS: PathOps = {\n separator: \"/\",\n resolve(...paths: string[]): string {\n return normalizeWebPath(paths.join(\"/\"));\n },\n normalize: normalizeWebPath,\n join(...paths: string[]): string {\n const nonEmpty = paths.filter((segment) => segment.length > 0);\n if (nonEmpty.length === 0) {\n return \".\";\n }\n return normalizeWebPath(nonEmpty.join(\"/\"));\n },\n relative(from: string, to: string): string {\n const fromSegments = getPathSegments(from);\n const toSegments = getPathSegments(to);\n let shared = 0;\n\n while (\n shared < fromSegments.length &&\n shared < toSegments.length &&\n fromSegments[shared] === toSegments[shared]\n ) {\n shared++;\n }\n\n const up = new Array(fromSegments.length - shared).fill(\"..\");\n const down = toSegments.slice(shared);\n return [...up, ...down].join(\"/\");\n },\n isAbsolute(path: string): boolean {\n return path.startsWith(\"/\");\n },\n dirname(path: string): string {\n const normalized = normalizeWebPath(path);\n if (normalized === \"/\") {\n return \"/\";\n }\n\n const segments = getPathSegments(normalized);\n if (segments.length <= 1) {\n return \"/\";\n }\n\n return `/${segments.slice(0, -1).join(\"/\")}`;\n },\n basename(path: string): string {\n const normalized = normalizeWebPath(path);\n if (normalized === \"/\") {\n return \"\";\n }\n\n const segments = getPathSegments(normalized);\n return segments[segments.length - 1] ?? \"\";\n },\n};\n\nexport function createWebUnderlyingFS(root: FileSystemDirectoryHandle): UnderlyingFS {\n return {\n pathOps: WEB_PATH_OPS,\n promises: {\n async readFile(path: string): Promise<Buffer> {\n const { parentSegments, name } = splitParent(path);\n const parent = await walkDirectory(root, parentSegments, false);\n const fileHandle = await parent.getFileHandle(name, { create: false });\n const file = await fileHandle.getFile();\n return Buffer.from(await file.arrayBuffer());\n },\n\n async readdir(path: string): Promise<string[]> {\n const dir = await walkDirectory(root, getPathSegments(path), false);\n const entries: string[] = [];\n for await (const [name] of dir.entries()) {\n entries.push(name);\n }\n return entries;\n },\n\n async stat(path: string): Promise<{\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n mtimeMs: number;\n }> {\n const segments = getPathSegments(path);\n\n if (segments.length === 0) {\n return createDirectoryStat();\n }\n\n const { parentSegments, name } = splitParent(path);\n const parent = await walkDirectory(root, parentSegments, false);\n\n try {\n const fileHandle = await parent.getFileHandle(name, { create: false });\n const file = await fileHandle.getFile();\n return {\n isFile: () => true,\n isDirectory: () => false,\n size: file.size,\n mtime: new Date(file.lastModified ?? 0),\n mtimeMs: file.lastModified ?? 0,\n };\n } catch (error) {\n if (!isNotFoundOrTypeMismatch(error)) throw error;\n }\n\n try {\n await parent.getDirectoryHandle(name, { create: false });\n return createDirectoryStat();\n } catch (error) {\n if (!isNotFoundOrTypeMismatch(error)) throw error;\n throw error;\n }\n },\n\n async writeFile(path: string, data: Buffer | string): Promise<void> {\n const { parentSegments, name } = splitParent(path);\n const parent = await walkDirectory(root, parentSegments, false);\n const fileHandle = await parent.getFileHandle(name, { create: true });\n const writable = await fileHandle.createWritable();\n await writable.write(toWritableData(data));\n await writable.close();\n },\n\n async appendFile(path: string, data: Buffer | string): Promise<void> {\n const { parentSegments, name } = splitParent(path);\n const parent = await walkDirectory(root, parentSegments, false);\n const fileHandle = await parent.getFileHandle(name, { create: true });\n const file = await fileHandle.getFile();\n const writable = await fileHandle.createWritable({ keepExistingData: true });\n await writable.write({\n type: \"write\",\n position: file.size,\n data: toWritableData(data),\n });\n await writable.close();\n },\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const segments = getPathSegments(path);\n if (segments.length === 0) {\n return;\n }\n\n if (opts?.recursive) {\n await walkDirectory(root, segments, true);\n return;\n }\n\n const parent = await walkDirectory(root, segments.slice(0, -1), false);\n const name = segments[segments.length - 1]!;\n const exists = await entryExists(parent, name);\n if (exists) {\n throw new Error(`EEXIST: file already exists, mkdir '${normalizeWebPath(path)}'`);\n }\n await parent.getDirectoryHandle(name, { create: true });\n },\n\n async rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n const segments = getPathSegments(path);\n if (segments.length === 0) {\n throw new Error(\"EPERM: operation not permitted, rm '/'\");\n }\n\n const parent = await walkDirectory(root, segments.slice(0, -1), false);\n const name = segments[segments.length - 1]!;\n try {\n await parent.removeEntry(name, { recursive: opts?.recursive });\n } catch (error) {\n if (opts?.force && isNotFoundError(error)) {\n return;\n }\n throw error;\n }\n },\n },\n streams: {\n createReadStream(path: string): AsyncIterable<Uint8Array> {\n return {\n async *[Symbol.asyncIterator]() {\n const { parentSegments, name } = splitParent(path);\n const parent = await walkDirectory(root, parentSegments, false);\n const fileHandle = await parent.getFileHandle(name, { create: false });\n const file = await fileHandle.getFile();\n for await (const chunk of streamToAsyncIterable(file.stream())) {\n yield chunk;\n }\n },\n };\n },\n async createWriteStream(path: string, opts?: { append?: boolean }): Promise<VirtualFSWritable> {\n const { parentSegments, name } = splitParent(path);\n const parent = await walkDirectory(root, parentSegments, false);\n const fileHandle = await parent.getFileHandle(name, { create: true });\n const existingFile = opts?.append ? await fileHandle.getFile() : null;\n const writable = await fileHandle.createWritable({ keepExistingData: !!opts?.append });\n let position = opts?.append ? existingFile?.size ?? 0 : 0;\n let closed = false;\n\n return {\n async write(chunk: Uint8Array): Promise<void> {\n if (closed) {\n throw new Error(\"stream is closed\");\n }\n const bytes = new Uint8Array(chunk.byteLength);\n bytes.set(chunk, 0);\n await writable.write({\n type: \"write\",\n position,\n data: bytes,\n });\n position += bytes.byteLength;\n },\n async close(): Promise<void> {\n if (closed) return;\n closed = true;\n await writable.close();\n },\n async abort(_reason?: unknown): Promise<void> {\n if (closed) return;\n closed = true;\n await writable.abort?.();\n },\n };\n },\n },\n };\n}\n\nexport class WebFileSystem extends FileSystem {\n constructor(root: FileSystemDirectoryHandle, permissions?: PermissionRules) {\n super(\"/\", permissions, createWebUnderlyingFS(root));\n }\n}\n\nfunction createDirectoryStat() {\n return {\n isFile: () => false,\n isDirectory: () => true,\n size: 0,\n mtime: DIRECTORY_MTIME,\n mtimeMs: DIRECTORY_MTIME.getTime(),\n };\n}\n\nfunction normalizeWebPath(path: string): string {\n const normalized = path.replace(/\\\\/g, \"/\");\n const rawSegments = (normalized.startsWith(\"/\") ? normalized : `/${normalized}`)\n .split(\"/\")\n .filter(Boolean);\n\n const segments: string[] = [];\n for (const segment of rawSegments) {\n if (segment === \".\") continue;\n if (segment === \"..\") {\n segments.pop();\n continue;\n }\n segments.push(segment);\n }\n\n return `/${segments.join(\"/\")}`;\n}\n\nfunction getPathSegments(path: string): string[] {\n const normalized = normalizeWebPath(path);\n return normalized.split(\"/\").filter(Boolean);\n}\n\nfunction splitParent(path: string): { parentSegments: string[]; name: string } {\n const segments = getPathSegments(path);\n if (segments.length === 0) {\n throw new Error(`Invalid file path: \"${path}\"`);\n }\n return {\n parentSegments: segments.slice(0, -1),\n name: segments[segments.length - 1]!,\n };\n}\n\nasync function walkDirectory(\n root: FileSystemDirectoryHandle,\n segments: string[],\n create: boolean\n): Promise<FileSystemDirectoryHandle> {\n let current = root;\n for (const segment of segments) {\n current = await current.getDirectoryHandle(segment, { create });\n }\n return current;\n}\n\nasync function entryExists(dir: FileSystemDirectoryHandle, name: string): Promise<boolean> {\n try {\n await dir.getFileHandle(name, { create: false });\n return true;\n } catch (error) {\n if (isTypeMismatchError(error)) return true;\n if (!isNotFoundError(error)) throw error;\n }\n\n try {\n await dir.getDirectoryHandle(name, { create: false });\n return true;\n } catch (error) {\n if (isTypeMismatchError(error)) return true;\n if (!isNotFoundError(error)) throw error;\n return false;\n }\n}\n\nasync function* streamToAsyncIterable(\n stream: ReadableStream<Uint8Array>,\n): AsyncIterable<Uint8Array> {\n const reader = stream.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n return;\n }\n if (value) {\n yield value;\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction isNotFoundOrTypeMismatch(error: unknown): boolean {\n return isNotFoundError(error) || isTypeMismatchError(error);\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n return getErrorName(error) === \"NotFoundError\";\n}\n\nfunction isTypeMismatchError(error: unknown): boolean {\n return getErrorName(error) === \"TypeMismatchError\";\n}\n\nfunction getErrorName(error: unknown): string | undefined {\n if (!error || typeof error !== \"object\") return undefined;\n const named = error as { name?: unknown };\n return typeof named.name === \"string\" ? named.name : undefined;\n}\n\nfunction toWritableData(data: Buffer | string): string | ArrayBuffer {\n if (typeof data === \"string\") {\n return data;\n }\n const out = new Uint8Array(data.length);\n out.set(data);\n return out.buffer;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "D6530F8F37F8735C64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/src/index.mjs
CHANGED
|
@@ -6,22 +6,17 @@ import { ShellError, LexError, ParseError } from "./errors.mjs";
|
|
|
6
6
|
import { Lexer, lex, tokenToString } from "./lexer/index.mjs";
|
|
7
7
|
import { Parser, parse } from "./parser/index.mjs";
|
|
8
8
|
import {
|
|
9
|
+
isWordNode,
|
|
9
10
|
isCommandNode,
|
|
10
11
|
isPipelineNode,
|
|
11
12
|
isAndNode,
|
|
12
13
|
isOrNode,
|
|
13
14
|
isSequenceNode,
|
|
14
|
-
isLiteralNode,
|
|
15
|
-
isVariableNode,
|
|
16
|
-
isSubstitutionNode,
|
|
17
|
-
isGlobNode,
|
|
18
|
-
isConcatNode,
|
|
19
15
|
isIfNode,
|
|
20
16
|
isForNode,
|
|
21
17
|
isWhileNode,
|
|
22
18
|
isUntilNode,
|
|
23
|
-
isCaseNode
|
|
24
|
-
isArithmeticNode
|
|
19
|
+
isCaseNode
|
|
25
20
|
} from "./parser/index.mjs";
|
|
26
21
|
import { Interpreter, BreakException, ContinueException } from "./interpreter/index.mjs";
|
|
27
22
|
import { createVirtualFS } from "./fs/index.mjs";
|
|
@@ -39,22 +34,17 @@ export {
|
|
|
39
34
|
tokenToString,
|
|
40
35
|
parse,
|
|
41
36
|
lex,
|
|
37
|
+
isWordNode,
|
|
42
38
|
isWhileNode,
|
|
43
|
-
isVariableNode,
|
|
44
39
|
isUntilNode,
|
|
45
|
-
isSubstitutionNode,
|
|
46
40
|
isSequenceNode,
|
|
47
41
|
isRawValue,
|
|
48
42
|
isPipelineNode,
|
|
49
43
|
isOrNode,
|
|
50
|
-
isLiteralNode,
|
|
51
44
|
isIfNode,
|
|
52
|
-
isGlobNode,
|
|
53
45
|
isForNode,
|
|
54
|
-
isConcatNode,
|
|
55
46
|
isCommandNode,
|
|
56
47
|
isCaseNode,
|
|
57
|
-
isArithmeticNode,
|
|
58
48
|
isAndNode,
|
|
59
49
|
globVirtualFS,
|
|
60
50
|
escapeForInterpolation,
|
|
@@ -85,4 +75,4 @@ export {
|
|
|
85
75
|
BreakException
|
|
86
76
|
};
|
|
87
77
|
|
|
88
|
-
//# debugId=
|
|
78
|
+
//# debugId=C4DB171A1954453764756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"// Main class exports\nexport { ShellDSL, createShellDSL, type Program } from \"./shell-dsl.mjs\";\nexport { ShellPromise, type ShellPromiseOptions } from \"./shell-promise.mjs\";\n\n// Types\nexport type {\n VirtualFS,\n FileStat,\n Command,\n CommandContext,\n Stdin,\n Stdout,\n Stderr,\n OutputCollector,\n ExecResult,\n ShellConfig,\n RawValue,\n} from \"./types.mjs\";\nexport { isRawValue } from \"./types.mjs\";\n\n// Errors\nexport { ShellError, LexError, ParseError } from \"./errors.mjs\";\n\n// Lexer\nexport { Lexer, lex, tokenToString } from \"./lexer/index.mjs\";\nexport type { Token, RedirectMode } from \"./lexer/index.mjs\";\n\n// Parser\nexport { Parser, parse } from \"./parser/index.mjs\";\nexport type {\n ASTNode,\n Redirect,\n CommandNode,\n PipelineNode,\n AndNode,\n OrNode,\n SequenceNode,\n
|
|
5
|
+
"// Main class exports\nexport { ShellDSL, createShellDSL, type Program } from \"./shell-dsl.mjs\";\nexport { ShellPromise, type ShellPromiseOptions } from \"./shell-promise.mjs\";\n\n// Types\nexport type {\n VirtualFS,\n VirtualFSWritable,\n FileStat,\n Command,\n CommandContext,\n Stdin,\n Stdout,\n Stderr,\n OutputCollector,\n ExecResult,\n ShellConfig,\n RawValue,\n} from \"./types.mjs\";\nexport { isRawValue } from \"./types.mjs\";\n\n// Errors\nexport { ShellError, LexError, ParseError } from \"./errors.mjs\";\n\n// Lexer\nexport { Lexer, lex, tokenToString } from \"./lexer/index.mjs\";\nexport type { Token, RedirectMode } from \"./lexer/index.mjs\";\n\n// Parser\nexport { Parser, parse } from \"./parser/index.mjs\";\nexport type {\n ASTNode,\n Redirect,\n CommandNode,\n PipelineNode,\n AndNode,\n OrNode,\n SequenceNode,\n WordNode,\n WordPart,\n TextPart,\n VariablePart,\n SubstitutionPart,\n ArithmeticPart,\n IfNode,\n ForNode,\n WhileNode,\n UntilNode,\n CaseNode,\n CaseClause,\n} from \"./parser/index.mjs\";\nexport {\n isWordNode,\n isCommandNode,\n isPipelineNode,\n isAndNode,\n isOrNode,\n isSequenceNode,\n isIfNode,\n isForNode,\n isWhileNode,\n isUntilNode,\n isCaseNode,\n} from \"./parser/index.mjs\";\n\n// Interpreter\nexport { Interpreter, type InterpreterOptions, BreakException, ContinueException } from \"./interpreter/index.mjs\";\n\n// Filesystem\nexport { createVirtualFS } from \"./fs/index.mjs\";\nexport {\n FileSystem,\n ReadOnlyFileSystem,\n WebFileSystem,\n createWebUnderlyingFS,\n type PathOps,\n type Permission,\n type PermissionRules,\n type UnderlyingFS,\n} from \"./fs/index.mjs\";\n\n// I/O\nexport { createStdin, StdinImpl } from \"./io/index.mjs\";\nexport { createStdout, createStderr, createPipe, OutputCollectorImpl, PipeBuffer } from \"./io/index.mjs\";\n\n// Utilities\nexport { escape, escapeForInterpolation, globVirtualFS } from \"./utils/index.mjs\";\nexport type { GlobVirtualFS, GlobOptions } from \"./utils/index.mjs\";\n\n// Version Control\nexport { VersionControlSystem } from \"./vcs/index.mjs\";\nexport type {\n VCSConfig,\n VCSAttributeRule,\n VCSResolvedAttributes,\n VCSDiffMode,\n VCSPatchSuppressionReason,\n Revision,\n DiffEntry,\n TreeManifest,\n TreeEntry,\n FileEntry,\n DirectoryEntry,\n VCSIndexEntry,\n VCSIndexFile,\n CommitOptions,\n CheckoutOptions,\n LogOptions,\n LogEntry,\n BranchInfo,\n} from \"./vcs/index.mjs\";\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AACA;AACA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AACA;AACA;AAiBA;AAGA;AAGA;AAIA;AAsBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA;AAGA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYA;AACA;AAGA;AAIA;",
|
|
8
|
+
"debugId": "C4DB171A1954453764756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|