virrun 2.31.1 → 2.32.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.
Files changed (34) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +29 -55
  3. package/dist/cli.js +2 -14
  4. package/dist/{createVirrun-Cg9BQxg_.js → createVirrun-B3b8HpM_.js} +512 -143
  5. package/dist/createVirrun-B6jYdgVI.js +23361 -0
  6. package/dist/createVirrun-B7d3DDOh.js +23361 -0
  7. package/dist/createVirrun-BRWPPzzC.js +23370 -0
  8. package/dist/createVirrun-Bx6rca4K.js +23364 -0
  9. package/dist/{createVirrun-C8zMQHAQ.js → createVirrun-CirfmuYa.js} +507 -144
  10. package/dist/createVirrun-D6vtkrkN.js +23357 -0
  11. package/dist/{createVirrun-rH3O-itW.js → createVirrun-F5Gqe026.js} +4497 -166
  12. package/dist/createVirrun-GQ4IRIfZ.js +23361 -0
  13. package/dist/createVirrun-oOCnOtt0.js +23519 -0
  14. package/dist/index.d.ts +3207 -45
  15. package/dist/index.js +25 -2
  16. package/dist/mainCommand-B99KFsCA.js +26295 -0
  17. package/dist/mainCommand-BuOZ5_tl.js +25784 -0
  18. package/dist/mainCommand-BwiSo5oU.js +26276 -0
  19. package/dist/mainCommand-BziUFOd-.js +26786 -0
  20. package/dist/mainCommand-C6beIti-.js +26748 -0
  21. package/dist/mainCommand-CshwG7mT.js +26277 -0
  22. package/dist/mainCommand-D-8tCgpQ.js +26275 -0
  23. package/dist/mainCommand-D6J0bW3I.js +26290 -0
  24. package/dist/mainCommand-Dka4ZkbD.js +26786 -0
  25. package/dist/mainCommand-KhyFmO-q.js +26508 -0
  26. package/dist/mainCommand-WMKpWxk1.js +26731 -0
  27. package/dist/mainCommand-asfHvyFh.js +26323 -0
  28. package/dist/mainCommand-ePTFrlMi.js +26315 -0
  29. package/dist/mainCommand-nqEH0Sdn.js +26785 -0
  30. package/dist/mainCommand-pfsJR4Jj.js +26787 -0
  31. package/dist/mainCommand-sh3BgZHx.js +26522 -0
  32. package/dist/mainCommand-w8tHehgL.js +26785 -0
  33. package/package.json +13 -7
  34. package/schema.json +24 -0
@@ -1,8 +1,9 @@
1
1
  import { createRequire } from "node:module";
2
- import { constants, tmpdir } from "node:os";
3
2
  import { execFileSync, spawn } from "node:child_process";
