almostnode 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +731 -0
- package/dist/__sw__.js +394 -0
- package/dist/ai-chatbot-demo-entry.d.ts +6 -0
- package/dist/ai-chatbot-demo-entry.d.ts.map +1 -0
- package/dist/ai-chatbot-demo.d.ts +42 -0
- package/dist/ai-chatbot-demo.d.ts.map +1 -0
- package/dist/assets/runtime-worker-D9x_Ddwz.js +60543 -0
- package/dist/assets/runtime-worker-D9x_Ddwz.js.map +1 -0
- package/dist/convex-app-demo-entry.d.ts +6 -0
- package/dist/convex-app-demo-entry.d.ts.map +1 -0
- package/dist/convex-app-demo.d.ts +68 -0
- package/dist/convex-app-demo.d.ts.map +1 -0
- package/dist/cors-proxy.d.ts +46 -0
- package/dist/cors-proxy.d.ts.map +1 -0
- package/dist/create-runtime.d.ts +42 -0
- package/dist/create-runtime.d.ts.map +1 -0
- package/dist/demo.d.ts +6 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/dev-server.d.ts +97 -0
- package/dist/dev-server.d.ts.map +1 -0
- package/dist/frameworks/next-dev-server.d.ts +202 -0
- package/dist/frameworks/next-dev-server.d.ts.map +1 -0
- package/dist/frameworks/vite-dev-server.d.ts +85 -0
- package/dist/frameworks/vite-dev-server.d.ts.map +1 -0
- package/dist/index.cjs +14965 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +14867 -0
- package/dist/index.mjs.map +1 -0
- package/dist/next-demo.d.ts +49 -0
- package/dist/next-demo.d.ts.map +1 -0
- package/dist/npm/index.d.ts +71 -0
- package/dist/npm/index.d.ts.map +1 -0
- package/dist/npm/registry.d.ts +66 -0
- package/dist/npm/registry.d.ts.map +1 -0
- package/dist/npm/resolver.d.ts +52 -0
- package/dist/npm/resolver.d.ts.map +1 -0
- package/dist/npm/tarball.d.ts +29 -0
- package/dist/npm/tarball.d.ts.map +1 -0
- package/dist/runtime-interface.d.ts +90 -0
- package/dist/runtime-interface.d.ts.map +1 -0
- package/dist/runtime.d.ts +103 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/sandbox-helpers.d.ts +43 -0
- package/dist/sandbox-helpers.d.ts.map +1 -0
- package/dist/sandbox-runtime.d.ts +65 -0
- package/dist/sandbox-runtime.d.ts.map +1 -0
- package/dist/server-bridge.d.ts +89 -0
- package/dist/server-bridge.d.ts.map +1 -0
- package/dist/shims/assert.d.ts +51 -0
- package/dist/shims/assert.d.ts.map +1 -0
- package/dist/shims/async_hooks.d.ts +37 -0
- package/dist/shims/async_hooks.d.ts.map +1 -0
- package/dist/shims/buffer.d.ts +20 -0
- package/dist/shims/buffer.d.ts.map +1 -0
- package/dist/shims/child_process-browser.d.ts +92 -0
- package/dist/shims/child_process-browser.d.ts.map +1 -0
- package/dist/shims/child_process.d.ts +93 -0
- package/dist/shims/child_process.d.ts.map +1 -0
- package/dist/shims/chokidar.d.ts +55 -0
- package/dist/shims/chokidar.d.ts.map +1 -0
- package/dist/shims/cluster.d.ts +52 -0
- package/dist/shims/cluster.d.ts.map +1 -0
- package/dist/shims/crypto.d.ts +122 -0
- package/dist/shims/crypto.d.ts.map +1 -0
- package/dist/shims/dgram.d.ts +34 -0
- package/dist/shims/dgram.d.ts.map +1 -0
- package/dist/shims/diagnostics_channel.d.ts +80 -0
- package/dist/shims/diagnostics_channel.d.ts.map +1 -0
- package/dist/shims/dns.d.ts +87 -0
- package/dist/shims/dns.d.ts.map +1 -0
- package/dist/shims/domain.d.ts +25 -0
- package/dist/shims/domain.d.ts.map +1 -0
- package/dist/shims/esbuild.d.ts +105 -0
- package/dist/shims/esbuild.d.ts.map +1 -0
- package/dist/shims/events.d.ts +37 -0
- package/dist/shims/events.d.ts.map +1 -0
- package/dist/shims/fs.d.ts +115 -0
- package/dist/shims/fs.d.ts.map +1 -0
- package/dist/shims/fsevents.d.ts +67 -0
- package/dist/shims/fsevents.d.ts.map +1 -0
- package/dist/shims/http.d.ts +217 -0
- package/dist/shims/http.d.ts.map +1 -0
- package/dist/shims/http2.d.ts +81 -0
- package/dist/shims/http2.d.ts.map +1 -0
- package/dist/shims/https.d.ts +36 -0
- package/dist/shims/https.d.ts.map +1 -0
- package/dist/shims/inspector.d.ts +25 -0
- package/dist/shims/inspector.d.ts.map +1 -0
- package/dist/shims/module.d.ts +22 -0
- package/dist/shims/module.d.ts.map +1 -0
- package/dist/shims/net.d.ts +100 -0
- package/dist/shims/net.d.ts.map +1 -0
- package/dist/shims/os.d.ts +159 -0
- package/dist/shims/os.d.ts.map +1 -0
- package/dist/shims/path.d.ts +72 -0
- package/dist/shims/path.d.ts.map +1 -0
- package/dist/shims/perf_hooks.d.ts +50 -0
- package/dist/shims/perf_hooks.d.ts.map +1 -0
- package/dist/shims/process.d.ts +93 -0
- package/dist/shims/process.d.ts.map +1 -0
- package/dist/shims/querystring.d.ts +23 -0
- package/dist/shims/querystring.d.ts.map +1 -0
- package/dist/shims/readdirp.d.ts +52 -0
- package/dist/shims/readdirp.d.ts.map +1 -0
- package/dist/shims/readline.d.ts +62 -0
- package/dist/shims/readline.d.ts.map +1 -0
- package/dist/shims/rollup.d.ts +34 -0
- package/dist/shims/rollup.d.ts.map +1 -0
- package/dist/shims/sentry.d.ts +163 -0
- package/dist/shims/sentry.d.ts.map +1 -0
- package/dist/shims/stream.d.ts +181 -0
- package/dist/shims/stream.d.ts.map +1 -0
- package/dist/shims/tls.d.ts +53 -0
- package/dist/shims/tls.d.ts.map +1 -0
- package/dist/shims/tty.d.ts +30 -0
- package/dist/shims/tty.d.ts.map +1 -0
- package/dist/shims/url.d.ts +64 -0
- package/dist/shims/url.d.ts.map +1 -0
- package/dist/shims/util.d.ts +106 -0
- package/dist/shims/util.d.ts.map +1 -0
- package/dist/shims/v8.d.ts +73 -0
- package/dist/shims/v8.d.ts.map +1 -0
- package/dist/shims/vfs-adapter.d.ts +126 -0
- package/dist/shims/vfs-adapter.d.ts.map +1 -0
- package/dist/shims/vm.d.ts +45 -0
- package/dist/shims/vm.d.ts.map +1 -0
- package/dist/shims/worker_threads.d.ts +66 -0
- package/dist/shims/worker_threads.d.ts.map +1 -0
- package/dist/shims/ws.d.ts +66 -0
- package/dist/shims/ws.d.ts.map +1 -0
- package/dist/shims/zlib.d.ts +161 -0
- package/dist/shims/zlib.d.ts.map +1 -0
- package/dist/transform.d.ts +24 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/virtual-fs.d.ts +226 -0
- package/dist/virtual-fs.d.ts.map +1 -0
- package/dist/vite-demo.d.ts +35 -0
- package/dist/vite-demo.d.ts.map +1 -0
- package/dist/vite-sw.js +132 -0
- package/dist/worker/runtime-worker.d.ts +8 -0
- package/dist/worker/runtime-worker.d.ts.map +1 -0
- package/dist/worker-runtime.d.ts +50 -0
- package/dist/worker-runtime.d.ts.map +1 -0
- package/package.json +85 -0
- package/src/ai-chatbot-demo-entry.ts +244 -0
- package/src/ai-chatbot-demo.ts +509 -0
- package/src/convex-app-demo-entry.ts +1107 -0
- package/src/convex-app-demo.ts +1316 -0
- package/src/cors-proxy.ts +81 -0
- package/src/create-runtime.ts +147 -0
- package/src/demo.ts +304 -0
- package/src/dev-server.ts +274 -0
- package/src/frameworks/next-dev-server.ts +2224 -0
- package/src/frameworks/vite-dev-server.ts +702 -0
- package/src/index.ts +101 -0
- package/src/next-demo.ts +1784 -0
- package/src/npm/index.ts +347 -0
- package/src/npm/registry.ts +152 -0
- package/src/npm/resolver.ts +385 -0
- package/src/npm/tarball.ts +209 -0
- package/src/runtime-interface.ts +103 -0
- package/src/runtime.ts +1046 -0
- package/src/sandbox-helpers.ts +173 -0
- package/src/sandbox-runtime.ts +252 -0
- package/src/server-bridge.ts +426 -0
- package/src/shims/assert.ts +664 -0
- package/src/shims/async_hooks.ts +86 -0
- package/src/shims/buffer.ts +75 -0
- package/src/shims/child_process-browser.ts +217 -0
- package/src/shims/child_process.ts +463 -0
- package/src/shims/chokidar.ts +313 -0
- package/src/shims/cluster.ts +67 -0
- package/src/shims/crypto.ts +830 -0
- package/src/shims/dgram.ts +47 -0
- package/src/shims/diagnostics_channel.ts +196 -0
- package/src/shims/dns.ts +172 -0
- package/src/shims/domain.ts +58 -0
- package/src/shims/esbuild.ts +805 -0
- package/src/shims/events.ts +195 -0
- package/src/shims/fs.ts +803 -0
- package/src/shims/fsevents.ts +63 -0
- package/src/shims/http.ts +904 -0
- package/src/shims/http2.ts +96 -0
- package/src/shims/https.ts +86 -0
- package/src/shims/inspector.ts +30 -0
- package/src/shims/module.ts +82 -0
- package/src/shims/net.ts +359 -0
- package/src/shims/os.ts +195 -0
- package/src/shims/path.ts +199 -0
- package/src/shims/perf_hooks.ts +92 -0
- package/src/shims/process.ts +346 -0
- package/src/shims/querystring.ts +97 -0
- package/src/shims/readdirp.ts +228 -0
- package/src/shims/readline.ts +110 -0
- package/src/shims/rollup.ts +80 -0
- package/src/shims/sentry.ts +133 -0
- package/src/shims/stream.ts +1126 -0
- package/src/shims/tls.ts +95 -0
- package/src/shims/tty.ts +64 -0
- package/src/shims/url.ts +171 -0
- package/src/shims/util.ts +312 -0
- package/src/shims/v8.ts +113 -0
- package/src/shims/vfs-adapter.ts +402 -0
- package/src/shims/vm.ts +83 -0
- package/src/shims/worker_threads.ts +111 -0
- package/src/shims/ws.ts +382 -0
- package/src/shims/zlib.ts +289 -0
- package/src/transform.ts +313 -0
- package/src/types/external.d.ts +67 -0
- package/src/virtual-fs.ts +903 -0
- package/src/vite-demo.ts +577 -0
- package/src/worker/runtime-worker.ts +128 -0
- package/src/worker-runtime.ts +145 -0
package/src/shims/fs.ts
ADDED
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js fs module shim
|
|
3
|
+
* Wraps VirtualFS to provide Node.js compatible API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { VirtualFS, createNodeError } from '../virtual-fs';
|
|
7
|
+
import type { Stats, FSWatcher, WatchListener, WatchEventType } from '../virtual-fs';
|
|
8
|
+
|
|
9
|
+
export type { Stats, FSWatcher, WatchListener, WatchEventType };
|
|
10
|
+
|
|
11
|
+
export interface FsShim {
|
|
12
|
+
readFileSync(path: string): Buffer;
|
|
13
|
+
readFileSync(path: string, encoding: 'utf8' | 'utf-8'): string;
|
|
14
|
+
readFileSync(path: string, options: { encoding: 'utf8' | 'utf-8' }): string;
|
|
15
|
+
readFileSync(path: string, options: { encoding?: null }): Buffer;
|
|
16
|
+
writeFileSync(path: string, data: string | Uint8Array): void;
|
|
17
|
+
existsSync(path: string): boolean;
|
|
18
|
+
mkdirSync(path: string, options?: { recursive?: boolean }): void;
|
|
19
|
+
readdirSync(path: string): string[];
|
|
20
|
+
readdirSync(path: string, options: { withFileTypes: true }): Dirent[];
|
|
21
|
+
readdirSync(path: string, options?: { withFileTypes?: boolean; encoding?: string } | string): string[] | Dirent[];
|
|
22
|
+
statSync(path: string): Stats;
|
|
23
|
+
lstatSync(path: string): Stats;
|
|
24
|
+
fstatSync(fd: number): Stats;
|
|
25
|
+
unlinkSync(path: string): void;
|
|
26
|
+
rmdirSync(path: string): void;
|
|
27
|
+
renameSync(oldPath: string, newPath: string): void;
|
|
28
|
+
realpathSync(path: string): string;
|
|
29
|
+
accessSync(path: string, mode?: number): void;
|
|
30
|
+
copyFileSync(src: string, dest: string): void;
|
|
31
|
+
openSync(path: string, flags: string | number, mode?: number): number;
|
|
32
|
+
closeSync(fd: number): void;
|
|
33
|
+
readSync(fd: number, buffer: Buffer | Uint8Array, offset: number, length: number, position: number | null): number;
|
|
34
|
+
writeSync(fd: number, buffer: Buffer | Uint8Array | string, offset?: number, length?: number, position?: number | null): number;
|
|
35
|
+
ftruncateSync(fd: number, len?: number): void;
|
|
36
|
+
fsyncSync(fd: number): void;
|
|
37
|
+
fdatasyncSync(fd: number): void;
|
|
38
|
+
mkdtempSync(prefix: string): string;
|
|
39
|
+
rmSync(path: string, options?: { recursive?: boolean; force?: boolean }): void;
|
|
40
|
+
watch(filename: string, options?: { persistent?: boolean; recursive?: boolean }, listener?: WatchListener): FSWatcher;
|
|
41
|
+
watch(filename: string, listener?: WatchListener): FSWatcher;
|
|
42
|
+
readFile(path: string, callback: (err: Error | null, data?: Uint8Array) => void): void;
|
|
43
|
+
readFile(path: string, options: { encoding: string }, callback: (err: Error | null, data?: string) => void): void;
|
|
44
|
+
stat(path: string, callback: (err: Error | null, stats?: Stats) => void): void;
|
|
45
|
+
lstat(path: string, callback: (err: Error | null, stats?: Stats) => void): void;
|
|
46
|
+
readdir(path: string, callback: (err: Error | null, files?: string[]) => void): void;
|
|
47
|
+
realpath(path: string, callback: (err: Error | null, resolvedPath?: string) => void): void;
|
|
48
|
+
access(path: string, callback: (err: Error | null) => void): void;
|
|
49
|
+
access(path: string, mode: number, callback: (err: Error | null) => void): void;
|
|
50
|
+
createReadStream(path: string): unknown;
|
|
51
|
+
createWriteStream(path: string): unknown;
|
|
52
|
+
promises: FsPromises;
|
|
53
|
+
constants: FsConstants;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface FsPromises {
|
|
57
|
+
readFile(path: string): Promise<Buffer>;
|
|
58
|
+
readFile(path: string, encoding: 'utf8' | 'utf-8'): Promise<string>;
|
|
59
|
+
readFile(path: string, options: { encoding: 'utf8' | 'utf-8' }): Promise<string>;
|
|
60
|
+
writeFile(path: string, data: string | Uint8Array): Promise<void>;
|
|
61
|
+
stat(path: string): Promise<Stats>;
|
|
62
|
+
lstat(path: string): Promise<Stats>;
|
|
63
|
+
readdir(path: string): Promise<string[]>;
|
|
64
|
+
mkdir(path: string, options?: { recursive?: boolean }): Promise<void>;
|
|
65
|
+
unlink(path: string): Promise<void>;
|
|
66
|
+
rmdir(path: string): Promise<void>;
|
|
67
|
+
rename(oldPath: string, newPath: string): Promise<void>;
|
|
68
|
+
access(path: string, mode?: number): Promise<void>;
|
|
69
|
+
realpath(path: string): Promise<string>;
|
|
70
|
+
copyFile(src: string, dest: string): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface FsConstants {
|
|
74
|
+
F_OK: number;
|
|
75
|
+
R_OK: number;
|
|
76
|
+
W_OK: number;
|
|
77
|
+
X_OK: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Dirent class - represents a directory entry returned by readdirSync with withFileTypes: true
|
|
82
|
+
*/
|
|
83
|
+
export class Dirent {
|
|
84
|
+
name: string;
|
|
85
|
+
private _isDirectory: boolean;
|
|
86
|
+
private _isFile: boolean;
|
|
87
|
+
|
|
88
|
+
constructor(name: string, isDirectory: boolean, isFile: boolean) {
|
|
89
|
+
this.name = name;
|
|
90
|
+
this._isDirectory = isDirectory;
|
|
91
|
+
this._isFile = isFile;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
isDirectory(): boolean {
|
|
95
|
+
return this._isDirectory;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
isFile(): boolean {
|
|
99
|
+
return this._isFile;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
isBlockDevice(): boolean {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
isCharacterDevice(): boolean {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
isFIFO(): boolean {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
isSocket(): boolean {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
isSymbolicLink(): boolean {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Create a Buffer-like object from Uint8Array
|
|
125
|
+
* This is a minimal Buffer implementation for browser compatibility
|
|
126
|
+
*/
|
|
127
|
+
function createBuffer(data: Uint8Array): Buffer {
|
|
128
|
+
const buffer = data as Buffer;
|
|
129
|
+
|
|
130
|
+
// Add Buffer-specific methods
|
|
131
|
+
Object.defineProperty(buffer, 'toString', {
|
|
132
|
+
value: function (encoding?: string) {
|
|
133
|
+
if (encoding === 'utf8' || encoding === 'utf-8' || !encoding) {
|
|
134
|
+
return new TextDecoder().decode(this);
|
|
135
|
+
}
|
|
136
|
+
if (encoding === 'base64') {
|
|
137
|
+
let binary = '';
|
|
138
|
+
for (let i = 0; i < this.length; i++) {
|
|
139
|
+
binary += String.fromCharCode(this[i]);
|
|
140
|
+
}
|
|
141
|
+
return btoa(binary);
|
|
142
|
+
}
|
|
143
|
+
if (encoding === 'hex') {
|
|
144
|
+
return Array.from(this as Uint8Array)
|
|
145
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
146
|
+
.join('');
|
|
147
|
+
}
|
|
148
|
+
throw new Error(`Unsupported encoding: ${encoding}`);
|
|
149
|
+
},
|
|
150
|
+
writable: true,
|
|
151
|
+
configurable: true,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return buffer;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Convert a path-like value to a string path
|
|
159
|
+
* Handles URL objects (file:// protocol) and Buffer
|
|
160
|
+
*/
|
|
161
|
+
// Path remapping for CLI tools that use incorrect absolute paths
|
|
162
|
+
// This maps /convex/ -> /project/convex/ to fix the Convex CLI path issue
|
|
163
|
+
const pathRemaps: Array<{ from: string; to: string }> = [
|
|
164
|
+
{ from: '/convex/', to: '/project/convex/' },
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
function remapPath(path: string): string {
|
|
168
|
+
// Strip 'vfs:' namespace prefix from paths (comes from esbuild namespace)
|
|
169
|
+
if (path.includes('vfs:')) {
|
|
170
|
+
const cleanPath = path.replace(/vfs:/g, '');
|
|
171
|
+
if (!remapPath.logged) remapPath.logged = new Set();
|
|
172
|
+
if (!remapPath.logged.has(path)) {
|
|
173
|
+
console.log(`[fs] Stripping vfs: prefix: ${path} -> ${cleanPath}`);
|
|
174
|
+
remapPath.logged.add(path);
|
|
175
|
+
}
|
|
176
|
+
path = cleanPath;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const remap of pathRemaps) {
|
|
180
|
+
if (path === remap.from.slice(0, -1) || path.startsWith(remap.from)) {
|
|
181
|
+
const remapped = remap.to + path.slice(remap.from.length);
|
|
182
|
+
// Only log once per unique path to avoid noise
|
|
183
|
+
if (!remapPath.logged) remapPath.logged = new Set();
|
|
184
|
+
if (!remapPath.logged.has(path)) {
|
|
185
|
+
console.log(`[fs] Remapping path: ${path} -> ${remapped}`);
|
|
186
|
+
remapPath.logged.add(path);
|
|
187
|
+
}
|
|
188
|
+
return remapped;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return path;
|
|
192
|
+
}
|
|
193
|
+
remapPath.logged = new Set<string>();
|
|
194
|
+
|
|
195
|
+
function toPath(pathLike: unknown, getCwd?: () => string): string {
|
|
196
|
+
let path: string;
|
|
197
|
+
|
|
198
|
+
if (typeof pathLike === 'string') {
|
|
199
|
+
path = pathLike;
|
|
200
|
+
} else if (pathLike instanceof URL) {
|
|
201
|
+
// Handle file:// URLs
|
|
202
|
+
if (pathLike.protocol === 'file:') {
|
|
203
|
+
// Remove file:// prefix and decode
|
|
204
|
+
path = decodeURIComponent(pathLike.pathname);
|
|
205
|
+
} else {
|
|
206
|
+
throw new Error(`Unsupported URL protocol: ${pathLike.protocol}`);
|
|
207
|
+
}
|
|
208
|
+
} else if (Buffer.isBuffer(pathLike)) {
|
|
209
|
+
path = pathLike.toString('utf8');
|
|
210
|
+
} else if (pathLike && typeof pathLike === 'object' && 'toString' in pathLike) {
|
|
211
|
+
path = String(pathLike);
|
|
212
|
+
} else {
|
|
213
|
+
throw new TypeError(`Path must be a string, URL, or Buffer. Received: ${typeof pathLike}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Resolve relative paths against cwd
|
|
217
|
+
if (!path.startsWith('/') && getCwd) {
|
|
218
|
+
const cwd = getCwd();
|
|
219
|
+
path = cwd.endsWith('/') ? cwd + path : cwd + '/' + path;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Apply path remapping for CLI tools that use incorrect absolute paths
|
|
223
|
+
path = remapPath(path);
|
|
224
|
+
|
|
225
|
+
return path;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// File descriptor tracking
|
|
229
|
+
interface FileDescriptor {
|
|
230
|
+
path: string;
|
|
231
|
+
position: number;
|
|
232
|
+
flags: string;
|
|
233
|
+
content: Uint8Array;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const fdMap = new Map<number, FileDescriptor>();
|
|
237
|
+
let nextFd = 3; // Start at 3 (0, 1, 2 are stdin, stdout, stderr)
|
|
238
|
+
|
|
239
|
+
// Call tracking for infinite loop detection
|
|
240
|
+
const callTracker = {
|
|
241
|
+
statSync: new Map<string, number>(),
|
|
242
|
+
readdirSync: new Map<string, number>(),
|
|
243
|
+
lastReset: Date.now(),
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
function trackCall(method: 'statSync' | 'readdirSync', path: string): void {
|
|
247
|
+
// Reset counters every 500ms to allow legitimate repeated calls
|
|
248
|
+
const now = Date.now();
|
|
249
|
+
if (now - callTracker.lastReset > 500) {
|
|
250
|
+
callTracker.statSync.clear();
|
|
251
|
+
callTracker.readdirSync.clear();
|
|
252
|
+
callTracker.lastReset = now;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const map = callTracker[method];
|
|
256
|
+
const count = (map.get(path) || 0) + 1;
|
|
257
|
+
map.set(path, count);
|
|
258
|
+
|
|
259
|
+
// Log at different thresholds to understand the pattern
|
|
260
|
+
if (count === 10 && path.includes('_generated')) {
|
|
261
|
+
console.warn(`[fs] ${method} called ${count}x on ${path}`);
|
|
262
|
+
// Print full stack trace at 10 calls to see the call path
|
|
263
|
+
const err = new Error();
|
|
264
|
+
console.log(`[fs] Stack at ${count} calls:`, err.stack?.split('\n').slice(1, 10).join('\n'));
|
|
265
|
+
}
|
|
266
|
+
if (count === 50) {
|
|
267
|
+
console.warn(`[fs] Potential infinite loop: ${method} called ${count}+ times on ${path}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function createFsShim(vfs: VirtualFS, getCwd?: () => string): FsShim {
|
|
272
|
+
// Helper to resolve paths with cwd
|
|
273
|
+
const resolvePath = (pathLike: unknown) => toPath(pathLike, getCwd);
|
|
274
|
+
const constants: FsConstants = {
|
|
275
|
+
F_OK: 0,
|
|
276
|
+
R_OK: 4,
|
|
277
|
+
W_OK: 2,
|
|
278
|
+
X_OK: 1,
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const promises: FsPromises = {
|
|
282
|
+
readFile(pathLike: unknown, encodingOrOptions?: string | { encoding?: string | null }): Promise<Buffer | string> {
|
|
283
|
+
return new Promise((resolve, reject) => {
|
|
284
|
+
try {
|
|
285
|
+
const path = resolvePath(pathLike);
|
|
286
|
+
let encoding: string | undefined;
|
|
287
|
+
if (typeof encodingOrOptions === 'string') {
|
|
288
|
+
encoding = encodingOrOptions;
|
|
289
|
+
} else if (encodingOrOptions?.encoding) {
|
|
290
|
+
encoding = encodingOrOptions.encoding;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (encoding === 'utf8' || encoding === 'utf-8') {
|
|
294
|
+
resolve(vfs.readFileSync(path, 'utf8'));
|
|
295
|
+
} else {
|
|
296
|
+
resolve(createBuffer(vfs.readFileSync(path)));
|
|
297
|
+
}
|
|
298
|
+
} catch (err) {
|
|
299
|
+
reject(err);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
},
|
|
303
|
+
writeFile(pathLike: unknown, data: string | Uint8Array): Promise<void> {
|
|
304
|
+
return new Promise((resolve, reject) => {
|
|
305
|
+
try {
|
|
306
|
+
vfs.writeFileSync(resolvePath(pathLike), data);
|
|
307
|
+
resolve();
|
|
308
|
+
} catch (err) {
|
|
309
|
+
reject(err);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
},
|
|
313
|
+
stat(pathLike: string | unknown): Promise<Stats> {
|
|
314
|
+
return new Promise((resolve, reject) => {
|
|
315
|
+
try {
|
|
316
|
+
const path = typeof pathLike === 'string' ? pathLike : resolvePath(pathLike);
|
|
317
|
+
resolve(vfs.statSync(path));
|
|
318
|
+
} catch (err) {
|
|
319
|
+
reject(err);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
},
|
|
323
|
+
lstat(pathLike: unknown): Promise<Stats> {
|
|
324
|
+
return this.stat(resolvePath(pathLike));
|
|
325
|
+
},
|
|
326
|
+
readdir(pathLike: unknown): Promise<string[]> {
|
|
327
|
+
return new Promise((resolve, reject) => {
|
|
328
|
+
try {
|
|
329
|
+
resolve(vfs.readdirSync(resolvePath(pathLike)));
|
|
330
|
+
} catch (err) {
|
|
331
|
+
reject(err);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
},
|
|
335
|
+
mkdir(pathLike: unknown, options?: { recursive?: boolean }): Promise<void> {
|
|
336
|
+
return new Promise((resolve, reject) => {
|
|
337
|
+
try {
|
|
338
|
+
vfs.mkdirSync(resolvePath(pathLike), options);
|
|
339
|
+
resolve();
|
|
340
|
+
} catch (err) {
|
|
341
|
+
reject(err);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
},
|
|
345
|
+
unlink(pathLike: unknown): Promise<void> {
|
|
346
|
+
return new Promise((resolve, reject) => {
|
|
347
|
+
try {
|
|
348
|
+
vfs.unlinkSync(resolvePath(pathLike));
|
|
349
|
+
resolve();
|
|
350
|
+
} catch (err) {
|
|
351
|
+
reject(err);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
},
|
|
355
|
+
rmdir(path: string): Promise<void> {
|
|
356
|
+
return new Promise((resolve, reject) => {
|
|
357
|
+
try {
|
|
358
|
+
vfs.rmdirSync(path);
|
|
359
|
+
resolve();
|
|
360
|
+
} catch (err) {
|
|
361
|
+
reject(err);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
},
|
|
365
|
+
rename(oldPath: string, newPath: string): Promise<void> {
|
|
366
|
+
return new Promise((resolve, reject) => {
|
|
367
|
+
try {
|
|
368
|
+
vfs.renameSync(oldPath, newPath);
|
|
369
|
+
resolve();
|
|
370
|
+
} catch (err) {
|
|
371
|
+
reject(err);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
},
|
|
375
|
+
access(path: string, mode?: number): Promise<void> {
|
|
376
|
+
return new Promise((resolve, reject) => {
|
|
377
|
+
try {
|
|
378
|
+
vfs.accessSync(path, mode);
|
|
379
|
+
resolve();
|
|
380
|
+
} catch (err) {
|
|
381
|
+
reject(err);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
},
|
|
385
|
+
realpath(path: string): Promise<string> {
|
|
386
|
+
return new Promise((resolve, reject) => {
|
|
387
|
+
try {
|
|
388
|
+
resolve(vfs.realpathSync(path));
|
|
389
|
+
} catch (err) {
|
|
390
|
+
reject(err);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
},
|
|
394
|
+
copyFile(src: string, dest: string): Promise<void> {
|
|
395
|
+
return new Promise((resolve, reject) => {
|
|
396
|
+
try {
|
|
397
|
+
vfs.copyFileSync(src, dest);
|
|
398
|
+
resolve();
|
|
399
|
+
} catch (err) {
|
|
400
|
+
reject(err);
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
},
|
|
404
|
+
} as FsPromises;
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
readFileSync(
|
|
408
|
+
pathLike: unknown,
|
|
409
|
+
encodingOrOptions?: string | { encoding?: string | null }
|
|
410
|
+
): Buffer | string {
|
|
411
|
+
const path = resolvePath(pathLike);
|
|
412
|
+
let encoding: string | undefined;
|
|
413
|
+
|
|
414
|
+
if (typeof encodingOrOptions === 'string') {
|
|
415
|
+
encoding = encodingOrOptions;
|
|
416
|
+
} else if (encodingOrOptions?.encoding) {
|
|
417
|
+
encoding = encodingOrOptions.encoding;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (encoding === 'utf8' || encoding === 'utf-8') {
|
|
421
|
+
return vfs.readFileSync(path, 'utf8');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const data = vfs.readFileSync(path);
|
|
425
|
+
return createBuffer(data);
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
writeFileSync(pathLike: unknown, data: string | Uint8Array): void {
|
|
429
|
+
// Handle file descriptor
|
|
430
|
+
if (typeof pathLike === 'number') {
|
|
431
|
+
const fd = pathLike;
|
|
432
|
+
const entry = fdMap.get(fd);
|
|
433
|
+
if (!entry) {
|
|
434
|
+
const err = new Error(`EBADF: bad file descriptor, write`) as Error & { code: string; errno: number };
|
|
435
|
+
err.code = 'EBADF';
|
|
436
|
+
err.errno = -9;
|
|
437
|
+
throw err;
|
|
438
|
+
}
|
|
439
|
+
// Convert string to Uint8Array if needed
|
|
440
|
+
const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;
|
|
441
|
+
// Replace entire content
|
|
442
|
+
entry.content = new Uint8Array(bytes);
|
|
443
|
+
entry.position = bytes.length;
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
const path = resolvePath(pathLike);
|
|
447
|
+
// Debug: Log when writing to convex directories
|
|
448
|
+
if (path.includes('convex') || path.includes('_generated')) {
|
|
449
|
+
console.log('[fs] writeFileSync:', path);
|
|
450
|
+
}
|
|
451
|
+
vfs.writeFileSync(path, data);
|
|
452
|
+
},
|
|
453
|
+
|
|
454
|
+
existsSync(pathLike: unknown): boolean {
|
|
455
|
+
return vfs.existsSync(resolvePath(pathLike));
|
|
456
|
+
},
|
|
457
|
+
|
|
458
|
+
mkdirSync(pathLike: unknown, options?: { recursive?: boolean }): void {
|
|
459
|
+
const path = resolvePath(pathLike);
|
|
460
|
+
// Debug: Log when creating convex directories
|
|
461
|
+
if (path.includes('convex') || path.includes('_generated')) {
|
|
462
|
+
console.log('[fs] mkdirSync:', path, options);
|
|
463
|
+
}
|
|
464
|
+
vfs.mkdirSync(path, options);
|
|
465
|
+
},
|
|
466
|
+
|
|
467
|
+
readdirSync(pathLike: unknown, options?: { withFileTypes?: boolean; encoding?: string } | string): string[] | Dirent[] {
|
|
468
|
+
const path = resolvePath(pathLike);
|
|
469
|
+
trackCall('readdirSync', path);
|
|
470
|
+
const entries = vfs.readdirSync(path);
|
|
471
|
+
|
|
472
|
+
// Handle withFileTypes option - returns Dirent objects instead of strings
|
|
473
|
+
const opts = typeof options === 'string' ? { encoding: options } : options;
|
|
474
|
+
if (opts?.withFileTypes) {
|
|
475
|
+
const dirents: Dirent[] = entries.map(name => {
|
|
476
|
+
const entryPath = path.endsWith('/') ? path + name : path + '/' + name;
|
|
477
|
+
let isDir = false;
|
|
478
|
+
let isFile = false;
|
|
479
|
+
try {
|
|
480
|
+
const stat = vfs.statSync(entryPath);
|
|
481
|
+
isDir = stat.isDirectory();
|
|
482
|
+
isFile = stat.isFile();
|
|
483
|
+
} catch {
|
|
484
|
+
isFile = true; // Default to file if stat fails
|
|
485
|
+
}
|
|
486
|
+
return new Dirent(name, isDir, isFile);
|
|
487
|
+
});
|
|
488
|
+
// Debug: Log readdirSync results for _generated
|
|
489
|
+
if (path.includes('_generated')) {
|
|
490
|
+
console.log(`[fs] readdirSync(${path}, withFileTypes) -> [${dirents.map(d => d.name).join(', ')}]`);
|
|
491
|
+
}
|
|
492
|
+
return dirents;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Debug: Log readdirSync results for _generated
|
|
496
|
+
if (path.includes('_generated')) {
|
|
497
|
+
console.log(`[fs] readdirSync(${path}) -> [${entries.join(', ')}]`);
|
|
498
|
+
}
|
|
499
|
+
return entries;
|
|
500
|
+
},
|
|
501
|
+
|
|
502
|
+
statSync(pathLike: unknown): Stats {
|
|
503
|
+
const origPath = typeof pathLike === 'string' ? pathLike : String(pathLike);
|
|
504
|
+
const path = resolvePath(pathLike);
|
|
505
|
+
trackCall('statSync', path);
|
|
506
|
+
const result = vfs.statSync(path);
|
|
507
|
+
// Debug: Log all statSync calls on _generated paths (show if path was modified)
|
|
508
|
+
if (path.includes('_generated')) {
|
|
509
|
+
const wasRemapped = origPath !== path;
|
|
510
|
+
console.log(`[fs] statSync(${origPath}${wasRemapped ? ' -> ' + path : ''}) -> isDir: ${result.isDirectory()}`);
|
|
511
|
+
}
|
|
512
|
+
return result;
|
|
513
|
+
},
|
|
514
|
+
|
|
515
|
+
lstatSync(pathLike: unknown): Stats {
|
|
516
|
+
return vfs.lstatSync(resolvePath(pathLike));
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
fstatSync(fd: number): Stats {
|
|
520
|
+
const entry = fdMap.get(fd);
|
|
521
|
+
if (!entry) {
|
|
522
|
+
const err = new Error(`EBADF: bad file descriptor, fstat`) as Error & { code: string; errno: number };
|
|
523
|
+
err.code = 'EBADF';
|
|
524
|
+
err.errno = -9;
|
|
525
|
+
throw err;
|
|
526
|
+
}
|
|
527
|
+
return vfs.statSync(entry.path);
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
openSync(pathLike: unknown, flags: string | number, _mode?: number): number {
|
|
531
|
+
const path = resolvePath(pathLike);
|
|
532
|
+
const flagStr = typeof flags === 'number' ? 'r' : flags;
|
|
533
|
+
|
|
534
|
+
// Check if file exists for read modes
|
|
535
|
+
const exists = vfs.existsSync(path);
|
|
536
|
+
const isWriteMode = flagStr.includes('w') || flagStr.includes('a');
|
|
537
|
+
const isReadMode = flagStr.includes('r') && !flagStr.includes('+');
|
|
538
|
+
|
|
539
|
+
if (!exists && isReadMode) {
|
|
540
|
+
const err = new Error(`ENOENT: no such file or directory, open '${path}'`) as Error & { code: string; errno: number; path: string };
|
|
541
|
+
err.code = 'ENOENT';
|
|
542
|
+
err.errno = -2;
|
|
543
|
+
err.path = path;
|
|
544
|
+
throw err;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Get or create content
|
|
548
|
+
let content: Uint8Array;
|
|
549
|
+
if (exists && !flagStr.includes('w')) {
|
|
550
|
+
content = vfs.readFileSync(path);
|
|
551
|
+
} else {
|
|
552
|
+
content = new Uint8Array(0);
|
|
553
|
+
if (isWriteMode) {
|
|
554
|
+
// Ensure parent directory exists
|
|
555
|
+
const parentPath = path.substring(0, path.lastIndexOf('/')) || '/';
|
|
556
|
+
if (!vfs.existsSync(parentPath)) {
|
|
557
|
+
vfs.mkdirSync(parentPath, { recursive: true });
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const fd = nextFd++;
|
|
563
|
+
fdMap.set(fd, {
|
|
564
|
+
path,
|
|
565
|
+
position: flagStr.includes('a') ? content.length : 0,
|
|
566
|
+
flags: flagStr,
|
|
567
|
+
content: new Uint8Array(content),
|
|
568
|
+
});
|
|
569
|
+
return fd;
|
|
570
|
+
},
|
|
571
|
+
|
|
572
|
+
closeSync(fd: number): void {
|
|
573
|
+
const entry = fdMap.get(fd);
|
|
574
|
+
if (!entry) {
|
|
575
|
+
return; // Silently ignore
|
|
576
|
+
}
|
|
577
|
+
// Write back content if it was opened for writing
|
|
578
|
+
if (entry.flags.includes('w') || entry.flags.includes('a') || entry.flags.includes('+')) {
|
|
579
|
+
vfs.writeFileSync(entry.path, entry.content);
|
|
580
|
+
}
|
|
581
|
+
fdMap.delete(fd);
|
|
582
|
+
},
|
|
583
|
+
|
|
584
|
+
readSync(fd: number, buffer: Buffer | Uint8Array, offset: number, length: number, position: number | null): number {
|
|
585
|
+
const entry = fdMap.get(fd);
|
|
586
|
+
if (!entry) {
|
|
587
|
+
const err = new Error(`EBADF: bad file descriptor, read`) as Error & { code: string; errno: number };
|
|
588
|
+
err.code = 'EBADF';
|
|
589
|
+
err.errno = -9;
|
|
590
|
+
throw err;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const readPos = position !== null ? position : entry.position;
|
|
594
|
+
const bytesToRead = Math.min(length, entry.content.length - readPos);
|
|
595
|
+
|
|
596
|
+
if (bytesToRead <= 0) {
|
|
597
|
+
return 0;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
for (let i = 0; i < bytesToRead; i++) {
|
|
601
|
+
buffer[offset + i] = entry.content[readPos + i];
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (position === null) {
|
|
605
|
+
entry.position += bytesToRead;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return bytesToRead;
|
|
609
|
+
},
|
|
610
|
+
|
|
611
|
+
writeSync(fd: number, buffer: Buffer | Uint8Array | string, offset?: number, length?: number, position?: number | null): number {
|
|
612
|
+
const entry = fdMap.get(fd);
|
|
613
|
+
if (!entry) {
|
|
614
|
+
const err = new Error(`EBADF: bad file descriptor, write`) as Error & { code: string; errno: number };
|
|
615
|
+
err.code = 'EBADF';
|
|
616
|
+
err.errno = -9;
|
|
617
|
+
throw err;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Handle string input
|
|
621
|
+
let data: Uint8Array;
|
|
622
|
+
if (typeof buffer === 'string') {
|
|
623
|
+
data = new TextEncoder().encode(buffer);
|
|
624
|
+
offset = 0;
|
|
625
|
+
length = data.length;
|
|
626
|
+
} else {
|
|
627
|
+
data = buffer;
|
|
628
|
+
offset = offset ?? 0;
|
|
629
|
+
length = length ?? (data.length - offset);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const writePos = position !== null && position !== undefined ? position : entry.position;
|
|
633
|
+
const endPos = writePos + length;
|
|
634
|
+
|
|
635
|
+
// Expand content if needed
|
|
636
|
+
if (endPos > entry.content.length) {
|
|
637
|
+
const newContent = new Uint8Array(endPos);
|
|
638
|
+
newContent.set(entry.content);
|
|
639
|
+
entry.content = newContent;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Write data
|
|
643
|
+
for (let i = 0; i < length; i++) {
|
|
644
|
+
entry.content[writePos + i] = data[offset + i];
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (position === null || position === undefined) {
|
|
648
|
+
entry.position = endPos;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
return length;
|
|
652
|
+
},
|
|
653
|
+
|
|
654
|
+
ftruncateSync(fd: number, len: number = 0): void {
|
|
655
|
+
const entry = fdMap.get(fd);
|
|
656
|
+
if (!entry) {
|
|
657
|
+
const err = new Error(`EBADF: bad file descriptor, ftruncate`) as Error & { code: string; errno: number };
|
|
658
|
+
err.code = 'EBADF';
|
|
659
|
+
err.errno = -9;
|
|
660
|
+
throw err;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (len < entry.content.length) {
|
|
664
|
+
entry.content = entry.content.slice(0, len);
|
|
665
|
+
} else if (len > entry.content.length) {
|
|
666
|
+
const newContent = new Uint8Array(len);
|
|
667
|
+
newContent.set(entry.content);
|
|
668
|
+
entry.content = newContent;
|
|
669
|
+
}
|
|
670
|
+
},
|
|
671
|
+
|
|
672
|
+
fsyncSync(_fd: number): void {
|
|
673
|
+
// No-op - our virtual FS doesn't have disk buffering
|
|
674
|
+
},
|
|
675
|
+
|
|
676
|
+
fdatasyncSync(_fd: number): void {
|
|
677
|
+
// No-op - our virtual FS doesn't have disk buffering
|
|
678
|
+
},
|
|
679
|
+
|
|
680
|
+
mkdtempSync(prefix: string): string {
|
|
681
|
+
// Generate a unique suffix
|
|
682
|
+
const suffix = Math.random().toString(36).slice(2, 8);
|
|
683
|
+
const tempDir = `${prefix}${suffix}`;
|
|
684
|
+
const resolvedPath = resolvePath(tempDir);
|
|
685
|
+
vfs.mkdirSync(resolvedPath, { recursive: true });
|
|
686
|
+
return resolvedPath;
|
|
687
|
+
},
|
|
688
|
+
|
|
689
|
+
rmSync(pathLike: unknown, options?: { recursive?: boolean; force?: boolean }): void {
|
|
690
|
+
const path = resolvePath(pathLike);
|
|
691
|
+
if (!vfs.existsSync(path)) {
|
|
692
|
+
if (options?.force) return;
|
|
693
|
+
throw createNodeError('ENOENT', 'rm', path);
|
|
694
|
+
}
|
|
695
|
+
const stats = vfs.statSync(path);
|
|
696
|
+
if (stats.isDirectory()) {
|
|
697
|
+
if (options?.recursive) {
|
|
698
|
+
// Recursively delete directory contents
|
|
699
|
+
const entries = vfs.readdirSync(path);
|
|
700
|
+
for (const entry of entries) {
|
|
701
|
+
const entryPath = path.endsWith('/') ? path + entry : path + '/' + entry;
|
|
702
|
+
this.rmSync(entryPath, options);
|
|
703
|
+
}
|
|
704
|
+
vfs.rmdirSync(path);
|
|
705
|
+
} else {
|
|
706
|
+
throw createNodeError('EISDIR', 'rm', path);
|
|
707
|
+
}
|
|
708
|
+
} else {
|
|
709
|
+
vfs.unlinkSync(path);
|
|
710
|
+
}
|
|
711
|
+
},
|
|
712
|
+
|
|
713
|
+
unlinkSync(pathLike: unknown): void {
|
|
714
|
+
const path = resolvePath(pathLike);
|
|
715
|
+
// Debug: Log unlink calls on _generated
|
|
716
|
+
if (path.includes('_generated')) {
|
|
717
|
+
console.log(`[fs] unlinkSync(${path})`);
|
|
718
|
+
}
|
|
719
|
+
vfs.unlinkSync(path);
|
|
720
|
+
},
|
|
721
|
+
|
|
722
|
+
rmdirSync(pathLike: unknown): void {
|
|
723
|
+
vfs.rmdirSync(resolvePath(pathLike));
|
|
724
|
+
},
|
|
725
|
+
|
|
726
|
+
renameSync(oldPathLike: unknown, newPathLike: unknown): void {
|
|
727
|
+
vfs.renameSync(resolvePath(oldPathLike), resolvePath(newPathLike));
|
|
728
|
+
},
|
|
729
|
+
|
|
730
|
+
realpathSync(pathLike: unknown): string {
|
|
731
|
+
return vfs.realpathSync(resolvePath(pathLike));
|
|
732
|
+
},
|
|
733
|
+
|
|
734
|
+
accessSync(pathLike: unknown, _mode?: number): void {
|
|
735
|
+
vfs.accessSync(resolvePath(pathLike));
|
|
736
|
+
},
|
|
737
|
+
|
|
738
|
+
copyFileSync(srcLike: unknown, destLike: unknown): void {
|
|
739
|
+
const src = resolvePath(srcLike);
|
|
740
|
+
const dest = resolvePath(destLike);
|
|
741
|
+
const data = vfs.readFileSync(src);
|
|
742
|
+
vfs.writeFileSync(dest, data);
|
|
743
|
+
},
|
|
744
|
+
|
|
745
|
+
watch(
|
|
746
|
+
pathLike: unknown,
|
|
747
|
+
optionsOrListener?: { persistent?: boolean; recursive?: boolean } | WatchListener,
|
|
748
|
+
listener?: WatchListener
|
|
749
|
+
): FSWatcher {
|
|
750
|
+
return vfs.watch(resolvePath(pathLike), optionsOrListener as { persistent?: boolean; recursive?: boolean }, listener);
|
|
751
|
+
},
|
|
752
|
+
|
|
753
|
+
readFile(
|
|
754
|
+
pathLike: unknown,
|
|
755
|
+
optionsOrCallback?: { encoding?: string } | ((err: Error | null, data?: string | Uint8Array) => void),
|
|
756
|
+
callback?: (err: Error | null, data?: string | Uint8Array) => void
|
|
757
|
+
): void {
|
|
758
|
+
const path = resolvePath(pathLike);
|
|
759
|
+
vfs.readFile(path, optionsOrCallback as { encoding?: string }, callback);
|
|
760
|
+
},
|
|
761
|
+
|
|
762
|
+
stat(pathLike: unknown, callback: (err: Error | null, stats?: Stats) => void): void {
|
|
763
|
+
vfs.stat(resolvePath(pathLike), callback);
|
|
764
|
+
},
|
|
765
|
+
|
|
766
|
+
lstat(pathLike: unknown, callback: (err: Error | null, stats?: Stats) => void): void {
|
|
767
|
+
vfs.lstat(resolvePath(pathLike), callback);
|
|
768
|
+
},
|
|
769
|
+
|
|
770
|
+
readdir(
|
|
771
|
+
pathLike: unknown,
|
|
772
|
+
optionsOrCallback?: { withFileTypes?: boolean } | ((err: Error | null, files?: string[]) => void),
|
|
773
|
+
callback?: (err: Error | null, files?: string[]) => void
|
|
774
|
+
): void {
|
|
775
|
+
vfs.readdir(resolvePath(pathLike), optionsOrCallback as { withFileTypes?: boolean }, callback);
|
|
776
|
+
},
|
|
777
|
+
|
|
778
|
+
realpath(pathLike: unknown, callback: (err: Error | null, resolvedPath?: string) => void): void {
|
|
779
|
+
vfs.realpath(resolvePath(pathLike), callback);
|
|
780
|
+
},
|
|
781
|
+
|
|
782
|
+
access(
|
|
783
|
+
pathLike: unknown,
|
|
784
|
+
modeOrCallback?: number | ((err: Error | null) => void),
|
|
785
|
+
callback?: (err: Error | null) => void
|
|
786
|
+
): void {
|
|
787
|
+
vfs.access(resolvePath(pathLike), modeOrCallback, callback);
|
|
788
|
+
},
|
|
789
|
+
|
|
790
|
+
createReadStream(pathLike: unknown): unknown {
|
|
791
|
+
return vfs.createReadStream(resolvePath(pathLike));
|
|
792
|
+
},
|
|
793
|
+
|
|
794
|
+
createWriteStream(pathLike: unknown): unknown {
|
|
795
|
+
return vfs.createWriteStream(resolvePath(pathLike));
|
|
796
|
+
},
|
|
797
|
+
|
|
798
|
+
promises,
|
|
799
|
+
constants,
|
|
800
|
+
} as FsShim;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
export default createFsShim;
|