typescript-virtual-container 1.4.6 → 1.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.vscode/settings.json +1 -0
  2. package/README.md +0 -1
  3. package/benchmark-results.txt +21 -21
  4. package/builds/self-standalone.js +1111 -299
  5. package/builds/self-standalone.js.map +4 -4
  6. package/builds/standalone-wo-sftp.js +993 -183
  7. package/builds/standalone-wo-sftp.js.map +4 -4
  8. package/builds/standalone.cjs +984 -173
  9. package/builds/standalone.cjs.map +4 -4
  10. package/dist/SSHMimic/exec.d.ts.map +1 -1
  11. package/dist/SSHMimic/exec.js +0 -1
  12. package/dist/SSHMimic/exec.js.map +1 -1
  13. package/dist/SSHMimic/index.d.ts.map +1 -1
  14. package/dist/SSHMimic/index.js +3 -1
  15. package/dist/SSHMimic/index.js.map +1 -1
  16. package/dist/SSHMimic/sftp.d.ts.map +1 -1
  17. package/dist/SSHMimic/sftp.js +0 -6
  18. package/dist/SSHMimic/sftp.js.map +1 -1
  19. package/dist/VirtualFileSystem/binaryPack.d.ts.map +1 -1
  20. package/dist/VirtualFileSystem/binaryPack.js +21 -9
  21. package/dist/VirtualFileSystem/binaryPack.js.map +1 -1
  22. package/dist/VirtualFileSystem/index.d.ts +93 -0
  23. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  24. package/dist/VirtualFileSystem/index.js +361 -46
  25. package/dist/VirtualFileSystem/index.js.map +1 -1
  26. package/dist/VirtualFileSystem/internalTypes.d.ts +23 -4
  27. package/dist/VirtualFileSystem/internalTypes.d.ts.map +1 -1
  28. package/dist/VirtualFileSystem/journal.d.ts +47 -0
  29. package/dist/VirtualFileSystem/journal.d.ts.map +1 -0
  30. package/dist/VirtualFileSystem/journal.js +178 -0
  31. package/dist/VirtualFileSystem/journal.js.map +1 -0
  32. package/dist/VirtualFileSystem/path.js +1 -1
  33. package/dist/VirtualFileSystem/path.js.map +1 -1
  34. package/dist/VirtualShell/idleManager.d.ts +65 -0
  35. package/dist/VirtualShell/idleManager.d.ts.map +1 -0
  36. package/dist/VirtualShell/idleManager.js +106 -0
  37. package/dist/VirtualShell/idleManager.js.map +1 -0
  38. package/dist/VirtualShell/index.d.ts +28 -0
  39. package/dist/VirtualShell/index.d.ts.map +1 -1
  40. package/dist/VirtualShell/index.js +48 -0
  41. package/dist/VirtualShell/index.js.map +1 -1
  42. package/dist/VirtualShell/shell.js +4 -4
  43. package/dist/VirtualShell/shell.js.map +1 -1
  44. package/dist/commands/man.d.ts.map +1 -1
  45. package/dist/commands/man.js +5 -27
  46. package/dist/commands/man.js.map +1 -1
  47. package/dist/commands/manuals-bundle.d.ts +11 -0
  48. package/dist/commands/manuals-bundle.d.ts.map +1 -0
  49. package/dist/commands/manuals-bundle.js +898 -0
  50. package/dist/commands/manuals-bundle.js.map +1 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +1 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/modules/linuxRootfs.d.ts +8 -1
  56. package/dist/modules/linuxRootfs.d.ts.map +1 -1
  57. package/dist/modules/linuxRootfs.js +47 -14
  58. package/dist/modules/linuxRootfs.js.map +1 -1
  59. package/dist/self-standalone.js +16 -1
  60. package/dist/self-standalone.js.map +1 -1
  61. package/dist/standalone.js +22 -0
  62. package/dist/standalone.js.map +1 -1
  63. package/docs/assets/hierarchy.js +1 -1
  64. package/docs/assets/navigation.js +1 -1
  65. package/docs/assets/search.js +1 -1
  66. package/docs/classes/HoneyPot.html +8 -8
  67. package/docs/classes/IdleManager.html +159 -0
  68. package/docs/classes/SshClient.html +18 -18
  69. package/docs/classes/VirtualFileSystem.html +57 -32
  70. package/docs/classes/VirtualPackageManager.html +12 -12
  71. package/docs/classes/VirtualSftpServer.html +3 -3
  72. package/docs/classes/VirtualShell.html +40 -25
  73. package/docs/classes/VirtualSshServer.html +5 -5
  74. package/docs/classes/VirtualUserManager.html +26 -26
  75. package/docs/functions/assertDiff.html +1 -1
  76. package/docs/functions/diffSnapshots.html +1 -1
  77. package/docs/functions/formatDiff.html +1 -1
  78. package/docs/functions/getArg.html +1 -1
  79. package/docs/functions/getFlag.html +1 -1
  80. package/docs/functions/ifFlag.html +1 -1
  81. package/docs/hierarchy.html +1 -1
  82. package/docs/index.html +1 -2
  83. package/docs/interfaces/AuditLogEntry.html +2 -2
  84. package/docs/interfaces/CommandContext.html +11 -11
  85. package/docs/interfaces/CommandResult.html +12 -12
  86. package/docs/interfaces/ExecStream.html +5 -5
  87. package/docs/interfaces/HoneyPotStats.html +2 -2
  88. package/docs/interfaces/IdleManagerOptions.html +7 -0
  89. package/docs/interfaces/InstalledPackage.html +10 -10
  90. package/docs/interfaces/NanoEditorSession.html +4 -4
  91. package/docs/interfaces/PackageDefinition.html +13 -13
  92. package/docs/interfaces/PackageFile.html +4 -4
  93. package/docs/interfaces/RemoveOptions.html +2 -2
  94. package/docs/interfaces/ShellEnv.html +3 -3
  95. package/docs/interfaces/ShellModule.html +7 -7
  96. package/docs/interfaces/ShellProperties.html +4 -4
  97. package/docs/interfaces/ShellStream.html +6 -6
  98. package/docs/interfaces/SudoChallenge.html +8 -8
  99. package/docs/interfaces/VfsBaseNode.html +6 -6
  100. package/docs/interfaces/VfsDiff.html +5 -5
  101. package/docs/interfaces/VfsDiffEntry.html +3 -3
  102. package/docs/interfaces/VfsDiffModified.html +5 -5
  103. package/docs/interfaces/VfsDirectoryNode.html +7 -7
  104. package/docs/interfaces/VfsFileNode.html +8 -8
  105. package/docs/interfaces/VfsOptions.html +18 -4
  106. package/docs/interfaces/VfsSnapshot.html +2 -2
  107. package/docs/interfaces/VfsSnapshotBaseNode.html +3 -3
  108. package/docs/interfaces/VfsSnapshotDirectoryNode.html +4 -4
  109. package/docs/interfaces/VfsSnapshotFileNode.html +5 -5
  110. package/docs/interfaces/WriteFileOptions.html +3 -3
  111. package/docs/modules.html +1 -1
  112. package/docs/types/CommandMode.html +1 -1
  113. package/docs/types/CommandOutcome.html +1 -1
  114. package/docs/types/IdleState.html +1 -0
  115. package/docs/types/VfsNodeStats.html +1 -1
  116. package/docs/types/VfsNodeType.html +1 -1
  117. package/docs/types/VfsPersistenceMode.html +1 -1
  118. package/docs/types/VfsSnapshotNode.html +1 -1
  119. package/package.json +5 -4
  120. package/scripts/generate-manuals-bundle.mjs +49 -0
  121. package/src/SSHMimic/exec.ts +0 -1
  122. package/src/SSHMimic/index.ts +3 -1
  123. package/src/SSHMimic/sftp.ts +0 -6
  124. package/src/VirtualFileSystem/binaryPack.ts +21 -9
  125. package/src/VirtualFileSystem/index.ts +369 -52
  126. package/src/VirtualFileSystem/internalTypes.ts +24 -4
  127. package/src/VirtualFileSystem/journal.ts +163 -0
  128. package/src/VirtualFileSystem/path.ts +1 -1
  129. package/src/VirtualShell/idleManager.ts +133 -0
  130. package/src/VirtualShell/index.ts +48 -0
  131. package/src/VirtualShell/shell.ts +4 -4
  132. package/src/commands/man.ts +5 -35
  133. package/src/commands/manuals-bundle.ts +898 -0
  134. package/src/index.ts +2 -0
  135. package/src/modules/linuxRootfs.ts +58 -14
  136. package/src/self-standalone.ts +14 -1
  137. package/src/standalone.ts +23 -0
  138. package/builds/standalone.js +0 -491
  139. package/builds/standalone.js.map +0 -7