4
- import { mkdtempSync } from "node:fs";
3
+ import { appendFileSync, chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
4
+ import { constants, homedir, tmpdir } from "node:os";
5
5
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
6
+ import { createHash } from "node:crypto";
6
7
  import process$1 from "node:process";
7
8
  import { runInThisContext } from "node:vm";
8
9
  import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
@@ -10,13 +11,11 @@ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
10
11
  var __commonJSMin$1 = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
11
12
  var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
12
13
  //#endregion
13
- //#region src/models/source/SourceType.ts
14
- let SourceType = /* @__PURE__ */ function(SourceType) {
15
- SourceType["Dir"] = "dir";
16
- SourceType["Files"] = "files";
17
- SourceType["Git"] = "git";
18
- return SourceType;
19
- }({});
14
+ //#region src/services/cli/parseCliCommand.ts
15
+ const parseCliCommand = (argv) => {
16
+ const separatorIndex = argv.indexOf("--");
17
+ return separatorIndex === -1 ? [...argv] : argv.slice(separatorIndex + 1);
18
+ };
20
19
  //#endregion
21
20
  //#region src/models/virrun/BackendType.ts
22
21
  let BackendType = /* @__PURE__ */ function(BackendType) {
@@ -27,73 +26,6 @@ let BackendType = /* @__PURE__ */ function(BackendType) {
27
26
  return BackendType;
28
27
  }({});
29
28
  //#endregion
30
- //#region src/services/exec/toExitCode.ts
31
- const toExitCode = (code, signal) => code ?? (signal ? 128 + constants.signals[signal] : 0);
32
- //#endregion
33
- //#region src/services/exec/createNativeBackend.ts
34
- const createNativeBackend = () => ({
35
- exec: (command, options) => new Promise((resolve, reject) => {
36
- const [file, ...args] = Array.isArray(command) ? command : [command];
37
- const child = spawn(file, args, {
38
- cwd: options.cwd === "" ? void 0 : options.cwd,
39
- shell: !Array.isArray(command),
40
- stdio: options.stdio
41
- });
42
- let stdout = "";
43
- let stderr = "";
44
- child.stdout?.on("data", (chunk) => {
45
- stdout += chunk.toString();
46
- });
47
- child.stderr?.on("data", (chunk) => {
48
- stderr += chunk.toString();
49
- });
50
- child.on("error", reject);
51
- child.on("close", (code, signal) => {
52
- resolve({
53
- exitCode: toExitCode(code, signal),
54
- stderr,
55
- stdout
56
- });
57
- });
58
- }),
59
- name: "native"
60
- });
61
- //#endregion
62
- //#region src/services/exec/buildBwrapArgs.ts
63
- const buildBwrapArgs = (command, cwd, { network = false, overlayDirs = [] } = {}) => {
64
- const dir = cwd === "" ? process.cwd() : cwd;
65
- const commandArgs = Array.isArray(command) ? [...command] : [
66
- "/bin/sh",
67
- "-c",
68
- command
69
- ];
70
- const overlays = [dir, ...overlayDirs].flatMap((overlayDir) => [
71
- "--overlay-src",
72
- overlayDir,
73
- "--tmp-overlay",
74
- overlayDir
75
- ]);
76
- return [
77
- "--unshare-all",
78
- ...network ? ["--share-net"] : [],
79
- "--die-with-parent",
80
- "--ro-bind",
81
- "/",
82
- "/",
83
- "--dev",
84
- "/dev",
85
- "--proc",
86
- "/proc",
87
- "--tmpfs",
88
- "/tmp",
89
- ...overlays,
90
- "--chdir",
91
- dir,
92
- "--",
93
- ...commandArgs
94
- ];
95
- };
96
- //#endregion
97
29
  //#region ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/core.js
98
30
  var _a$1;
99
31
  function $constructor(name, initializer, params) {
@@ -18680,20 +18612,241 @@ const exhaustiveGuard = (value) => {
18680
18612
  throw new InvalidOperationError("Read", exhaustiveGuard.name, JSON.stringify(value));
18681
18613
  };
18682
18614
  //#endregion
18683
- //#region src/services/exec/isOsBackendSupported.ts
18615
+ //#region src/services/exec/bwrap/buildBwrapArgs.ts
18616
+ const buildBwrapArgs = (command, cwd, { bindDirs = [], isNetworkEnabled = false } = {}, { lowerDirs = [], upperDir, workDir } = {}) => {
18617
+ if (upperDir === void 0 !== (workDir === void 0)) throw new InvalidOperationError(Operation.Create, buildBwrapArgs.name, "a persistent overlay needs both upperDir and workDir");
18618
+ const dir = cwd === "" ? process.cwd() : cwd;
18619
+ const commandArgs = Array.isArray(command) ? [...command] : [
18620
+ "/bin/sh",
18621
+ "-c",
18622
+ command
18623
+ ];
18624
+ const topOverlay = upperDir !== void 0 && workDir !== void 0 ? [
18625
+ "--overlay",
18626
+ upperDir,
18627
+ workDir,
18628
+ dir
18629
+ ] : ["--tmp-overlay", dir];
18630
+ return [
18631
+ "--unshare-all",
18632
+ ...isNetworkEnabled ? ["--share-net"] : [],
18633
+ "--die-with-parent",
18634
+ "--ro-bind",
18635
+ "/",
18636
+ "/",
18637
+ "--dev",
18638
+ "/dev",
18639
+ "--proc",
18640
+ "/proc",
18641
+ "--tmpfs",
18642
+ "/tmp",
18643
+ "--overlay-src",
18644
+ dir,
18645
+ ...lowerDirs.flatMap((lowerDir) => ["--overlay-src", lowerDir]),
18646
+ ...topOverlay,
18647
+ ...bindDirs.flatMap((bindDir) => [
18648
+ "--bind",
18649
+ bindDir,
18650
+ bindDir
18651
+ ]),
18652
+ "--chdir",
18653
+ dir,
18654
+ "--",
18655
+ ...commandArgs
18656
+ ];
18657
+ };
18658
+ //#endregion
18659
+ //#region src/services/exec/util/constants.ts
18660
+ const GITIGNORE_FILENAME = ".gitignore";
18661
+ const VIRRUN_CACHE_DIRECTORY_NAME = ".virrun";
18662
+ const VIRRUN_GITIGNORE_ENTRY = `/${VIRRUN_CACHE_DIRECTORY_NAME}/`;
18663
+ const VIRRUN_STORE_DIRECTORY_NAME = "store";
18664
+ const VIRRUN_PNPM_STORE_DIRECTORY_NAME = "pnpm";
18665
+ const VIRRUN_COREPACK_STORE_DIRECTORY_NAME = "corepack";
18666
+ const VIRRUN_SNAPSHOTS_DIRECTORY_NAME = "snapshots";
18667
+ const VIRRUN_SNAPSHOT_UPPER_DIRECTORY_NAME = "upper";
18668
+ const VIRRUN_SNAPSHOT_WORK_DIRECTORY_NAME = "work";
18669
+ const PNPM_LOCKFILE_FILENAME = "pnpm-lock.yaml";
18670
+ const VIRRUN_CONFIGURATION_FILENAME = "virrun.config.json";
18671
+ const VIRRUN_ENV_KEY = "VIRRUN";
18672
+ const COREPACK_HOME_KEY = "COREPACK_HOME";
18673
+ const VIRRUN_CACHE_HOME_KEY = "VIRRUN_CACHE_HOME";
18674
+ const PNPM_CONFIG_PACKAGE_IMPORT_METHOD_KEY = "PNPM_CONFIG_PACKAGE_IMPORT_METHOD";
18675
+ const PNPM_CONFIG_PACKAGE_IMPORT_METHOD_VALUE = "copy";
18676
+ const PNPM_CONFIG_STORE_DIR_KEY = "PNPM_CONFIG_STORE_DIR";
18677
+ const VIRRUN_TEMP_DIR_PREFIX = "virrun-temp-";
18678
+ //#endregion
18679
+ //#region src/services/exec/os/isOsBackendSupported.ts
18684
18680
  let isSupported;
18685
18681
  const isOsBackendSupported = () => {
18686
18682
  if (isSupported !== void 0) return isSupported;
18687
- if (process.platform !== "linux") {
18688
- isSupported = false;
18689
- return isSupported;
18690
- }
18691
- const dir = mkdtempSync(join(tmpdir(), "os-support-"));
18692
- isSupported = getResult(() => execFileSync("bwrap", buildBwrapArgs("true", dir), { stdio: "pipe" })).match(() => true, () => false);
18683
+ else if (process.platform === "linux") {
18684
+ const dir = mkdtempSync(join(tmpdir(), VIRRUN_TEMP_DIR_PREFIX));
18685
+ isSupported = getResult(() => withFinalizer(() => execFileSync("bwrap", buildBwrapArgs("true", dir), { stdio: "pipe" }), () => {
18686
+ rmSync(dir, {
18687
+ force: true,
18688
+ recursive: true
18689
+ });
18690
+ })).match(() => true, () => false);
18691
+ } else if (process.platform === "win32") isSupported = getResult(() => execFileSync("wsl.exe", [
18692
+ "--exec",
18693
+ "mktemp",
18694
+ "-d"
18695
+ ], { stdio: "pipe" })).map((stdout) => stdout.toString().trim()).andThen((wslDir) => getResult(() => withFinalizer(() => execFileSync("wsl.exe", [
18696
+ "--exec",
18697
+ "bwrap",
18698
+ ...buildBwrapArgs("true", wslDir)
18699
+ ], { stdio: "pipe" }), () => {
18700
+ getResult(() => execFileSync("wsl.exe", [
18701
+ "--exec",
18702
+ "rm",
18703
+ "-rf",
18704
+ wslDir
18705
+ ], { stdio: "pipe" })).unwrapOr(void 0);
18706
+ }))).match(() => true, () => false);
18707
+ else isSupported = false;
18693
18708
  return isSupported;
18694
18709
  };
18695
18710
  //#endregion
18696
- //#region src/services/exec/parseBwrapExitCode.ts
18711
+ //#region src/services/configuration/resolveBackend.ts
18712
+ const resolveBackend = (configuration) => {
18713
+ if (configuration === void 0) return "auto";
18714
+ else if (configuration.backend === "os" && !isOsBackendSupported()) return configuration.fallback === "os" ? "native" : configuration.fallback;
18715
+ else return configuration.backend;
18716
+ };
18717
+ //#endregion
18718
+ //#region src/services/configuration/parseVirrunConfiguration.ts
18719
+ const isBackendType = (value) => typeof value === "string" && Object.values(BackendType).includes(value);
18720
+ const toBackendType = (value, field) => {
18721
+ if (isBackendType(value)) return value;
18722
+ throw new InvalidOperationError(Operation.Read, parseVirrunConfiguration.name, `\`${field}\` must be one of: ${Object.values(BackendType).join(", ")}`);
18723
+ };
18724
+ const parseVirrunConfiguration = (content) => {
18725
+ const result = getResult(() => JSON.parse(content));
18726
+ if (result.isErr()) throw new InvalidOperationError(Operation.Read, parseVirrunConfiguration.name, `not valid JSON: ${result.error.message}`);
18727
+ const value = result.value;
18728
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new InvalidOperationError(Operation.Read, parseVirrunConfiguration.name, "must be a JSON object");
18729
+ const allowedKeys = [
18730
+ "$schema",
18731
+ "backend",
18732
+ "fallback"
18733
+ ];
18734
+ const unknownKeys = Object.keys(value).filter((key) => !allowedKeys.includes(key));
18735
+ if (unknownKeys.length > 0) throw new InvalidOperationError(Operation.Read, parseVirrunConfiguration.name, `unknown ${unknownKeys.length === 1 ? "key" : "keys"}: ${unknownKeys.join(", ")} (allowed: ${allowedKeys.join(", ")})`);
18736
+ const { backend = "auto", fallback = "native" } = value;
18737
+ return {
18738
+ backend: toBackendType(backend, "backend"),
18739
+ fallback: toBackendType(fallback, "fallback")
18740
+ };
18741
+ };
18742
+ //#endregion
18743
+ //#region src/services/exec/util/resolveCwd.ts
18744
+ const resolveCwd = (cwd) => cwd === "" ? process.cwd() : cwd;
18745
+ //#endregion
18746
+ //#region ../../node_modules/.pnpm/empathic@2.0.1/node_modules/empathic/resolve.mjs
18747
+ /**
18748
+ * Resolve an absolute path from {@link root}, but only
18749
+ * if {@link input} isn't already absolute.
18750
+ *
18751
+ * @param input The path to resolve.
18752
+ * @param root The base path; default = process.cwd()
18753
+ * @returns The resolved absolute path.
18754
+ */
18755
+ function absolute(input, root) {
18756
+ return isAbsolute(input) ? input : resolve(root || ".", input);
18757
+ }
18758
+ //#endregion
18759
+ //#region ../../node_modules/.pnpm/empathic@2.0.1/node_modules/empathic/walk.mjs
18760
+ /**
18761
+ * Get all parent directories of {@link base}.
18762
+ * Stops after {@link Options['last']} is processed.
18763
+ *
18764
+ * @returns An array of absolute paths of all parent directories.
18765
+ */
18766
+ function up(base, options) {
18767
+ let { last, cwd } = options || {};
18768
+ let tmp = absolute(base, cwd);
18769
+ let root = absolute(last || "/", cwd);
18770
+ let prev, arr = [];
18771
+ while (prev !== root) {
18772
+ arr.push(tmp);
18773
+ tmp = dirname(prev = tmp);
18774
+ if (tmp === prev) break;
18775
+ }
18776
+ return arr;
18777
+ }
18778
+ //#endregion
18779
+ //#region ../../node_modules/.pnpm/empathic@2.0.1/node_modules/empathic/find.mjs
18780
+ /**
18781
+ * Find a file by name, walking parent directories until found.
18782
+ *
18783
+ * > [NOTE]
18784
+ * > This function only returns a value for file matches.
18785
+ * > A directory match with the same name will be ignored.
18786
+ *
18787
+ * @param name The file name to find.
18788
+ * @returns The absolute path to the file, if found.
18789
+ */
18790
+ function file(name, options) {
18791
+ let dir, tmp;
18792
+ for (dir of up(options && options.cwd || "", options)) try {
18793
+ tmp = join(dir, name);
18794
+ if (statSync(tmp).isFile()) return tmp;
18795
+ } catch {}
18796
+ }
18797
+ //#endregion
18798
+ //#region src/services/configuration/resolveVirrunConfiguration.ts
18799
+ const resolveVirrunConfiguration = (cwd = "") => {
18800
+ const configurationFile = file(VIRRUN_CONFIGURATION_FILENAME, { cwd: resolveCwd(cwd) });
18801
+ if (configurationFile === void 0) return void 0;
18802
+ else return parseVirrunConfiguration(readFileSync(configurationFile, "utf8"));
18803
+ };
18804
+ //#endregion
18805
+ //#region src/models/source/SourceType.ts
18806
+ let SourceType = /* @__PURE__ */ function(SourceType) {
18807
+ SourceType["Dir"] = "dir";
18808
+ SourceType["Files"] = "files";
18809
+ SourceType["Git"] = "git";
18810
+ return SourceType;
18811
+ }({});
18812
+ //#endregion
18813
+ //#region src/services/exec/util/toExitCode.ts
18814
+ const toExitCode = (code, signal) => code ?? (signal ? 128 + constants.signals[signal] : 0);
18815
+ //#endregion
18816
+ //#region src/services/exec/native/createNativeBackend.ts
18817
+ const createNativeBackend = () => ({
18818
+ exec: (command, options) => new Promise((resolve, reject) => {
18819
+ const [file, ...args] = Array.isArray(command) ? command : [command];
18820
+ const child = spawn(file, args, {
18821
+ cwd: options.cwd === "" ? void 0 : options.cwd,
18822
+ env: {
18823
+ ...process.env,
18824
+ ...options.env
18825
+ },
18826
+ shell: !Array.isArray(command),
18827
+ stdio: options.stdio
18828
+ });
18829
+ let stdout = "";
18830
+ let stderr = "";
18831
+ child.stdout?.on("data", (chunk) => {
18832
+ stdout += chunk.toString();
18833
+ });
18834
+ child.stderr?.on("data", (chunk) => {
18835
+ stderr += chunk.toString();
18836
+ });
18837
+ child.on("error", reject);
18838
+ child.on("close", (code, signal) => {
18839
+ resolve({
18840
+ exitCode: toExitCode(code, signal),
18841
+ stderr,
18842
+ stdout
18843
+ });
18844
+ });
18845
+ }),
18846
+ name: "native"
18847
+ });
18848
+ //#endregion
18849
+ //#region src/services/exec/bwrap/parseBwrapExitCode.ts
18697
18850
  const parseBwrapExitCode = (status) => {
18698
18851
  for (const line of status.split("\n")) {
18699
18852
  const result = getResult(() => JSON.parse(line));
@@ -18701,53 +18854,258 @@ const parseBwrapExitCode = (status) => {
18701
18854
  }
18702
18855
  };
18703
18856
  //#endregion
18704
- //#region src/services/exec/createOsBackend.ts
18705
- const createOsBackend = () => {
18706
- if (!isOsBackendSupported()) throw new InvalidOperationError(Operation.Create, createOsBackend.name, "requires Linux + bubblewrap");
18857
+ //#region src/services/exec/bwrap/constants.ts
18858
+ const WSL_BWRAP_STATUS_BEGIN = "\n__VIRRUN_BWRAP_STATUS_BEGIN__\n";
18859
+ const WSL_BWRAP_STATUS_END = "\n__VIRRUN_BWRAP_STATUS_END__\n";
18860
+ //#endregion
18861
+ //#region src/services/exec/bwrap/parseBwrapStderrStatus.ts
18862
+ const parseBwrapStderrStatus = (stderr) => {
18863
+ const beginIndex = stderr.lastIndexOf(WSL_BWRAP_STATUS_BEGIN);
18864
+ const endIndex = stderr.lastIndexOf(WSL_BWRAP_STATUS_END);
18865
+ if (beginIndex === -1 || endIndex === -1 || endIndex < beginIndex) return {
18866
+ status: "",
18867
+ stderr
18868
+ };
18869
+ const statusStartIndex = beginIndex + 31;
18707
18870
  return {
18708
- exec: (command, options) => new Promise((resolve, reject) => {
18709
- const stdio = [
18710
- options.stdio,
18711
- options.stdio,
18712
- options.stdio,
18713
- "pipe"
18714
- ];
18715
- const child = spawn("bwrap", [
18716
- "--json-status-fd",
18717
- "3",
18718
- ...buildBwrapArgs(command, options.cwd, options)
18719
- ], {
18720
- shell: false,
18721
- stdio
18722
- });
18723
- let stdout = "";
18724
- let stderr = "";
18725
- let status = "";
18726
- child.stdout?.on("data", (chunk) => {
18727
- stdout += chunk.toString();
18728
- });
18729
- child.stderr?.on("data", (chunk) => {
18730
- stderr += chunk.toString();
18731
- });
18732
- child.stdio[3]?.on("data", (chunk) => {
18733
- status += chunk.toString();
18734
- });
18735
- child.on("error", reject);
18736
- child.on("close", () => {
18737
- const exitCode = parseBwrapExitCode(status);
18738
- if (exitCode === void 0) reject(new InvalidOperationError(Operation.Create, createOsBackend.name, "bubblewrap failed to set up the sandbox"));
18739
- else resolve({
18740
- exitCode,
18741
- stderr,
18742
- stdout
18743
- });
18871
+ status: stderr.slice(statusStartIndex, endIndex),
18872
+ stderr: `${stderr.slice(0, beginIndex)}${stderr.slice(endIndex + 29)}`
18873
+ };
18874
+ };
18875
+ //#endregion
18876
+ //#region src/services/exec/bwrap/createBwrapBackend.ts
18877
+ const createBwrapBackend = (createBwrapArgs, createBwrapCommand, errorName) => ({
18878
+ exec: (command, options) => new Promise((resolve, reject) => {
18879
+ const bwrapCommand = createBwrapCommand(createBwrapArgs(command, options.cwd, options), options);
18880
+ const [file, ...args] = bwrapCommand.command;
18881
+ const stdio = bwrapCommand.statusSource === "fd" ? [
18882
+ options.stdio,
18883
+ options.stdio,
18884
+ options.stdio,
18885
+ "pipe"
18886
+ ] : [
18887
+ options.stdio,
18888
+ options.stdio,
18889
+ "pipe"
18890
+ ];
18891
+ const child = spawn(file, args, {
18892
+ env: {
18893
+ ...process.env,
18894
+ ...options.env
18895
+ },
18896
+ shell: false,
18897
+ stdio
18898
+ });
18899
+ let stdout = "";
18900
+ let stderr = "";
18901
+ let status = "";
18902
+ child.stdout?.on("data", (chunk) => {
18903
+ stdout += chunk.toString();
18904
+ });
18905
+ child.stderr?.on("data", (chunk) => {
18906
+ stderr += chunk.toString();
18907
+ });
18908
+ child.stdio[3]?.on("data", (chunk) => {
18909
+ status += chunk.toString();
18910
+ });
18911
+ child.on("error", reject);
18912
+ child.on("close", () => {
18913
+ const bwrapStderr = bwrapCommand.statusSource === "stderr" ? parseBwrapStderrStatus(stderr) : {
18914
+ status,
18915
+ stderr
18916
+ };
18917
+ const exitCode = parseBwrapExitCode(bwrapStderr.status);
18918
+ if (exitCode === void 0) reject(new InvalidOperationError(Operation.Create, errorName, "bubblewrap failed to set up the sandbox"));
18919
+ else resolve({
18920
+ exitCode,
18921
+ stderr: bwrapStderr.stderr,
18922
+ stdout
18744
18923
  });
18745
- }),
18746
- name: "os"
18924
+ });
18925
+ }),
18926
+ name: "os"
18927
+ });
18928
+ //#endregion
18929
+ //#region src/services/exec/bwrap/createLinuxOsBackend.ts
18930
+ const createLinuxOsBackend = (errorName) => createBwrapBackend((command, cwd, options) => buildBwrapArgs(command, cwd, options, options.overlayLayers), (bwrapArgs) => ({
18931
+ command: [
18932
+ "bwrap",
18933
+ "--json-status-fd",
18934
+ "3",
18935
+ ...bwrapArgs
18936
+ ],
18937
+ statusSource: "fd"
18938
+ }), errorName);
18939
+ //#endregion
18940
+ //#region src/services/exec/wsl/readWslPath.ts
18941
+ const wslPaths = /* @__PURE__ */ new Map();
18942
+ const readWslPath = (path) => {
18943
+ const wslPath = wslPaths.get(path);
18944
+ if (wslPath) return wslPath;
18945
+ const newWslPath = execFileSync("wsl.exe", [
18946
+ "--exec",
18947
+ "wslpath",
18948
+ "-a",
18949
+ path
18950
+ ], {
18951
+ encoding: "utf8",
18952
+ stdio: "pipe"
18953
+ }).trim();
18954
+ wslPaths.set(path, newWslPath);
18955
+ return newWslPath;
18956
+ };
18957
+ //#endregion
18958
+ //#region src/services/exec/wsl/createWslBwrapArgs.ts
18959
+ const readWslOverlayLayers = ({ lowerDirs, upperDir, workDir }) => ({
18960
+ lowerDirs: lowerDirs?.map((lowerDir) => readWslPath(lowerDir)),
18961
+ upperDir: upperDir === void 0 ? void 0 : readWslPath(upperDir),
18962
+ workDir: workDir === void 0 ? void 0 : readWslPath(workDir)
18963
+ });
18964
+ const createWslBwrapArgs = (command, cwd, { bindDirs = [], isNetworkEnabled = false, overlayLayers } = {}) => {
18965
+ return buildBwrapArgs(command, readWslPath(resolveCwd(cwd)), {
18966
+ bindDirs: bindDirs.map((bindDir) => readWslPath(bindDir)),
18967
+ isNetworkEnabled
18968
+ }, overlayLayers === void 0 ? void 0 : readWslOverlayLayers(overlayLayers));
18969
+ };
18970
+ //#endregion
18971
+ //#region src/services/exec/wsl/createWslEnvArgs.ts
18972
+ const WSL_PATH_ENV_KEYS = /* @__PURE__ */ new Set([COREPACK_HOME_KEY, PNPM_CONFIG_STORE_DIR_KEY]);
18973
+ const createWslEnvArgs = ({ env = {} }) => Object.entries(env).map(([key, value]) => `${key}=${WSL_PATH_ENV_KEYS.has(key) ? readWslPath(value) : value}`);
18974
+ //#endregion
18975
+ //#region src/services/exec/wsl/createWslOsBackend.ts
18976
+ const createWslOsBackend = (errorName) => createBwrapBackend(createWslBwrapArgs, (bwrapArgs, options) => ({
18977
+ command: [
18978
+ "wsl.exe",
18979
+ "--exec",
18980
+ "env",
18981
+ ...createWslEnvArgs(options),
18982
+ "sh",
18983
+ "-c",
18984
+ [
18985
+ `status="$(mktemp)"`,
18986
+ `bwrap --json-status-fd 3 "$@" 3>"$status"`,
18987
+ `bwrapExitCode=$?`,
18988
+ `printf '${WSL_BWRAP_STATUS_BEGIN.replaceAll("\n", String.raw`\n`)}' >&2`,
18989
+ `cat "$status" >&2`,
18990
+ `printf '${WSL_BWRAP_STATUS_END.replaceAll("\n", String.raw`\n`)}' >&2`,
18991
+ `rm -f "$status"`,
18992
+ `exit "$bwrapExitCode"`
18993
+ ].join("; "),
18994
+ "virrun-bwrap",
18995
+ ...bwrapArgs
18996
+ ],
18997
+ statusSource: "stderr"
18998
+ }), errorName);
18999
+ //#endregion
19000
+ //#region src/services/exec/os/createOsBackend.ts
19001
+ const createOsBackend = () => {
19002
+ if (!isOsBackendSupported()) throw new InvalidOperationError(Operation.Create, createOsBackend.name, "requires Linux/WSL + bubblewrap");
19003
+ if (process.platform === "linux") return createLinuxOsBackend(createOsBackend.name);
19004
+ else if (process.platform === "win32") return createWslOsBackend(createOsBackend.name);
19005
+ throw new InvalidOperationError(Operation.Create, createOsBackend.name, "requires Linux/WSL + bubblewrap");
19006
+ };
19007
+ //#endregion
19008
+ //#region src/services/exec/snapshot/removeSnapshotLocation.ts
19009
+ const makeTraversable = (dir) => {
19010
+ chmodSync(dir, 448);
19011
+ for (const entry of readdirSync(dir, { withFileTypes: true })) if (entry.isDirectory()) makeTraversable(join(dir, entry.name));
19012
+ };
19013
+ const removeSnapshotLocation = ({ dir }) => {
19014
+ if (existsSync(dir)) makeTraversable(dir);
19015
+ rmSync(dir, {
19016
+ force: true,
19017
+ recursive: true
19018
+ });
19019
+ };
19020
+ //#endregion
19021
+ //#region src/services/exec/snapshot/computeLockfileHash.ts
19022
+ const computeLockfileHash = (cwd) => {
19023
+ const dir = resolveCwd(cwd);
19024
+ const lockfile = join(dir, PNPM_LOCKFILE_FILENAME);
19025
+ if (!existsSync(lockfile)) throw new InvalidOperationError(Operation.Read, computeLockfileHash.name, `no ${PNPM_LOCKFILE_FILENAME} in ${dir}`);
19026
+ return createHash("sha256").update(readFileSync(lockfile)).digest("hex");
19027
+ };
19028
+ //#endregion
19029
+ //#region src/services/exec/util/getGlobalCacheDirectory.ts
19030
+ const getGlobalCacheDirectory = () => process.env["VIRRUN_CACHE_HOME"] || join(homedir(), ".virrun");
19031
+ //#endregion
19032
+ //#region src/services/exec/snapshot/resolveSnapshotLocation.ts
19033
+ const resolveSnapshotLocation = (cwd) => {
19034
+ const hash = computeLockfileHash(cwd);
19035
+ const snapshotDir = join(getGlobalCacheDirectory(), VIRRUN_SNAPSHOTS_DIRECTORY_NAME, hash);
19036
+ const upperDir = join(snapshotDir, VIRRUN_SNAPSHOT_UPPER_DIRECTORY_NAME);
19037
+ return {
19038
+ dir: snapshotDir,
19039
+ exists: existsSync(upperDir),
19040
+ hash,
19041
+ upperDir,
19042
+ workDir: join(snapshotDir, VIRRUN_SNAPSHOT_WORK_DIRECTORY_NAME)
19043
+ };
19044
+ };
19045
+ //#endregion
19046
+ //#region src/services/exec/snapshot/createSnapshot.ts
19047
+ const createSnapshot = (backend, command, options) => {
19048
+ const location = resolveSnapshotLocation(options.cwd);
19049
+ const { upperDir, workDir } = location;
19050
+ return getResultAsync(async () => {
19051
+ mkdirSync(upperDir, { recursive: true });
19052
+ mkdirSync(workDir, { recursive: true });
19053
+ const result = await backend.exec(command, {
19054
+ ...options,
19055
+ overlayLayers: {
19056
+ upperDir,
19057
+ workDir
19058
+ }
19059
+ });
19060
+ if (result.exitCode !== 0) throw new InvalidOperationError(Operation.Create, createSnapshot.name, `snapshot setup command exited with ${result.exitCode}: ${result.stderr}`);
19061
+ return {
19062
+ location: {
19063
+ ...location,
19064
+ exists: true
19065
+ },
19066
+ result
19067
+ };
19068
+ }).match((value) => value, (error) => {
19069
+ removeSnapshotLocation(location);
19070
+ throw error;
19071
+ });
19072
+ };
19073
+ //#endregion
19074
+ //#region src/services/exec/snapshot/forkSnapshot.ts
19075
+ const forkSnapshot = (backend, command, options) => {
19076
+ const { exists, upperDir } = resolveSnapshotLocation(options.cwd);
19077
+ if (!exists) throw new InvalidOperationError(Operation.Read, forkSnapshot.name, "no captured snapshot to fork; run createSnapshot first");
19078
+ return backend.exec(command, {
19079
+ ...options,
19080
+ overlayLayers: { lowerDirs: [upperDir] }
19081
+ });
19082
+ };
19083
+ //#endregion
19084
+ //#region src/services/exec/util/getRepoCacheDirectory.ts
19085
+ const getRepoCacheDirectory = (cwd) => join(resolveCwd(cwd), VIRRUN_CACHE_DIRECTORY_NAME);
19086
+ //#endregion
19087
+ //#region src/services/exec/store/createSharedPackageStoreOptions.ts
19088
+ const ensureGitIgnoreEntry = (cwd) => {
19089
+ const gitignore = join(cwd, GITIGNORE_FILENAME);
19090
+ const gitignoreContent = existsSync(gitignore) ? readFileSync(gitignore, "utf8") : "";
19091
+ if (gitignoreContent.split(/\r?\n/u).includes(VIRRUN_GITIGNORE_ENTRY)) return;
19092
+ appendFileSync(gitignore, `${gitignoreContent.endsWith("\n") || gitignoreContent === "" ? "" : "\n"}${VIRRUN_GITIGNORE_ENTRY}\n`);
19093
+ };
19094
+ const createSharedPackageStoreOptions = (cwd) => {
19095
+ const dir = resolveCwd(cwd);
19096
+ const storeDir = join(getRepoCacheDirectory(dir), VIRRUN_STORE_DIRECTORY_NAME, VIRRUN_PNPM_STORE_DIRECTORY_NAME);
19097
+ mkdirSync(storeDir, { recursive: true });
19098
+ ensureGitIgnoreEntry(dir);
19099
+ return {
19100
+ bindDirs: [storeDir],
19101
+ env: {
19102
+ [PNPM_CONFIG_PACKAGE_IMPORT_METHOD_KEY]: PNPM_CONFIG_PACKAGE_IMPORT_METHOD_VALUE,
19103
+ [PNPM_CONFIG_STORE_DIR_KEY]: storeDir
19104
+ }
18747
19105
  };
18748
19106
  };
18749
19107
  //#endregion
18750
- //#region src/services/exec/tokenizeShellCommand.ts
19108
+ //#region src/services/exec/vfs/tokenizeShellCommand.ts
18751
19109
  const SHELL_OPERATORS = /* @__PURE__ */ new Set([
18752
19110
  "$",
18753
19111
  "&",
@@ -18792,7 +19150,7 @@ const tokenizeShellCommand = (input) => {
18792
19150
  return tokens;
18793
19151
  };
18794
19152
  //#endregion
18795
- //#region src/services/exec/parseNodeInvocation.ts
19153
+ //#region src/services/exec/vfs/parseNodeInvocation.ts
18796
19154
  const parseNodeInvocation = (command) => {
18797
19155
  const tokens = typeof command === "string" ? tokenizeShellCommand(command) : [...command];
18798
19156
  if (!tokens) return void 0;
@@ -22787,10 +23145,10 @@ var import_vfs = (/* @__PURE__ */ __commonJSMin$1(((exports, module) => {
22787
23145
  SqliteProvider
22788
23146
  };
22789
23147
  })))();
22790
- const createPlatformaticFsProvider = ({ overlay = false } = {}) => {
23148
+ const createPlatformaticFsProvider = ({ isOverlayEnabled = false } = {}) => {
22791
23149
  const vfs = (0, import_vfs.create)({
22792
23150
  moduleHooks: true,
22793
- overlay
23151
+ overlay: isOverlayEnabled
22794
23152
  });
22795
23153
  return {
22796
23154
  dispose: () => {
@@ -22814,7 +23172,7 @@ const createPlatformaticFsProvider = ({ overlay = false } = {}) => {
22814
23172
  };
22815
23173
  };
22816
23174
  //#endregion
22817
- //#region src/services/exec/runNodeInProcess.ts
23175
+ //#region src/services/exec/vfs/runNodeInProcess.ts
22818
23176
  const runNodeInProcess = ({ code, file }, { cwd, stdio }) => {
22819
23177
  const originalStdoutWrite = process$1.stdout.write.bind(process$1.stdout);
22820
23178
  const originalStderrWrite = process$1.stderr.write.bind(process$1.stderr);
@@ -22823,7 +23181,7 @@ const runNodeInProcess = ({ code, file }, { cwd, stdio }) => {
22823
23181
  const originalRequire = globalThis.require;
22824
23182
  const originalCwd = cwd === "" ? "" : process$1.cwd();
22825
23183
  const baseDir = cwd === "" ? process$1.cwd() : cwd;
22826
- const fs = createPlatformaticFsProvider({ overlay: true });
23184
+ const fs = createPlatformaticFsProvider({ isOverlayEnabled: true });
22827
23185
  const isPipe = stdio === "pipe";
22828
23186
  const require = createRequire(resolve(baseDir, "[eval].js"));
22829
23187
  const cachedBefore = new Set(Object.keys(require.cache));
@@ -22868,7 +23226,7 @@ const runNodeInProcess = ({ code, file }, { cwd, stdio }) => {
22868
23226
  });
22869
23227
  };
22870
23228
  //#endregion
22871
- //#region src/services/exec/createVfsBackend.ts
23229
+ //#region src/services/exec/vfs/createVfsBackend.ts
22872
23230
  const createVfsBackend = () => {
22873
23231
  const native = createNativeBackend();
22874
23232
  return {
@@ -22890,7 +23248,7 @@ const loadDirSource = (source) => Promise.resolve({
22890
23248
  //#endregion
22891
23249
  //#region src/services/source/loadFilesSource.ts
22892
23250
  const loadFilesSource = async (source) => {
22893
- const cwd = await mkdtemp(join(tmpdir(), "sandbox-"));
23251
+ const cwd = await mkdtemp(join(tmpdir(), VIRRUN_TEMP_DIR_PREFIX));
22894
23252
  const dispose = () => rm(cwd, {
22895
23253
  force: true,
22896
23254
  recursive: true
@@ -22968,21 +23326,32 @@ const backendFactories = {
22968
23326
  ["os"]: createOsBackend,
22969
23327
  ["vfs"]: createVfsBackend
22970
23328
  };
22971
- const createVirrun = async (options = {}) => {
22972
- const { backend = "auto", source = {
22973
- dir: "",
22974
- type: "dir"
22975
- } } = options;
23329
+ const createVirrun = async ({ backend = "auto", source = {
23330
+ dir: "",
23331
+ type: "dir"
23332
+ } } = {}) => {
22976
23333
  const execBackend = backendFactories[backend]();
22977
23334
  const { cwd, dispose } = await loadSource(source);
23335
+ const sharedPackageStoreOptions = execBackend.name === "os" ? createSharedPackageStoreOptions(cwd) : {};
23336
+ const toOptions = (stdio) => ({
23337
+ ...sharedPackageStoreOptions,
23338
+ cwd,
23339
+ env: {
23340
+ ...sharedPackageStoreOptions.env,
23341
+ [VIRRUN_ENV_KEY]: "true"
23342
+ },
23343
+ stdio
23344
+ });
22978
23345
  return {
22979
23346
  backend: execBackend.name,
22980
23347
  dispose,
22981
- exec: (command, stdio = "pipe") => execBackend.exec(command, {
22982
- cwd,
22983
- stdio
22984
- })
23348
+ exec: (command, stdio = "pipe") => execBackend.exec(command, toOptions(stdio)),
23349
+ fork: async (command, stdio = "pipe") => {
23350
+ if (execBackend.name !== "os") return execBackend.exec(command, toOptions(stdio));
23351
+ if (!resolveSnapshotLocation(cwd).exists) return (await createSnapshot(execBackend, command, toOptions(stdio))).result;
23352
+ return forkSnapshot(execBackend, command, toOptions(stdio));
23353
+ }
22985
23354
  };
22986
23355
  };
22987
23356
  //#endregion
22988
- export { createNativeBackend as _, loadDirSource as a, SourceType as b, createPlatformaticFsProvider as c, tokenizeShellCommand as d, createOsBackend as f, buildBwrapArgs as g, withFinalizerAsync as h, loadFilesSource as i, ExitSignalError as l, isOsBackendSupported as m, loadSource as n, createVfsBackend as o, parseBwrapExitCode as p, loadGitSource as r, runNodeInProcess as s, createVirrun as t, parseNodeInvocation as u, toExitCode as v, BackendType as y };
23357
+ export { VIRRUN_SNAPSHOT_UPPER_DIRECTORY_NAME as $, parseBwrapExitCode as A, GITIGNORE_FILENAME as B, createWslBwrapArgs as C, parseBwrapStderrStatus as D, createBwrapBackend as E, resolveCwd as F, VIRRUN_CACHE_DIRECTORY_NAME as G, PNPM_CONFIG_PACKAGE_IMPORT_METHOD_VALUE as H, parseVirrunConfiguration as I, VIRRUN_COREPACK_STORE_DIRECTORY_NAME as J, VIRRUN_CACHE_HOME_KEY as K, resolveBackend as L, toExitCode as M, SourceType as N, WSL_BWRAP_STATUS_BEGIN as O, resolveVirrunConfiguration as P, VIRRUN_SNAPSHOTS_DIRECTORY_NAME as Q, isOsBackendSupported as R, createWslEnvArgs as S, createLinuxOsBackend as T, PNPM_CONFIG_STORE_DIR_KEY as U, PNPM_CONFIG_PACKAGE_IMPORT_METHOD_KEY as V, PNPM_LOCKFILE_FILENAME as W, VIRRUN_GITIGNORE_ENTRY as X, VIRRUN_ENV_KEY as Y, VIRRUN_PNPM_STORE_DIRECTORY_NAME as Z, getGlobalCacheDirectory as _, loadDirSource as a, BackendType as at, createOsBackend as b, createPlatformaticFsProvider as c, tokenizeShellCommand as d, VIRRUN_SNAPSHOT_WORK_DIRECTORY_NAME as et, createSharedPackageStoreOptions as f, resolveSnapshotLocation as g, createSnapshot as h, loadFilesSource as i, withFinalizerAsync as it, createNativeBackend as j, WSL_BWRAP_STATUS_END as k, ExitSignalError as l, forkSnapshot as m, loadSource as n, VIRRUN_TEMP_DIR_PREFIX as nt, createVfsBackend as o, parseCliCommand as ot, getRepoCacheDirectory as p, VIRRUN_CONFIGURATION_FILENAME as q, loadGitSource as r, buildBwrapArgs as rt, runNodeInProcess as s, createVirrun as t, VIRRUN_STORE_DIRECTORY_NAME as tt, parseNodeInvocation as u, computeLockfileHash as v, readWslPath as w, createWslOsBackend as x, removeSnapshotLocation as y, COREPACK_HOME_KEY as z };