typescript-virtual-container 1.1.7 → 1.2.1

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.
@@ -0,0 +1,40 @@
1
+ Benchmarking VirtualShell concurrency:
2
+
3
+ Running 1 shells...
4
+ Initialized 1 shells in 86ms, RSS 82 MB
5
+ Executed shell commands in 2ms, RSS now 82 MB (+0 MB)
6
+
7
+ Running 2 shells...
8
+ Initialized 2 shells in 2ms, RSS 82 MB
9
+ Executed shell commands in 1ms, RSS now 82 MB (+0 MB)
10
+
11
+ Running 5 shells...
12
+ Initialized 5 shells in 5ms, RSS 82 MB
13
+ Executed shell commands in 2ms, RSS now 82 MB (+0 MB)
14
+
15
+ Running 10 shells...
16
+ Initialized 10 shells in 5ms, RSS 84 MB
17
+ Executed shell commands in 3ms, RSS now 84 MB (+1 MB)
18
+
19
+ Running 20 shells...
20
+ Initialized 20 shells in 12ms, RSS 84 MB
21
+ Executed shell commands in 6ms, RSS now 85 MB (+1 MB)
22
+
23
+ Running 50 shells...
24
+ Initialized 50 shells in 30ms, RSS 87 MB
25
+ Executed shell commands in 12ms, RSS now 88 MB (+2 MB)
26
+
27
+ Running 100 shells...
28
+ Initialized 100 shells in 52ms, RSS 91 MB
29
+ Executed shell commands in 28ms, RSS now 92 MB (+2 MB)
30
+
31
+ Summary:
32
+
33
+ count init_ms cmd_ms init_rss final_rss
34
+ 1 86 2 82 MB 82 MB 0 MB
35
+ 2 2 1 82 MB 82 MB 0 MB
36
+ 5 5 2 82 MB 82 MB 0 MB
37
+ 10 5 3 84 MB 84 MB 1 MB
38
+ 20 12 6 84 MB 85 MB 1 MB
39
+ 50 30 12 87 MB 88 MB 2 MB
40
+ 100 52 28 91 MB 92 MB 2 MB
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { mkdirSync, rmSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { VirtualShell } from "./src/index.ts";
6
+
7
+ const counts = [1, 2, 5, 10, 20, 50, 100];
8
+ const rootBenchmarkPath = join(process.cwd(), ".benchmark-shells");
9
+
10
+ function bytesToMb(bytes: number): string {
11
+ return `${Math.round(bytes / 1024 / 1024)} MB`;
12
+ }
13
+
14
+ async function createShell(baseName: string, index: number): Promise<VirtualShell> {
15
+ const basePath = join(rootBenchmarkPath, `${baseName}-${index}`);
16
+ mkdirSync(basePath, { recursive: true });
17
+ const shell = new VirtualShell(`${baseName}-${index}`, undefined, basePath);
18
+ await shell.ensureInitialized();
19
+ return shell;
20
+ }
21
+
22
+ async function runSingleBenchmark(count: number) {
23
+ const label = `shells-${count}`;
24
+ const start = Date.now();
25
+ const shells: VirtualShell[] = await Promise.all(
26
+ Array.from({ length: count }, (_, index) => createShell(label, index)),
27
+ );
28
+ const initMs = Date.now() - start;
29
+ const initRss = process.memoryUsage().rss;
30
+
31
+ const commandStart = Date.now();
32
+ await Promise.all(
33
+ shells.map(async (shell, index) => {
34
+ const cwd = "/home/root";
35
+ shell.executeCommand(`mkdir -p /tmp/benchmark-${index}`, "root", cwd);
36
+ shell.executeCommand(
37
+ `echo "hello ${index}" > /tmp/benchmark-${index}/result.txt`,
38
+ "root",
39
+ cwd,
40
+ );
41
+ shell.executeCommand(
42
+ `cat /tmp/benchmark-${index}/result.txt`,
43
+ "root",
44
+ cwd,
45
+ );
46
+ }),
47
+ );
48
+ const commandMs = Date.now() - commandStart;
49
+ const finalRss = process.memoryUsage().rss;
50
+
51
+ return {
52
+ count,
53
+ initMs,
54
+ commandMs,
55
+ initRss,
56
+ finalRss,
57
+ deltaRss: finalRss - initRss,
58
+ };
59
+ }
60
+
61
+ async function main() {
62
+ rmSync(rootBenchmarkPath, { recursive: true, force: true });
63
+ mkdirSync(rootBenchmarkPath, { recursive: true });
64
+
65
+ console.log("Benchmarking VirtualShell concurrency:\n");
66
+ const results = [];
67
+ for (const count of counts) {
68
+ console.log(`Running ${count} shells...`);
69
+ const result = await runSingleBenchmark(count);
70
+ results.push(result);
71
+ console.log(
72
+ ` Initialized ${count} shells in ${result.initMs}ms, RSS ${bytesToMb(result.initRss)}`,
73
+ );
74
+ console.log(
75
+ ` Executed shell commands in ${result.commandMs}ms, RSS now ${bytesToMb(result.finalRss)} (+${bytesToMb(result.deltaRss)})`,
76
+ );
77
+ console.log("");
78
+ }
79
+
80
+ console.log("Summary:\n");
81
+ console.log(
82
+ "count\tinit_ms\tcmd_ms\tinit_rss\tfinal_rss\tdelta_rss",
83
+ );
84
+ for (const row of results) {
85
+ console.log(
86
+ `${row.count}\t${row.initMs}\t${row.commandMs}\t${bytesToMb(row.initRss)}\t${bytesToMb(row.finalRss)}\t${bytesToMb(row.deltaRss)}`,
87
+ );
88
+ }
89
+
90
+ console.log(`\nBenchmark data stored under ${rootBenchmarkPath}`);
91
+ }
92
+
93
+ main().catch((error) => {
94
+ console.error(error);
95
+ process.exit(1);
96
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Honeypot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,qBAAa,QAAQ;IACpB,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,KAAK,CAaX;IAEF,OAAO,CAAC,UAAU,CAAS;IAE3B;;;;OAIG;gBACS,UAAU,GAAE,MAAc;IAItC;;;;;;;;OAQG;IACI,MAAM,CACZ,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,kBAAkB,EACzB,GAAG,CAAC,EAAE,QAAQ,EACd,IAAI,CAAC,EAAE,SAAS,GACd,IAAI;IAYP;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAmB1B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAoB/B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAkChC;;OAEG;IACH,OAAO,CAAC,cAAc;IAyCtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAyCvB;;;;;;OAMG;IACH,OAAO,CAAC,GAAG;IAuBX;;;;;;OAMG;IACI,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE;IAOnE;;;;OAIG;IACI,QAAQ,IAAI,QAAQ,CAAC,aAAa,CAAC;IAI1C;;OAEG;IACI,KAAK,IAAI,IAAI;IAkBpB;;;;;OAKG;IACI,SAAS,CAAC,KAAK,GAAE,MAAY,GAAG,aAAa,EAAE;IAItD;;;;OAIG;IACI,eAAe,IAAI,KAAK,CAAC;QAC/B,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QACpC,OAAO,EAAE,MAAM,CAAC;KAChB,CAAC;CAkDF;AAED,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Honeypot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAID;;;;;GAKG;AACH,qBAAa,QAAQ;IACpB,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,KAAK,CAaX;IAEF,OAAO,CAAC,UAAU,CAAS;IAE3B;;;;OAIG;gBACS,UAAU,GAAE,MAAc;IAKtC;;;;;;;;OAQG;IACI,MAAM,CACZ,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,kBAAkB,EACzB,GAAG,CAAC,EAAE,QAAQ,EACd,IAAI,CAAC,EAAE,SAAS,GACd,IAAI;IAaP;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAmB1B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAoB/B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAkChC;;OAEG;IACH,OAAO,CAAC,cAAc;IAyCtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAyCvB;;;;;;OAMG;IACH,OAAO,CAAC,GAAG;IAuBX;;;;;;OAMG;IACI,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE;IAQnE;;;;OAIG;IACI,QAAQ,IAAI,QAAQ,CAAC,aAAa,CAAC;IAK1C;;OAEG;IACI,KAAK,IAAI,IAAI;IAmBpB;;;;;OAKG;IACI,SAAS,CAAC,KAAK,GAAE,MAAY,GAAG,aAAa,EAAE;IAKtD;;;;OAIG;IACI,eAAe,IAAI,KAAK,CAAC;QAC/B,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QACpC,OAAO,EAAE,MAAM,CAAC;KAChB,CAAC;CAmDF;AAED,eAAe,QAAQ,CAAC"}
@@ -7,6 +7,8 @@
7
7
  *
8
8
  * @module honeypot
9
9
  */
10
+ import { createPerfLogger } from "../utils/perfLogger";
11
+ const perf = createPerfLogger("HoneyPot");
10
12
  /**
11
13
  * HoneyPot audit and event tracking utility.
12
14
  *
@@ -36,6 +38,7 @@ export class HoneyPot {
36
38
  * @param maxLogSize Maximum audit log entries to retain (default: 10000).
37
39
  */
38
40
  constructor(maxLogSize = 10000) {
41
+ perf.mark("constructor");
39
42
  this.maxLogSize = maxLogSize;
40
43
  }
41
44
  /**
@@ -48,6 +51,7 @@ export class HoneyPot {
48
51
  * @param sftp SftpMimic instance (optional).
49
52
  */
50
53
  attach(shell, vfs, users, ssh, sftp) {
54
+ perf.mark("attach");
51
55
  this.attachVirtualShell(shell);
52
56
  this.attachVirtualFileSystem(vfs);
53
57
  this.attachVirtualUserManager(users);
@@ -204,6 +208,7 @@ export class HoneyPot {
204
208
  * @returns Filtered audit log entries.
205
209
  */
206
210
  getAuditLog(type, source) {
211
+ perf.mark("getAuditLog");
207
212
  return this.auditLog.filter((entry) => (!type || entry.type === type) && (!source || entry.source === source));
208
213
  }
209
214
  /**
@@ -212,12 +217,14 @@ export class HoneyPot {
212
217
  * @returns Snapshot of honeypot stats.
213
218
  */
214
219
  getStats() {
220
+ perf.mark("getStats");
215
221
  return Object.freeze({ ...this.stats });
216
222
  }
217
223
  /**
218
224
  * Clears audit log and resets statistics.
219
225
  */
220
226
  reset() {
227
+ perf.mark("reset");
221
228
  this.auditLog = [];
222
229
  this.stats = {
223
230
  authAttempts: 0,
@@ -241,6 +248,7 @@ export class HoneyPot {
241
248
  * @returns Recent audit log entries.
242
249
  */
243
250
  getRecent(limit = 100) {
251
+ perf.mark("getRecent");
244
252
  return this.auditLog.slice(Math.max(0, this.auditLog.length - limit));
245
253
  }
246
254
  /**
@@ -249,6 +257,7 @@ export class HoneyPot {
249
257
  * @returns Array of anomalies detected.
250
258
  */
251
259
  detectAnomalies() {
260
+ perf.mark("detectAnomalies");
252
261
  const anomalies = [];
253
262
  // High auth failure rate
254
263
  if (this.stats.authAttempts > 0 &&
@@ -1,19 +1,5 @@
1
1
  import type { CommandResult } from "../types/commands";
2
2
  import type { VirtualShell } from "../VirtualShell";
3
- /**
4
- * Programmatic client for executing shell commands against a virtual shell.
5
- *
6
- * Maintains working-directory state across invocations and runs commands as a
7
- * single authenticated user without SSH transport overhead.
8
- *
9
- * @example
10
- * ```ts
11
- * const shell = new VirtualShell("typescript-vm");
12
- * const client = new SshClient(shell, "alice");
13
- * const result = await client.cd("/tmp");
14
- * const list = await client.ls();
15
- * ```
16
- */
17
3
  export declare class SshClient {
18
4
  private shell;
19
5
  private username;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHClient/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,qBAAa,SAAS;IAUpB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAVjB,OAAO,CAAC,UAAU,CAAO;IAEzB;;;;;OAKG;gBAEM,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM;IAGzB;;;;;OAKG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA0BnD;;;;;OAKG;IACG,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAK/C;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;IAInC;;;;;OAKG;IACG,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAQ9C;;;;;OAKG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAI/C;;;;;;OAMG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAKpE;;;;;OAKG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAIjD;;;;;;OAMG;IACG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAKjE;;;;;;OAMG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAiBtE;;;;;OAKG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAiBpD;;;;OAIG;IACH,MAAM,IAAI,MAAM;IAIhB;;;;OAIG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;;OAKG;IACG,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAKjD;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC;IAItC;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC;IAIxC;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;CAGnC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHClient/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAkBpD,qBAAa,SAAS;IAUpB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAVjB,OAAO,CAAC,UAAU,CAAO;IAEzB;;;;;OAKG;gBAEM,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM;IAKzB;;;;;OAKG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA2BnD;;;;;OAKG;IACG,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAM/C;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;IAKnC;;;;;OAKG;IACG,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAS9C;;;;;OAKG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAK/C;;;;;;OAMG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAMpE;;;;;OAKG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAKjD;;;;;;OAMG;IACG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAMjE;;;;;;OAMG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAkBtE;;;;;OAKG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAkBpD;;;;OAIG;IACH,MAAM,IAAI,MAAM;IAKhB;;;;OAIG;IACH,WAAW,IAAI,MAAM;IAKrB;;;;;OAKG;IACG,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAMjD;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC;IAKtC;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC;IAKxC;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;CAInC"}
@@ -1,4 +1,5 @@
1
1
  import { runCommand } from "../commands";
2
+ import { createPerfLogger } from "../utils/perfLogger";
2
3
  /**
3
4
  * Programmatic client for executing shell commands against a virtual shell.
4
5
  *
@@ -13,6 +14,7 @@ import { runCommand } from "../commands";
13
14
  * const list = await client.ls();
14
15
  * ```
15
16
  */
17
+ const perf = createPerfLogger("SshClient");
16
18
  export class SshClient {
17
19
  shell;
18
20
  username;
@@ -26,6 +28,7 @@ export class SshClient {
26
28
  constructor(shell, username) {
27
29
  this.shell = shell;
28
30
  this.username = username;
31
+ perf.mark("constructor");
29
32
  }
30
33
  /**
31
34
  * Executes raw shell command.
@@ -34,6 +37,7 @@ export class SshClient {
34
37
  * @returns Command result with stdout/stderr/exitCode.
35
38
  */
36
39
  async exec(command) {
40
+ perf.mark("exec");
37
41
  const vfs = this.shell.getVfs();
38
42
  const users = this.shell.getUsers();
39
43
  const hostname = this.shell.getHostname();
@@ -54,6 +58,7 @@ export class SshClient {
54
58
  * @returns Result with directory listing in stdout.
55
59
  */
56
60
  async ls(path) {
61
+ perf.mark("ls");
57
62
  const target = path ?? ".";
58
63
  return this.exec(`ls ${target}`);
59
64
  }
@@ -63,6 +68,7 @@ export class SshClient {
63
68
  * @returns Result with cwd path in stdout.
64
69
  */
65
70
  async pwd() {
71
+ perf.mark("pwd");
66
72
  return this.exec("pwd");
67
73
  }
68
74
  /**
@@ -72,6 +78,7 @@ export class SshClient {
72
78
  * @returns Result; updates internal cwd on success.
73
79
  */
74
80
  async cd(path) {
81
+ perf.mark("cd");
75
82
  const result = await this.exec(`cd ${path}`);
76
83
  if (result.nextCwd && result.exitCode !== 1) {
77
84
  this.currentCwd = result.nextCwd;
@@ -85,6 +92,7 @@ export class SshClient {
85
92
  * @returns Result with file content in stdout.
86
93
  */
87
94
  async cat(path) {
95
+ perf.mark("cat");
88
96
  return this.exec(`cat ${path}`);
89
97
  }
90
98
  /**
@@ -95,6 +103,7 @@ export class SshClient {
95
103
  * @returns Result from mkdir command.
96
104
  */
97
105
  async mkdir(path, recursive = false) {
106
+ perf.mark("mkdir");
98
107
  const flag = recursive ? "-p " : "";
99
108
  return this.exec(`mkdir ${flag}${path}`);
100
109
  }
@@ -105,6 +114,7 @@ export class SshClient {
105
114
  * @returns Result from touch command.
106
115
  */
107
116
  async touch(path) {
117
+ perf.mark("touch");
108
118
  return this.exec(`touch ${path}`);
109
119
  }
110
120
  /**
@@ -115,6 +125,7 @@ export class SshClient {
115
125
  * @returns Result from rm command.
116
126
  */
117
127
  async rm(path, recursive = false) {
128
+ perf.mark("rm");
118
129
  const flag = recursive ? "-r " : "";
119
130
  return this.exec(`rm ${flag}${path}`);
120
131
  }
@@ -126,6 +137,7 @@ export class SshClient {
126
137
  * @returns Result from touch/write simulation.
127
138
  */
128
139
  async writeFile(path, content) {
140
+ perf.mark("writeFile");
129
141
  const vfs = this.shell.getVfs();
130
142
  if (!vfs) {
131
143
  throw new Error("SSH client not started");
@@ -148,6 +160,7 @@ export class SshClient {
148
160
  * @returns File content as string or error in result.
149
161
  */
150
162
  async readFile(path) {
163
+ perf.mark("readFile");
151
164
  const vfs = this.shell.getVfs();
152
165
  if (!vfs) {
153
166
  throw new Error("SSH client not started");
@@ -169,6 +182,7 @@ export class SshClient {
169
182
  * @returns Normalized cwd path.
170
183
  */
171
184
  getCwd() {
185
+ perf.mark("getCwd");
172
186
  return this.currentCwd;
173
187
  }
174
188
  /**
@@ -177,6 +191,7 @@ export class SshClient {
177
191
  * @returns Associated username.
178
192
  */
179
193
  getUsername() {
194
+ perf.mark("getUsername");
180
195
  return this.username;
181
196
  }
182
197
  /**
@@ -186,6 +201,7 @@ export class SshClient {
186
201
  * @returns Result with ASCII tree in stdout.
187
202
  */
188
203
  async tree(path) {
204
+ perf.mark("tree");
189
205
  const target = path ?? ".";
190
206
  return this.exec(`tree ${target}`);
191
207
  }
@@ -195,6 +211,7 @@ export class SshClient {
195
211
  * @returns Result from whoami command.
196
212
  */
197
213
  async whoami() {
214
+ perf.mark("whoami");
198
215
  return this.exec("whoami");
199
216
  }
200
217
  /**
@@ -203,6 +220,7 @@ export class SshClient {
203
220
  * @returns Result from hostname command.
204
221
  */
205
222
  async hostname() {
223
+ perf.mark("hostname");
206
224
  return this.exec("hostname");
207
225
  }
208
226
  /**
@@ -211,6 +229,7 @@ export class SshClient {
211
229
  * @returns Result from who command.
212
230
  */
213
231
  async who() {
232
+ perf.mark("who");
214
233
  return this.exec("who");
215
234
  }
216
235
  }
@@ -1,13 +1,6 @@
1
1
  import { EventEmitter } from "node:events";
2
2
  import { Server as SshServer } from "ssh2";
3
3
  import { VirtualShell } from "../VirtualShell";
4
- /**
5
- * SSH server facade that wires the virtual shell runtime into ssh2 sessions.
6
- *
7
- * This class is exported as `VirtualSshServer` for public API compatibility.
8
- * Create an instance, call {@link SshMimic.start}, and stop it with
9
- * {@link SshMimic.stop} when your process exits.
10
- */
11
4
  declare class SshMimic extends EventEmitter {
12
5
  port: number;
13
6
  server: SshServer | null;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAI/C;;;;;;GAMG;AACH,cAAM,QAAS,SAAQ,YAAY;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,aAAa,CAAS;IAE9B;;;;;;OAMG;gBACS,EACX,IAAI,EACJ,QAA0B,EAC1B,KAAkC,GAClC,EAAE;QACF,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,YAAY,CAAC;KACrB;IAQD;;;;OAIG;IACU,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAwHrC;;OAEG;IACI,IAAI,IAAI,IAAI;CAQnB;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAc/C,cAAM,QAAS,SAAQ,YAAY;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,aAAa,CAAS;IAE9B;;;;;;OAMG;gBACS,EACX,IAAI,EACJ,QAA0B,EAC1B,KAAkC,GAClC,EAAE;QACF,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,YAAY,CAAC;KACrB;IASD;;;;OAIG;IACU,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAyHrC;;OAEG;IACI,IAAI,IAAI,IAAI;CASnB;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { EventEmitter } from "node:events";
2
2
  import { Server as SshServer } from "ssh2";
3
3
  import { VirtualShell } from "../VirtualShell";
4
+ import { createPerfLogger } from "../utils/perfLogger";
4
5
  import { runExec } from "./exec";
5
6
  import { loadOrCreateHostKey } from "./hostKey";
6
7
  /**
@@ -10,6 +11,7 @@ import { loadOrCreateHostKey } from "./hostKey";
10
11
  * Create an instance, call {@link SshMimic.start}, and stop it with
11
12
  * {@link SshMimic.stop} when your process exits.
12
13
  */
14
+ const perf = createPerfLogger("SshMimic");
13
15
  class SshMimic extends EventEmitter {
14
16
  port;
15
17
  server;
@@ -24,6 +26,7 @@ class SshMimic extends EventEmitter {
24
26
  */
25
27
  constructor({ port, hostname = "typescript-vm", shell = new VirtualShell(hostname), }) {
26
28
  super();
29
+ perf.mark("constructor");
27
30
  this.port = port;
28
31
  this.shellHostname = hostname;
29
32
  this.server = null;
@@ -35,6 +38,7 @@ class SshMimic extends EventEmitter {
35
38
  * @returns Promise resolved with bound listening port.
36
39
  */
37
40
  async start() {
41
+ perf.mark("start");
38
42
  const shell = this.shell;
39
43
  const privateKey = loadOrCreateHostKey();
40
44
  // Ensure VirtualShell is fully initialized before accepting connections
@@ -118,6 +122,7 @@ class SshMimic extends EventEmitter {
118
122
  * Stops server if running.
119
123
  */
120
124
  stop() {
125
+ perf.mark("stop");
121
126
  if (this.server) {
122
127
  this.server.close(() => {
123
128
  console.log("SSH Mimic stopped");
@@ -1 +1 @@
1
- {"version":3,"file":"sftp.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/sftp.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AA2HhE,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC3B;AAED,qBAAa,SAAU,SAAQ,YAAY;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,OAAO,CAAiC;gBAEpC,EACX,IAAI,EACJ,QAA0B,EAC1B,KAAK,EACL,GAAG,EACH,KAAK,GACL,EAAE,gBAAgB;IAuBnB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,QAAQ;IAIH,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAwJ9B,IAAI,IAAI,IAAI;IASnB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAiB1B;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,kBAAkB;CA6a1B"}
1
+ {"version":3,"file":"sftp.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/sftp.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAI3C,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AA4HhE,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC3B;AAED,qBAAa,SAAU,SAAQ,YAAY;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,OAAO,CAAiC;gBAEpC,EACX,IAAI,EACJ,QAA0B,EAC1B,KAAK,EACL,GAAG,EACH,KAAK,GACL,EAAE,gBAAgB;IAwBnB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,QAAQ;IAIH,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAyJ9B,IAAI,IAAI,IAAI;IAUnB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAiB1B;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,kBAAkB;CA6a1B"}
@@ -2,6 +2,7 @@
2
2
  import { EventEmitter } from "node:events";
3
3
  import * as path from "node:path";
4
4
  import { Server as SshServer } from "ssh2";
5
+ import { createPerfLogger } from "../utils/perfLogger";
5
6
  import { VirtualShell } from "../VirtualShell";
6
7
  import { loadOrCreateHostKey } from "./hostKey";
7
8
  const SFTP_STATUS_CODE = {
@@ -23,6 +24,7 @@ const OPEN_MODE = {
23
24
  TRUNC: 0x00000010,
24
25
  EXCL: 0x00000020,
25
26
  };
27
+ const perf = createPerfLogger("SftpMimic");
26
28
  export class SftpMimic extends EventEmitter {
27
29
  port;
28
30
  server;
@@ -34,6 +36,7 @@ export class SftpMimic extends EventEmitter {
34
36
  handles = new Map();
35
37
  constructor({ port, hostname = "typescript-vm", shell, vfs, users, }) {
36
38
  super();
39
+ perf.mark("constructor");
37
40
  this.port = port;
38
41
  this.server = null;
39
42
  this.hostname = hostname;
@@ -62,6 +65,7 @@ export class SftpMimic extends EventEmitter {
62
65
  return this.shell?.users ?? this.users;
63
66
  }
64
67
  async start() {
68
+ perf.mark("start");
65
69
  const privateKey = loadOrCreateHostKey();
66
70
  // Ensure VirtualShell is fully initialized before accepting connections
67
71
  if (this.shell) {
@@ -175,6 +179,7 @@ export class SftpMimic extends EventEmitter {
175
179
  });
176
180
  }
177
181
  stop() {
182
+ perf.mark("stop");
178
183
  if (this.server) {
179
184
  this.server.close(() => {
180
185
  console.log("SFTP Mimic stopped");
@@ -1,12 +1,5 @@
1
1
  import { EventEmitter } from "node:events";
2
2
  import type { RemoveOptions, VfsNodeStats, WriteFileOptions } from "../types/vfs";
3
- /**
4
- * In-memory virtual filesystem with tar.gz mirror persistence.
5
- *
6
- * Paths are normalized to POSIX-like absolute paths. Use
7
- * {@link VirtualFileSystem.restoreMirror} on startup and
8
- * {@link VirtualFileSystem.flushMirror} to persist pending changes.
9
- */
10
3
  declare class VirtualFileSystem extends EventEmitter {
11
4
  private readonly mirrorRoot;
12
5
  private ensureMirrorRoot;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualFileSystem/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,OAAO,KAAK,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,MAAM,cAAc,CAAC;AAGtB;;;;;;GAMG;AACH,cAAM,iBAAkB,SAAQ,YAAY;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,eAAe;IA4BvB;;;;OAIG;gBACS,OAAO,GAAE,MAAsB;IAK3C;;;;OAIG;IACU,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3C;;;;OAIG;IACU,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAKzC;;;;;OAKG;IACI,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,MAAc,GAAG,IAAI;IAY5D;;;;;;;;OAQG;IACI,SAAS,CACf,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,OAAO,GAAE,gBAAqB,GAC5B,IAAI;IAwBP;;;;;;;OAOG;IACI,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAc3C;;;;;OAKG;IACI,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAS1C;;;;;OAKG;IACI,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQpD;;;;;OAKG;IACI,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY;IAqC7C;;;;;OAKG;IACI,IAAI,CAAC,OAAO,GAAE,MAAY,GAAG,MAAM,EAAE;IAS5C;;;;;OAKG;IACI,IAAI,CAAC,OAAO,GAAE,MAAY,GAAG,MAAM;IAW1C;;;;;;;;OAQG;IACI,aAAa,CAAC,UAAU,GAAE,MAAY,GAAG,MAAM;IAQtD;;;;OAIG;IACI,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAY7C;;;;OAIG;IACI,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAY/C;;;;;OAKG;IACI,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,IAAI;IA4BpE;;;;;OAKG;IACI,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAsBnD;AAED,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualFileSystem/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,OAAO,KAAK,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,MAAM,cAAc,CAAC;AActB,cAAM,iBAAkB,SAAQ,YAAY;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,eAAe;IA4BvB;;;;OAIG;gBACS,OAAO,GAAE,MAAsB;IAM3C;;;;OAIG;IACU,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3C;;;;OAIG;IACU,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAMzC;;;;;OAKG;IACI,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,MAAc,GAAG,IAAI;IAa5D;;;;;;;;OAQG;IACI,SAAS,CACf,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,OAAO,GAAE,gBAAqB,GAC5B,IAAI;IAyBP;;;;;;;OAOG;IACI,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAe3C;;;;;OAKG;IACI,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAU1C;;;;;OAKG;IACI,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IASpD;;;;;OAKG;IACI,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY;IAsC7C;;;;;OAKG;IACI,IAAI,CAAC,OAAO,GAAE,MAAY,GAAG,MAAM,EAAE;IAU5C;;;;;OAKG;IACI,IAAI,CAAC,OAAO,GAAE,MAAY,GAAG,MAAM;IAY1C;;;;;;;;OAQG;IACI,aAAa,CAAC,UAAU,GAAE,MAAY,GAAG,MAAM;IAStD;;;;OAIG;IACI,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAa7C;;;;OAIG;IACI,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAa/C;;;;;OAKG;IACI,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,IAAI;IA6BpE;;;;;OAKG;IACI,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAuBnD;AAED,eAAe,iBAAiB,CAAC"}
@@ -2,6 +2,7 @@ import { EventEmitter } from "node:events";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import { gunzipSync, gzipSync } from "node:zlib";
5
+ import { createPerfLogger } from "../utils/perfLogger";
5
6
  import { normalizePath } from "./path";
6
7
  /**
7
8
  * In-memory virtual filesystem with tar.gz mirror persistence.
@@ -10,6 +11,7 @@ import { normalizePath } from "./path";
10
11
  * {@link VirtualFileSystem.restoreMirror} on startup and
11
12
  * {@link VirtualFileSystem.flushMirror} to persist pending changes.
12
13
  */
14
+ const perf = createPerfLogger("VirtualFileSystem");
13
15
  class VirtualFileSystem extends EventEmitter {
14
16
  mirrorRoot;
15
17
  ensureMirrorRoot() {
@@ -77,6 +79,7 @@ class VirtualFileSystem extends EventEmitter {
77
79
  */
78
80
  constructor(baseDir = process.cwd()) {
79
81
  super();
82
+ perf.mark("constructor");
80
83
  this.mirrorRoot = path.resolve(baseDir, ".vfs", "mirror");
81
84
  }
82
85
  /**
@@ -85,6 +88,7 @@ class VirtualFileSystem extends EventEmitter {
85
88
  * If archive does not exist or cannot be read, creates fresh mirror file.
86
89
  */
87
90
  async restoreMirror() {
91
+ perf.mark("restoreMirror");
88
92
  this.ensureMirrorRoot();
89
93
  }
90
94
  /**
@@ -93,6 +97,7 @@ class VirtualFileSystem extends EventEmitter {
93
97
  * No-op when nothing changed and archive already exists.
94
98
  */
95
99
  async flushMirror() {
100
+ perf.mark("flushMirror");
96
101
  this.ensureMirrorRoot();
97
102
  this.emit("mirror:flush");
98
103
  }
@@ -103,6 +108,7 @@ class VirtualFileSystem extends EventEmitter {
103
108
  * @param mode POSIX-like mode bits for new directories.
104
109
  */
105
110
  mkdir(targetPath, mode = 0o755) {
111
+ perf.mark("mkdir");
106
112
  this.ensureMirrorRoot();
107
113
  const fsPath = this.resolveFsPath(targetPath);
108
114
  if (fs.existsSync(fsPath) && !fs.statSync(fsPath).isDirectory()) {
@@ -121,6 +127,7 @@ class VirtualFileSystem extends EventEmitter {
121
127
  * @param options Optional write behavior (mode, compression).
122
128
  */
123
129
  writeFile(targetPath, content, options = {}) {
130
+ perf.mark("writeFile");
124
131
  this.ensureMirrorRoot();
125
132
  const normalized = normalizePath(targetPath);
126
133
  const fsPath = this.resolveFsPath(normalized);
@@ -147,6 +154,7 @@ class VirtualFileSystem extends EventEmitter {
147
154
  * @returns UTF-8 string content.
148
155
  */
149
156
  readFile(targetPath) {
157
+ perf.mark("readFile");
150
158
  this.ensureMirrorRoot();
151
159
  const fsPath = this.resolveFsPath(targetPath);
152
160
  if (!fs.existsSync(fsPath) || !fs.statSync(fsPath).isFile()) {
@@ -165,6 +173,7 @@ class VirtualFileSystem extends EventEmitter {
165
173
  * @returns True when file or directory exists.
166
174
  */
167
175
  exists(targetPath) {
176
+ perf.mark("exists");
168
177
  try {
169
178
  const fsPath = this.resolveFsPath(targetPath);
170
179
  return fs.existsSync(fsPath);
@@ -180,6 +189,7 @@ class VirtualFileSystem extends EventEmitter {
180
189
  * @param mode New POSIX-like mode.
181
190
  */
182
191
  chmod(targetPath, mode) {
192
+ perf.mark("chmod");
183
193
  const fsPath = this.resolveFsPath(targetPath);
184
194
  if (!fs.existsSync(fsPath)) {
185
195
  throw new Error(`Path '${normalizePath(targetPath)}' does not exist.`);
@@ -193,6 +203,7 @@ class VirtualFileSystem extends EventEmitter {
193
203
  * @returns Typed stat object based on node type.
194
204
  */
195
205
  stat(targetPath) {
206
+ perf.mark("stat");
196
207
  this.ensureMirrorRoot();
197
208
  const normalized = normalizePath(targetPath);
198
209
  const fsPath = this.resolveFsPath(normalized);
@@ -231,6 +242,7 @@ class VirtualFileSystem extends EventEmitter {
231
242
  * @returns Sorted child names.
232
243
  */
233
244
  list(dirPath = "/") {
245
+ perf.mark("list");
234
246
  const fsPath = this.resolveFsPath(dirPath);
235
247
  if (!fs.existsSync(fsPath) || !fs.statSync(fsPath).isDirectory()) {
236
248
  throw new Error(`Cannot list '${dirPath}': not a directory.`);
@@ -244,6 +256,7 @@ class VirtualFileSystem extends EventEmitter {
244
256
  * @returns Multi-line tree string.
245
257
  */
246
258
  tree(dirPath = "/") {
259
+ perf.mark("tree");
247
260
  const fsPath = this.resolveFsPath(dirPath);
248
261
  if (!fs.existsSync(fsPath) || !fs.statSync(fsPath).isDirectory()) {
249
262
  throw new Error(`Cannot render tree for '${dirPath}': not a directory.`);
@@ -261,6 +274,7 @@ class VirtualFileSystem extends EventEmitter {
261
274
  * @returns Total byte usage for file content under target path.
262
275
  */
263
276
  getUsageBytes(targetPath = "/") {
277
+ perf.mark("getUsageBytes");
264
278
  const fsPath = this.resolveFsPath(targetPath);
265
279
  if (!fs.existsSync(fsPath)) {
266
280
  throw new Error(`Path '${normalizePath(targetPath)}' does not exist.`);
@@ -273,6 +287,7 @@ class VirtualFileSystem extends EventEmitter {
273
287
  * @param targetPath Path to file.
274
288
  */
275
289
  compressFile(targetPath) {
290
+ perf.mark("compressFile");
276
291
  const fsPath = this.resolveFsPath(targetPath);
277
292
  if (!fs.existsSync(fsPath) || !fs.statSync(fsPath).isFile()) {
278
293
  throw new Error(`Cannot compress '${targetPath}': not a file.`);
@@ -288,6 +303,7 @@ class VirtualFileSystem extends EventEmitter {
288
303
  * @param targetPath Path to file.
289
304
  */
290
305
  decompressFile(targetPath) {
306
+ perf.mark("decompressFile");
291
307
  const fsPath = this.resolveFsPath(targetPath);
292
308
  if (!fs.existsSync(fsPath) || !fs.statSync(fsPath).isFile()) {
293
309
  throw new Error(`Cannot decompress '${targetPath}': not a file.`);
@@ -304,6 +320,7 @@ class VirtualFileSystem extends EventEmitter {
304
320
  * @param options Removal options, including recursive delete.
305
321
  */
306
322
  remove(targetPath, options = {}) {
323
+ perf.mark("remove");
307
324
  const normalized = normalizePath(targetPath);
308
325
  if (normalized === "/") {
309
326
  throw new Error("Cannot remove root directory.");
@@ -333,6 +350,7 @@ class VirtualFileSystem extends EventEmitter {
333
350
  * @param toPath Destination path.
334
351
  */
335
352
  move(fromPath, toPath) {
353
+ perf.mark("move");
336
354
  const fromNormalized = normalizePath(fromPath);
337
355
  const toNormalized = normalizePath(toPath);
338
356
  if (fromNormalized === "/" || toNormalized === "/") {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualShell/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,iBAAiB,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACb;AA8BD;;;;;GAKG;AACH,cAAM,YAAa,SAAQ,YAAY;IACtC,QAAQ,EAAE,MAAM,CAAO;IACvB,GAAG,EAAE,iBAAiB,CAAC;IACvB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,WAAW,CAAgB;IAEnC;;;;;;OAMG;gBAEF,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,eAAe,EAC5B,QAAQ,CAAC,EAAE,MAAM;IAyBlB;;;OAGG;IACU,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/C;;;;;;OAMG;IACH,UAAU,CACT,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,QAAQ,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GACvE,IAAI;IASP;;;;;;OAMG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAKrE;;;;;;;OAOG;IAEH,uBAAuB,CACtB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAC1C,IAAI;IAeP;;;;OAIG;IACI,MAAM,IAAI,iBAAiB,GAAG,IAAI;IAIzC;;;;OAIG;IACI,QAAQ,IAAI,kBAAkB,GAAG,IAAI;IAI5C;;;;OAIG;IACI,WAAW,IAAI,MAAM;IAI5B;;;;;;OAMG;IACI,eAAe,CACrB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,GACtB,IAAI;CAIP;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualShell/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,iBAAiB,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACb;AAwCD;;;;;GAKG;AACH,cAAM,YAAa,SAAQ,YAAY;IACtC,QAAQ,EAAE,MAAM,CAAO;IACvB,GAAG,EAAE,iBAAiB,CAAC;IACvB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,WAAW,CAAgB;IAEnC;;;;;;OAMG;gBAEF,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,eAAe,EAC5B,QAAQ,CAAC,EAAE,MAAM;IA0BlB;;;OAGG;IACU,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/C;;;;;;OAMG;IACH,UAAU,CACT,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,QAAQ,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GACvE,IAAI;IASP;;;;;;OAMG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAMrE;;;;;;;OAOG;IAEH,uBAAuB,CACtB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAC1C,IAAI;IAgBP;;;;OAIG;IACI,MAAM,IAAI,iBAAiB,GAAG,IAAI;IAIzC;;;;OAIG;IACI,QAAQ,IAAI,kBAAkB,GAAG,IAAI;IAI5C;;;;OAIG;IACI,WAAW,IAAI,MAAM;IAI5B;;;;;;OAMG;IACI,eAAe,CACrB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,GACtB,IAAI;CAKP;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}