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";
@@ -16,14 +17,6 @@ const parseCliCommand = (argv) => {
16
17
  return separatorIndex === -1 ? [...argv] : argv.slice(separatorIndex + 1);
17
18
  };
18
19
  //#endregion
19
- //#region src/models/source/SourceType.ts
20
- let SourceType = /* @__PURE__ */ function(SourceType) {
21
- SourceType["Dir"] = "dir";
22
- SourceType["Files"] = "files";
23
- SourceType["Git"] = "git";
24
- return SourceType;
25
- }({});
26
- //#endregion
27
20
  //#region src/models/virrun/BackendType.ts
28
21
  let BackendType = /* @__PURE__ */ function(BackendType) {
29
22
  BackendType["Auto"] = "auto";
@@ -33,73 +26,6 @@ let BackendType = /* @__PURE__ */ function(BackendType) {
33
26
  return BackendType;
34
27
  }({});
35
28
  //#endregion
36
- //#region src/services/exec/toExitCode.ts
37
- const toExitCode = (code, signal) => code ?? (signal ? 128 + constants.signals[signal] : 0);
38
- //#endregion
39
- //#region src/services/exec/createNativeBackend.ts
40
- const createNativeBackend = () => ({
41
- exec: (command, options) => new Promise((resolve, reject) => {
42
- const [file, ...args] = Array.isArray(command) ? command : [command];
43
- const child = spawn(file, args, {
44
- cwd: options.cwd === "" ? void 0 : options.cwd,
45
- shell: !Array.isArray(command),
46
- stdio: options.stdio
47
- });
48
- let stdout = "";
49
- let stderr = "";
50
- child.stdout?.on("data", (chunk) => {
51
- stdout += chunk.toString();
52
- });
53
- child.stderr?.on("data", (chunk) => {
54
- stderr += chunk.toString();
55
- });
56
- child.on("error", reject);
57
- child.on("close", (code, signal) => {
58
- resolve({
59
- exitCode: toExitCode(code, signal),
60
- stderr,
61
- stdout
62
- });
63
- });
64
- }),
65
- name: "native"
66
- });
67
- //#endregion
68
- //#region src/services/exec/buildBwrapArgs.ts
69
- const buildBwrapArgs = (command, cwd, { network = false, overlayDirs = [] } = {}) => {
70
- const dir = cwd === "" ? process.cwd() : cwd;
71
- const commandArgs = Array.isArray(command) ? [...command] : [
72
- "/bin/sh",
73
- "-c",
74
- command
75
- ];
76
- const overlays = [dir, ...overlayDirs].flatMap((overlayDir) => [
77
- "--overlay-src",
78
- overlayDir,
79
- "--tmp-overlay",
80
- overlayDir
81
- ]);
82
- return [
83
- "--unshare-all",
84
- ...network ? ["--share-net"] : [],
85
- "--die-with-parent",
86
- "--ro-bind",
87
- "/",
88
- "/",
89
- "--dev",
90
- "/dev",
91
- "--proc",
92
- "/proc",
93
- "--tmpfs",
94
- "/tmp",
95
- ...overlays,
96
- "--chdir",
97
- dir,
98
- "--",
99
- ...commandArgs
100
- ];
101
- };
102
- //#endregion
103
29
  //#region ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/core.js
104
30
  var _a$1;
105
31
  function $constructor(name, initializer, params) {
@@ -18686,20 +18612,241 @@ const exhaustiveGuard = (value) => {
18686
18612
  throw new InvalidOperationError("Read", exhaustiveGuard.name, JSON.stringify(value));
18687
18613
  };
18688
18614
  //#endregion
18689
- //#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
18690
18680
  let isSupported;
18691
18681
  const isOsBackendSupported = () => {
18692
18682
  if (isSupported !== void 0) return isSupported;
18693
- if (process.platform !== "linux") {
18694
- isSupported = false;
18695
- return isSupported;
18696
- }
18697
- const dir = mkdtempSync(join(tmpdir(), "os-support-"));
18698
- 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;
18699
18708
  return isSupported;
18700
18709
  };
18701
18710
  //#endregion
18702
- //#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
+ if (configuration.backend === "os" && !isOsBackendSupported()) return configuration.fallback;
18715
+ 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
+ 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
18703
18850
  const parseBwrapExitCode = (status) => {
18704
18851
  for (const line of status.split("\n")) {
18705
18852
  const result = getResult(() => JSON.parse(line));
@@ -18707,53 +18854,258 @@ const parseBwrapExitCode = (status) => {
18707
18854
  }
18708
18855
  };
18709
18856
  //#endregion
18710
- //#region src/services/exec/createOsBackend.ts
18711
- const createOsBackend = () => {
18712
- 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;
18713
18870
  return {
18714
- exec: (command, options) => new Promise((resolve, reject) => {
18715
- const stdio = [
18716
- options.stdio,
18717
- options.stdio,
18718
- options.stdio,
18719
- "pipe"
18720
- ];
18721
- const child = spawn("bwrap", [
18722
- "--json-status-fd",
18723
- "3",
18724
- ...buildBwrapArgs(command, options.cwd, options)
18725
- ], {
18726
- shell: false,
18727
- stdio
18728
- });
18729
- let stdout = "";
18730
- let stderr = "";
18731
- let status = "";
18732
- child.stdout?.on("data", (chunk) => {
18733
- stdout += chunk.toString();
18734
- });
18735
- child.stderr?.on("data", (chunk) => {
18736
- stderr += chunk.toString();
18737
- });
18738
- child.stdio[3]?.on("data", (chunk) => {
18739
- status += chunk.toString();
18740
- });
18741
- child.on("error", reject);
18742
- child.on("close", () => {
18743
- const exitCode = parseBwrapExitCode(status);
18744
- if (exitCode === void 0) reject(new InvalidOperationError(Operation.Create, createOsBackend.name, "bubblewrap failed to set up the sandbox"));
18745
- else resolve({
18746
- exitCode,
18747
- stderr,
18748
- stdout
18749
- });
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
18750
18923
  });
18751
- }),
18752
- 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
+ }
18753
19105
  };
18754
19106
  };
18755
19107
  //#endregion
18756
- //#region src/services/exec/tokenizeShellCommand.ts
19108
+ //#region src/services/exec/vfs/tokenizeShellCommand.ts
18757
19109
  const SHELL_OPERATORS = /* @__PURE__ */ new Set([
18758
19110
  "$",
18759
19111
  "&",
@@ -18798,7 +19150,7 @@ const tokenizeShellCommand = (input) => {
18798
19150
  return tokens;
18799
19151
  };
18800
19152
  //#endregion
18801
- //#region src/services/exec/parseNodeInvocation.ts
19153
+ //#region src/services/exec/vfs/parseNodeInvocation.ts
18802
19154
  const parseNodeInvocation = (command) => {
18803
19155
  const tokens = typeof command === "string" ? tokenizeShellCommand(command) : [...command];
18804
19156
  if (!tokens) return void 0;
@@ -22793,10 +23145,10 @@ var import_vfs = (/* @__PURE__ */ __commonJSMin$1(((exports, module) => {
22793
23145
  SqliteProvider
22794
23146
  };
22795
23147
  })))();
22796
- const createPlatformaticFsProvider = ({ overlay = false } = {}) => {
23148
+ const createPlatformaticFsProvider = ({ isOverlayEnabled = false } = {}) => {
22797
23149
  const vfs = (0, import_vfs.create)({
22798
23150
  moduleHooks: true,
22799
- overlay
23151
+ overlay: isOverlayEnabled
22800
23152
  });
22801
23153
  return {
22802
23154
  dispose: () => {
@@ -22820,7 +23172,7 @@ const createPlatformaticFsProvider = ({ overlay = false } = {}) => {
22820
23172
  };
22821
23173
  };
22822
23174
  //#endregion
22823
- //#region src/services/exec/runNodeInProcess.ts
23175
+ //#region src/services/exec/vfs/runNodeInProcess.ts
22824
23176
  const runNodeInProcess = ({ code, file }, { cwd, stdio }) => {
22825
23177
  const originalStdoutWrite = process$1.stdout.write.bind(process$1.stdout);
22826
23178
  const originalStderrWrite = process$1.stderr.write.bind(process$1.stderr);
@@ -22829,7 +23181,7 @@ const runNodeInProcess = ({ code, file }, { cwd, stdio }) => {
22829
23181
  const originalRequire = globalThis.require;
22830
23182
  const originalCwd = cwd === "" ? "" : process$1.cwd();
22831
23183
  const baseDir = cwd === "" ? process$1.cwd() : cwd;
22832
- const fs = createPlatformaticFsProvider({ overlay: true });
23184
+ const fs = createPlatformaticFsProvider({ isOverlayEnabled: true });
22833
23185
  const isPipe = stdio === "pipe";
22834
23186
  const require = createRequire(resolve(baseDir, "[eval].js"));
22835
23187
  const cachedBefore = new Set(Object.keys(require.cache));
@@ -22874,7 +23226,7 @@ const runNodeInProcess = ({ code, file }, { cwd, stdio }) => {
22874
23226
  });
22875
23227
  };
22876
23228
  //#endregion
22877
- //#region src/services/exec/createVfsBackend.ts
23229
+ //#region src/services/exec/vfs/createVfsBackend.ts
22878
23230
  const createVfsBackend = () => {
22879
23231
  const native = createNativeBackend();
22880
23232
  return {
@@ -22896,7 +23248,7 @@ const loadDirSource = (source) => Promise.resolve({
22896
23248
  //#endregion
22897
23249
  //#region src/services/source/loadFilesSource.ts
22898
23250
  const loadFilesSource = async (source) => {
22899
- const cwd = await mkdtemp(join(tmpdir(), "sandbox-"));
23251
+ const cwd = await mkdtemp(join(tmpdir(), VIRRUN_TEMP_DIR_PREFIX));
22900
23252
  const dispose = () => rm(cwd, {
22901
23253
  force: true,
22902
23254
  recursive: true
@@ -22974,21 +23326,32 @@ const backendFactories = {
22974
23326
  ["os"]: createOsBackend,
22975
23327
  ["vfs"]: createVfsBackend
22976
23328
  };
22977
- const createVirrun = async (options = {}) => {
22978
- const { backend = "auto", source = {
22979
- dir: "",
22980
- type: "dir"
22981
- } } = options;
23329
+ const createVirrun = async ({ backend = "auto", source = {
23330
+ dir: "",
23331
+ type: "dir"
23332
+ } } = {}) => {
22982
23333
  const execBackend = backendFactories[backend]();
22983
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
+ });
22984
23345
  return {
22985
23346
  backend: execBackend.name,
22986
23347
  dispose,
22987
- exec: (command, stdio = "pipe") => execBackend.exec(command, {
22988
- cwd,
22989
- stdio
22990
- })
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
+ }
22991
23354
  };
22992
23355
  };
22993
23356
  //#endregion
22994
- 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, parseCliCommand as x, 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 };