package/src/index.ts CHANGED
@@ -5,6 +5,8 @@ export { default as VirtualFileSystem } from "./VirtualFileSystem/index";
5
5
  export { VirtualPackageManager } from "./VirtualPackageManager/index";
6
6
  export { VirtualShell } from "./VirtualShell/index";
7
7
  export { VirtualUserManager } from "./VirtualUserManager/index";
8
+ export { IdleManager } from "./VirtualShell/idleManager";
9
+ export type { IdleManagerOptions, IdleState } from "./VirtualShell/idleManager";
8
10
 
9
11
  export type {
10
12
  AuditLogEntry,
@@ -15,10 +15,12 @@
15
15
  */
16
16
 
17
17
  import * as os from "node:os";
18
- import type VirtualFileSystem from "../VirtualFileSystem";
18
+ import VirtualFileSystem from "../VirtualFileSystem";
19
+ import { decodeVfs } from "../VirtualFileSystem/binaryPack";
19
20
  import type { ShellProperties } from "../VirtualShell";
20
21
  import type { VirtualActiveSession, VirtualUserManager } from "../VirtualUserManager";
21
22
 
23
+
22
24
  // ─── helpers ────────────────────────────────────────────────────────────────
23
25
 
24
26
  function ensureDir(vfs: VirtualFileSystem, path: string, mode = 0o755): void {
@@ -31,7 +33,8 @@ function ensureFile(
31
33
  content: string,
32
34
  mode = 0o644,
33
35
  ): void {
34
- if (!vfs.exists(path)) vfs.writeFile(path, content, { mode });
36
+ // Use lazy stub no Buffer allocated until the file is actually read or overwritten
37
+ vfs.writeStub(path, content, mode);
35
38
  }
36
39
 
37
40
  function write(vfs: VirtualFileSystem, path: string, content: string): void {
@@ -766,6 +769,50 @@ function bootstrapMisc(vfs: VirtualFileSystem, props: ShellProperties): void {
766
769
  ensureDir(vfs, "/lost+found", 0o700);
767
770
  }
768
771
 
772
+ // ── Static rootfs snapshot cache ─────────────────────────────────────────────
773
+ // The static parts of the rootfs (dev, usr, var, bin, tmp, etc, sys, misc)
774
+ // are identical for all shells sharing the same hostname+props.
775
+ // We build them once, serialise to VFSB binary, and clone via decodeVfs()
776
+ // for each new shell — avoiding ~250 JS object allocations per instance.
777
+
778
+ const _staticRootfsCache = new Map<string, Buffer>();
779
+
780
+ function _staticCacheKey(hostname: string, props: ShellProperties): string {
781
+ return `${hostname}|${props.kernel}|${props.os}|${props.arch}`;
782
+ }
783
+
784
+ /**
785
+ * Build or retrieve the static rootfs VFSB snapshot for the given
786
+ * hostname + ShellProperties combination.
787
+ *
788
+ * Subsequent calls with the same key return the cached Buffer in ~0ms.
789
+ */
790
+ export function getStaticRootfsSnapshot(
791
+ hostname: string,
792
+ props: ShellProperties,
793
+ ): Buffer {
794
+ const key = _staticCacheKey(hostname, props);
795
+ const cached = _staticRootfsCache.get(key);
796
+ if (cached) return cached;
797
+
798
+ // Build the static subset into a temporary VFS
799
+ const tmp = new VirtualFileSystem({ mode: "memory" });
800
+ bootstrapEtc(tmp, hostname, props);
801
+ bootstrapSys(tmp, hostname, props);
802
+ bootstrapDev(tmp);
803
+ bootstrapUsr(tmp);
804
+ bootstrapVar(tmp);
805
+ bootstrapBin(tmp);
806
+ bootstrapTmp(tmp);
807
+ bootstrapMisc(tmp, props);
808
+ bootProcLog(tmp, props);
809
+
810
+ const snapshot = tmp.encodeBinary();
811
+ _staticRootfsCache.set(key, snapshot);
812
+ return snapshot;
813
+ }
814
+
815
+
769
816
  // ─── main entry point ─────────────────────────────────────────────────────────
770
817
 
771
818
  /**
@@ -787,18 +834,15 @@ export function bootstrapLinuxRootfs(
787
834
  shellStartTime: number,
788
835
  sessions: VirtualActiveSession[] = [],
789
836
  ): void {
790
- bootstrapEtc(vfs, hostname, props);
791
- bootstrapSys(vfs, hostname, props);
792
- bootstrapDev(vfs);
793
- bootstrapUsr(vfs);
794
- bootstrapVar(vfs);
795
- bootstrapBin(vfs);
796
- bootstrapTmp(vfs);
797
- bootstrapRoot(vfs);
798
- bootstrapMisc(vfs, props);
799
- bootProcLog(vfs, props);
800
- refreshProc(vfs, props, hostname, shellStartTime, sessions);
801
- syncEtcPasswd(vfs, users);
837
+ // Fast path: clone the cached static rootfs snapshot (VFSB decode ~0.07ms)
838
+ // instead of rebuilding ~250 JS objects from scratch each time.
839
+ const snapshot = getStaticRootfsSnapshot(hostname, props);
840
+ vfs.importRootTree(decodeVfs(snapshot));
841
+
842
+ // Dynamic parts: per-instance data injected after the static clone
843
+ bootstrapRoot(vfs); // /root home dir + .bashrc
844
+ refreshProc(vfs, props, hostname, shellStartTime, sessions); // /proc live data
845
+ syncEtcPasswd(vfs, users); // /etc/passwd|group|shadow
802
846
  }
803
847
 
804
848
  // ─── optional live engine ─────────────────────────────────────────────────────
@@ -62,7 +62,7 @@ function writeLastLogin(username: string, from: string): void {
62
62
  }
63
63
 
64
64
  async function flushVfs(): Promise<void> {
65
- await virtualShell.vfs.flushMirror();
65
+ await virtualShell.vfs.stopAutoFlush();
66
66
  }
67
67
 
68
68
  function loadHistory(authUser: string): string[] {
@@ -506,6 +506,19 @@ runReadlineShell().catch((error: unknown) => {
506
506
  process.exit(1);
507
507
  });
508
508
 
509
+
510
+ // ── Graceful shutdown (process-level) ────────────────────────────────────────
511
+ let _shuttingDown = false;
512
+ async function _gracefulShutdown(signal: string): Promise<void> {
513
+ if (_shuttingDown) return;
514
+ _shuttingDown = true;
515
+ process.stdout.write(`\n[${signal}] Saving VFS...\n`);
516
+ try { await virtualShell.vfs.stopAutoFlush(); } catch {}
517
+ process.exit(0);
518
+ }
519
+ process.on("SIGTERM", () => { void _gracefulShutdown("SIGTERM"); });
520
+ process.on("beforeExit", () => { void virtualShell.vfs.stopAutoFlush(); });
521
+
509
522
  process.on("uncaughtException", (error) => {
510
523
  console.error("Uncaught exception:", error);
511
524
  });
package/src/standalone.ts CHANGED
@@ -31,6 +31,29 @@ new VirtualSftpServer({ port: 2223, hostname, shell: virtualShell })
31
31
  process.exit(1);
32
32
  });
33
33
 
34
+
35
+ // ── Graceful shutdown ─────────────────────────────────────────────────────────
36
+ // On SIGINT / SIGTERM: flush the WAL journal to a full checkpoint before exit.
37
+ // A kill -9 or OOM crash is unrecoverable here, but the WAL journal on disk
38
+ // guarantees all writes since the last checkpoint are replayed on next start.
39
+ let isShuttingDown = false;
40
+ async function gracefulShutdown(signal: string): Promise<void> {
41
+ if (isShuttingDown) return;
42
+ isShuttingDown = true;
43
+ console.log(`\n[${signal}] Flushing VFS checkpoint before exit...`);
44
+ try {
45
+ await virtualShell.vfs.stopAutoFlush();
46
+ console.log("[shutdown] Checkpoint written. Goodbye.");
47
+ } catch (err) {
48
+ console.error("[shutdown] Flush failed:", err);
49
+ }
50
+ process.exit(0);
51
+ }
52
+
53
+ process.on("SIGINT", () => { void gracefulShutdown("SIGINT"); });
54
+ process.on("SIGTERM", () => { void gracefulShutdown("SIGTERM"); });
55
+ process.on("beforeExit", () => { void virtualShell.vfs.stopAutoFlush(); });
56
+
34
57
  process.on("uncaughtException", (error) => {
35
58
  console.debug("Oh my god, something terrible happened: ", error);
36
59
  });