@crustjs/crust-darwin-x64 0.4.1 → 0.5.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.
package/bin/dist/index.js CHANGED
@@ -1,169 +1,34 @@
1
- import { accessSync, constants, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
2
- import { chmod, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
1
+ import { accessSync, constants, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, rmdirSync, statSync, writeFileSync } from "node:fs";
2
+ import { chmod, copyFile, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
5
- import { pathToFileURL } from "node:url";
6
- import { spawn } from "node:child_process";
5
+ import { spawn, spawnSync } from "node:child_process";
7
6
  import { randomBytes } from "node:crypto";
8
7
  import { once } from "node:events";
9
8
  import { text } from "node:stream/consumers";
10
- //#region ../utils/dist/artifacts.js
11
- /**
12
- * Set by `crust build` while it prepares the Command Snapshot: the absolute
13
- * entry-isolated directory Extension build hooks write into. Core reads it
14
- * to run the hooks; `resolveArtifactDir` reads it so sections evaluated during
15
- * that run see the artifacts being built instead of the wiped `.crust/root`.
16
- */
17
- const BUILD_OUT_DIR_ENV = "CRUST_INTERNAL_BUILD_OUT_DIR";
18
- //#endregion
19
- //#region ../core/dist/invocation-ka-BNJKZ.js
20
- /**
21
- * A typed error for runtime recipe, Extension, Context, documentation, argv, and validation failures.
22
- *
23
- * Every `CrustError` carries a {@link CrustErrorCode} that identifies the specific
24
- * failure, enabling programmatic error handling without fragile message parsing.
25
- *
26
- * @example
27
- * ```ts
28
- * import { CrustError } from "@crustjs/core";
29
- *
30
- * const outcome = await app.run(["deploy"], { args: { target: "prod" } });
31
- * if (outcome.status === "failed") {
32
- * const err = outcome.error;
33
- * if (err instanceof CrustError) {
34
- * console.error(`[${err.code}] ${err.message}`);
35
- * }
36
- * }
37
- * ```
38
- */
39
- var CrustError = class extends Error {
40
- /** Machine-readable error code for programmatic handling */
41
- code;
42
- /** Structured payload for programmatic handling */
43
- details;
44
- /** Optional wrapped original error/value */
45
- cause;
46
- constructor(code, message, ...details) {
47
- super(message);
48
- this.name = "CrustError";
49
- this.code = code;
50
- this.details = details[0];
51
- }
52
- is(code) {
53
- return Object.is(this.code, code);
54
- }
55
- withCause(cause) {
56
- this.cause = cause;
57
- return this;
58
- }
59
- toJSON() {
60
- return {
61
- code: this.code,
62
- message: this.message,
63
- details: this.details
64
- };
9
+ import { fileURLToPath, pathToFileURL } from "node:url";
10
+ //#region \0rolldown/runtime.js
11
+ var __create = Object.create;
12
+ var __defProp$1 = Object.defineProperty;
13
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
14
+ var __getOwnPropNames = Object.getOwnPropertyNames;
15
+ var __getProtoOf = Object.getPrototypeOf;
16
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
17
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
20
+ key = keys[i];
21
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp$1(to, key, {
22
+ get: ((k) => from[k]).bind(null, key),
23
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
24
+ });
65
25
  }
26
+ return to;
66
27
  };
67
- globalThis.AsyncDisposableStack;
68
- Object.freeze({ [Symbol("crust.finished")]: true });
69
- function invalidSections({ subject, name }) {
70
- return new CrustError("DEFINITION", `${subject === "command" ? "Command" : "Extension"} "${name}" contains invalid documentation sections`, {
71
- subject,
72
- name,
73
- reason: "invalid-sections"
74
- });
75
- }
76
- function normalizeSection(section, owner) {
77
- const { title, body, only, except } = section;
78
- if (!title.trim() || /[\r\n]/.test(title) || !body.trim() || only?.length === 0 || except?.length === 0 || only !== void 0 && except !== void 0) throw invalidSections(owner);
79
- const audience = (ids) => {
80
- return Object.freeze(ids.map((consumer) => typeof consumer === "string" ? consumer : consumer.id));
81
- };
82
- return Object.freeze({
83
- title,
84
- body,
85
- ...only ? { only: audience(only) } : except ? { except: audience(except) } : {}
86
- });
87
- }
88
- function validateCommandSections(name, sections) {
89
- return sections.map((section) => normalizeSection(section, {
90
- subject: "command",
91
- name
92
- }));
93
- }
94
- /**
95
- * Snapshot subprocess protocol used by first-party build tooling.
96
- *
97
- * When set to a non-empty file path, `.execute()` prepares the command tree,
98
- * validates its documentation sections, optionally runs Extension build hooks when the
99
- * build output directory is set, writes its final JSON snapshot and Build Report, and exits
100
- * without dispatching a Command Action. In-process callers use `Crust.snapshot()`.
101
- *
102
- * Only source entries run by `crust build` honor it: finished Bun/Node bundles carry
103
- * `process.env.CRUST_INTERNAL_BUILD === "1"` as a literal and compile the protocol out.
104
- */
105
- const SNAPSHOT_PATH_ENV = "CRUST_INTERNAL_SNAPSHOT_PATH";
106
- //#endregion
107
- //#region ../core/dist/index.js
108
- /** Mint an Extension identity from any non-blank, trimmed string. */
109
- function defineExtensionId(id) {
110
- if (!id.trim() || id !== id.trim()) throw new CrustError("DEFINITION", "Extension id must be a non-empty, trimmed string", {
111
- subject: "extension",
112
- reason: "empty-id"
113
- });
114
- return id;
115
- }
116
- const commandDefinitionInternal = Symbol.for("crust.commandDefinition");
117
- function resolveCommandName(name, aliases = []) {
118
- if (name.trim() === "") throw new CrustError("DEFINITION", "Command name must be a non-empty string", {
119
- subject: "command",
120
- name,
121
- reason: "empty-name"
122
- });
123
- if (name === "__proto__") throw new CrustError("DEFINITION", "Command name \"__proto__\" is reserved", {
124
- subject: "command",
125
- name,
126
- reason: "reserved-name"
127
- });
128
- if (aliases.includes(name)) throw new CrustError("DEFINITION", `Command "${name}" must not list its own canonical name as an alias`, {
129
- subject: "command",
130
- name,
131
- reason: "alias-collision"
132
- });
133
- return name;
134
- }
135
- function isCommandRecipe(value) {
136
- return typeof value === "function";
137
- }
138
- function defineCommand(nameInput, configOrRecipe, maybeRecipe) {
139
- const hasConfig = !isCommandRecipe(configOrRecipe);
140
- const config = hasConfig ? configOrRecipe : {};
141
- const recipe = hasConfig ? maybeRecipe : configOrRecipe;
142
- const name = resolveCommandName(nameInput, config.aliases);
143
- for (const alias of config.aliases ?? []) if (alias === "" || /[ \t\n\r\v\f]/.test(alias) || alias.startsWith("-")) throw new CrustError("DEFINITION", `Command "${name}" has an invalid alias "${alias}"`);
144
- const { sections, version: _rootVersion, ...metaRest } = config;
145
- const meta = {
146
- ...metaRest,
147
- ...config.aliases ? { aliases: [...config.aliases] } : {},
148
- ...sections ? { sections: validateCommandSections(name, sections) } : {}
149
- };
150
- const internal = {
151
- name,
152
- recipe,
153
- meta
154
- };
155
- const named = (defName) => {
156
- return Object.freeze({
157
- name: defName,
158
- as: (newName) => named(resolveCommandName(newName, meta.aliases)),
159
- [commandDefinitionInternal]: Object.freeze({
160
- ...internal,
161
- name: defName
162
- })
163
- });
164
- };
165
- return named(name);
166
- }
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp$1(target, "default", {
29
+ value: mod,
30
+ enumerable: true
31
+ }) : target, mod));
167
32
  //#endregion
168
33
  //#region ../style/dist/index.js
169
34
  var __defProp = Object.defineProperty;
@@ -1463,15 +1328,7 @@ function linkCode(url, options) {
1463
1328
  function link$1(text, url, options) {
1464
1329
  return applyStyle(text, linkCode(url, options));
1465
1330
  }
1466
- const dynamicColorKinds = [[
1467
- "fg",
1468
- fgPairAtDepth,
1469
- fg$1
1470
- ], [
1471
- "bg",
1472
- bgPairAtDepth,
1473
- bg$1
1474
- ]];
1331
+ const dynamicColorKinds = [["fg", fg$1], ["bg", bg$1]];
1475
1332
  const styleMethodPairs = ansiCodes_exports;
1476
1333
  function stepIsModifier(step) {
1477
1334
  return step.kind === "named" && isModifierName(step.name);
@@ -1480,6 +1337,21 @@ function stepPair(step, colorDepth) {
1480
1337
  if (step.kind === "named") return styleMethodPairs[step.name];
1481
1338
  return step.kind === "fg" ? fgPairAtDepth(step.input, colorDepth) : bgPairAtDepth(step.input, colorDepth);
1482
1339
  }
1340
+ function colorStep(kind, input) {
1341
+ const step = {
1342
+ kind,
1343
+ input
1344
+ };
1345
+ stepPair(step, "truecolor");
1346
+ return Array.isArray(input) ? {
1347
+ kind,
1348
+ input: [
1349
+ input[0],
1350
+ input[1],
1351
+ input[2]
1352
+ ]
1353
+ } : step;
1354
+ }
1483
1355
  function applyChain(text, steps, resolveCapabilities) {
1484
1356
  if (text == null) return "";
1485
1357
  let result = String(text);
@@ -1527,16 +1399,10 @@ function buildChainableStyleFactory(resolveCapabilities, runtime) {
1527
1399
  }]);
1528
1400
  }
1529
1401
  });
1530
- for (const [kind, pairAtDepth] of dynamicColorKinds) Object.defineProperty(styleFn, kind, {
1402
+ for (const [kind] of dynamicColorKinds) Object.defineProperty(styleFn, kind, {
1531
1403
  configurable: false,
1532
1404
  enumerable: true,
1533
- value: (input) => {
1534
- pairAtDepth(input, "truecolor");
1535
- return createChainableStyle([...steps, {
1536
- kind,
1537
- input
1538
- }]);
1539
- },
1405
+ value: (input) => createChainableStyle([...steps, colorStep(kind, input)]),
1540
1406
  writable: false
1541
1407
  });
1542
1408
  let open = "";
@@ -1642,15 +1508,9 @@ function createStyleInstance(options, runtime) {
1642
1508
  linkCode(url, hyperlinkOptions);
1643
1509
  return text;
1644
1510
  },
1645
- ...Object.fromEntries(dynamicColorKinds.map(([kind, pairAtDepth, paint]) => [kind, (...args) => {
1511
+ ...Object.fromEntries(dynamicColorKinds.map(([kind, paint]) => [kind, (...args) => {
1646
1512
  const resolved = resolveCapabilities();
1647
- if (args.length === 1) {
1648
- pairAtDepth(args[0], "truecolor");
1649
- return createChainableStyle([{
1650
- kind,
1651
- input: args[0]
1652
- }]);
1653
- }
1513
+ if (args.length === 1) return createChainableStyle([colorStep(kind, args[0])]);
1654
1514
  return paint(args[0], args[1], resolved.colorDepth);
1655
1515
  }])),
1656
1516
  ...methods
@@ -1717,6 +1577,12 @@ style.link;
1717
1577
  style.fg;
1718
1578
  style.bg;
1719
1579
  //#endregion
1580
+ //#region ../utils/dist/error.js
1581
+ /** Narrows a caught Node.js system error to its stable errno contract. */
1582
+ function isErrnoException(error) {
1583
+ return error instanceof Error && "code" in error && typeof error.code === "string";
1584
+ }
1585
+ //#endregion
1720
1586
  //#region ../utils/dist/json.js
1721
1587
  /** Narrow a JSON document root to an object (not array/scalar/null). */
1722
1588
  function isJsonObject(value) {
@@ -1730,10 +1596,96 @@ function isWithin(parent, child) {
1730
1596
  return path === "" || !isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`);
1731
1597
  }
1732
1598
  //#endregion
1733
- //#region ../utils/dist/error.js
1734
- /** Narrows a caught Node.js system error to its stable errno contract. */
1735
- function isErrnoException(error) {
1736
- return error instanceof Error && "code" in error && typeof error.code === "string";
1599
+ //#region ../utils/dist/artifacts.js
1600
+ /**
1601
+ * Set by `crust build` while it prepares the Command Snapshot: the absolute
1602
+ * entry-isolated directory Extension build hooks write into. Core reads it
1603
+ * to run the hooks; `resolveArtifactDir` reads it so sections evaluated during
1604
+ * that run see the artifacts being built instead of the wiped `.crust/root`.
1605
+ */
1606
+ const BUILD_OUT_DIR_ENV = "CRUST_INTERNAL_BUILD_OUT_DIR";
1607
+ /**
1608
+ * `Symbol.for` registry key of the packaged-build marker, for bundlers without a
1609
+ * `define` option (native `deno bundle`). `crust build` prepends
1610
+ * `globalThis[Symbol.for(PACKAGED_BUILD_KEY)] = true` to the command bundle,
1611
+ * before the inlined application graph, so every bundled copy sees it.
1612
+ * The registry key is shared across module copies; the value is never read from
1613
+ * the environment, so detection needs no Deno env permission.
1614
+ */
1615
+ const PACKAGED_BUILD_KEY = "crustjs.packagedBuild";
1616
+ //#endregion
1617
+ //#region ../core/dist/invocation-CD299Gic.js
1618
+ /**
1619
+ * A typed error for runtime recipe, Extension, Context, documentation, argv, and validation failures.
1620
+ *
1621
+ * Every `CrustError` carries a {@link CrustErrorCode} that identifies the specific
1622
+ * failure, enabling programmatic error handling without fragile message parsing.
1623
+ *
1624
+ * @example
1625
+ * ```ts
1626
+ * import { CrustError } from "@crustjs/core";
1627
+ *
1628
+ * const outcome = await app.run(["deploy"], { args: { target: "prod" } });
1629
+ * if (outcome.status === "failed") {
1630
+ * const err = outcome.error;
1631
+ * if (err instanceof CrustError) {
1632
+ * console.error(`[${err.code}] ${err.message}`);
1633
+ * }
1634
+ * }
1635
+ * ```
1636
+ */
1637
+ var CrustError = class extends Error {
1638
+ /** Machine-readable error code for programmatic handling */
1639
+ code;
1640
+ /** Structured payload for programmatic handling */
1641
+ details;
1642
+ /** Optional wrapped original error/value */
1643
+ cause;
1644
+ constructor(code, message, ...details) {
1645
+ super(message);
1646
+ this.name = "CrustError";
1647
+ this.code = code;
1648
+ this.details = details[0];
1649
+ }
1650
+ is(code) {
1651
+ return Object.is(this.code, code);
1652
+ }
1653
+ withCause(cause) {
1654
+ this.cause = cause;
1655
+ return this;
1656
+ }
1657
+ toJSON() {
1658
+ return {
1659
+ code: this.code,
1660
+ message: this.message,
1661
+ details: this.details
1662
+ };
1663
+ }
1664
+ };
1665
+ Object.freeze({ [Symbol("crust.finished")]: true });
1666
+ /**
1667
+ * Snapshot subprocess protocol used by first-party build tooling.
1668
+ *
1669
+ * When set to a non-empty file path, `.execute()` prepares the command tree,
1670
+ * validates its documentation sections, optionally runs Extension build hooks when the
1671
+ * build output directory is set, writes its final JSON snapshot and Build Report, and exits
1672
+ * without dispatching a Command Action. In-process callers use `Crust.snapshot()`.
1673
+ *
1674
+ * Only source entries run by `crust build` honor it: finished Bun/Node bundles carry
1675
+ * `process.env.CRUST_INTERNAL_BUILD === "1"` as a literal and compile the protocol out;
1676
+ * packaged Deno bundles set the `isPackagedBuild()` marker and skip it without
1677
+ * reading the environment.
1678
+ */
1679
+ const SNAPSHOT_PATH_ENV = "CRUST_INTERNAL_SNAPSHOT_PATH";
1680
+ //#endregion
1681
+ //#region ../core/dist/index.js
1682
+ /** Mint an Extension identity from any non-blank, trimmed string. */
1683
+ function defineExtensionId(id) {
1684
+ if (!id.trim() || id !== id.trim()) throw new CrustError("DEFINITION", "Extension id must be a non-empty, trimmed string", {
1685
+ subject: "extension",
1686
+ reason: "empty-id"
1687
+ });
1688
+ return id;
1737
1689
  }
1738
1690
  //#endregion
1739
1691
  //#region ../utils/dist/process.js
@@ -1766,11 +1718,42 @@ function getWindowsShimCommand(command, args, shell, platform = process.platform
1766
1718
  };
1767
1719
  }
1768
1720
  /**
1721
+ * @internal Kill a POSIX process group (spawned with `detached: true`) or a
1722
+ * live Windows process tree. Descendants that leave the group are not reached.
1723
+ * If POSIX group signaling fails, try the direct child before rethrowing.
1724
+ */
1725
+ function killProcessTree(child) {
1726
+ if (child.pid === void 0) return;
1727
+ if (process.platform === "win32") {
1728
+ if (child.exitCode === null && child.signalCode === null) {
1729
+ const result = spawnSync("taskkill", [
1730
+ "/pid",
1731
+ String(child.pid),
1732
+ "/T",
1733
+ "/F"
1734
+ ], {
1735
+ stdio: "ignore",
1736
+ timeout: 1e4
1737
+ });
1738
+ if (result.error || result.status !== 0) child.kill("SIGKILL");
1739
+ }
1740
+ return;
1741
+ }
1742
+ try {
1743
+ process.kill(-child.pid, "SIGKILL");
1744
+ } catch (error) {
1745
+ if (error.code === "ESRCH") return;
1746
+ child.kill("SIGKILL");
1747
+ throw error;
1748
+ }
1749
+ }
1750
+ /**
1769
1751
  * Spawn a process and wait for it to exit without imposing an error policy.
1770
1752
  *
1771
- * On Windows, the automatic `.cmd`/`.bat` workaround escapes arguments for
1772
- * `cmd.exe`; unsafe shell-expansion characters throw before spawning. Explicit
1773
- * `shell: true` calls are passed through unchanged and own their shell escaping.
1753
+ * On Windows, the automatic `.cmd`/`.bat` workaround supports command shims
1754
+ * forwarding arguments through `%*`, not arbitrary batch files consuming `%1`.
1755
+ * Unsafe shell-expansion characters throw before spawning. Explicit `shell: true`
1756
+ * calls are passed through unchanged and own their shell escaping.
1774
1757
  */
1775
1758
  async function runProcess(command, args = [], options = {}) {
1776
1759
  const windowsShimCommand = getWindowsShimCommand(command, args, options.shell);
@@ -1785,18 +1768,39 @@ async function runProcess(command, args = [], options = {}) {
1785
1768
  collectStdout ? "pipe" : "ignore",
1786
1769
  "pipe"
1787
1770
  ] : "inherit",
1771
+ detached: options.timeout !== void 0 && process.platform !== "win32",
1788
1772
  windowsVerbatimArguments: windowsShimCommand?.windowsVerbatimArguments
1789
1773
  });
1790
- const [stdout, stderr, [exitCode]] = await Promise.all([
1791
- collectStdout ? text(proc.stdout) : "",
1792
- collect ? text(proc.stderr) : "",
1793
- once(proc, "close")
1794
- ]);
1795
- return {
1796
- exitCode,
1797
- stdout,
1798
- stderr
1799
- };
1774
+ let timer;
1775
+ const deadline = new Promise((_, reject) => {
1776
+ if (options.timeout === void 0) return;
1777
+ const timeout = options.timeout;
1778
+ timer = setTimeout(() => {
1779
+ let failure = /* @__PURE__ */ new Error(`${[command, ...args].join(" ")} did not exit within ${timeout} ms and was killed.`);
1780
+ try {
1781
+ killProcessTree(proc);
1782
+ } catch (cause) {
1783
+ failure = new Error("Process-tree cleanup failed after timeout.", { cause });
1784
+ }
1785
+ proc.stdout?.destroy();
1786
+ proc.stderr?.destroy();
1787
+ reject(failure);
1788
+ }, timeout);
1789
+ });
1790
+ try {
1791
+ const [stdout, stderr, [exitCode]] = await Promise.race([Promise.all([
1792
+ collectStdout ? text(proc.stdout) : "",
1793
+ collect ? text(proc.stderr) : "",
1794
+ once(proc, "close")
1795
+ ]), deadline]);
1796
+ return {
1797
+ exitCode,
1798
+ stdout,
1799
+ stderr
1800
+ };
1801
+ } finally {
1802
+ clearTimeout(timer);
1803
+ }
1800
1804
  }
1801
1805
  /** Resolve a bare executable name from PATH (and PATHEXT on Windows). */
1802
1806
  function which(command) {
@@ -1818,47 +1822,902 @@ function which(command) {
1818
1822
  return null;
1819
1823
  }
1820
1824
  //#endregion
1821
- //#region src/utils/build-helpers.ts
1822
- const BUILD_RUNTIMES = [
1823
- "bun",
1824
- "deno",
1825
- "node"
1826
- ];
1827
- const BUN_TARGETS = {
1828
- runtime: "Bun",
1829
- targets: [
1830
- "bun-linux-x64",
1831
- "bun-linux-arm64",
1832
- "bun-linux-x64-musl",
1833
- "bun-linux-arm64-musl",
1834
- "bun-darwin-x64",
1835
- "bun-darwin-arm64",
1836
- "bun-windows-x64",
1837
- "bun-windows-arm64"
1838
- ],
1839
- info: {
1840
- "bun-linux-x64": {
1841
- alias: "linux-x64",
1842
- platformKey: "linux-x64",
1843
- os: "linux",
1844
- cpu: "x64",
1845
- libc: "glibc"
1846
- },
1847
- "bun-linux-arm64": {
1848
- alias: "linux-arm64",
1849
- platformKey: "linux-arm64",
1850
- os: "linux",
1851
- cpu: "arm64",
1852
- libc: "glibc"
1853
- },
1854
- "bun-linux-x64-musl": {
1855
- alias: "linux-x64-musl",
1856
- platformKey: "linux-x64-musl",
1857
- os: "linux",
1858
- cpu: "x64",
1859
- libc: "musl"
1860
- },
1861
- "bun-linux-arm64-musl": {
1825
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/lrucache.js
1826
+ var require_lrucache = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1827
+ var LRUCache = class {
1828
+ constructor() {
1829
+ this.max = 1e3;
1830
+ this.map = /* @__PURE__ */ new Map();
1831
+ }
1832
+ get(key) {
1833
+ const value = this.map.get(key);
1834
+ if (value === void 0) return;
1835
+ else {
1836
+ this.map.delete(key);
1837
+ this.map.set(key, value);
1838
+ return value;
1839
+ }
1840
+ }
1841
+ delete(key) {
1842
+ return this.map.delete(key);
1843
+ }
1844
+ set(key, value) {
1845
+ if (!this.delete(key) && value !== void 0) {
1846
+ if (this.map.size >= this.max) {
1847
+ const firstKey = this.map.keys().next().value;
1848
+ this.delete(firstKey);
1849
+ }
1850
+ this.map.set(key, value);
1851
+ }
1852
+ return this;
1853
+ }
1854
+ };
1855
+ module.exports = LRUCache;
1856
+ }));
1857
+ //#endregion
1858
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/parse-options.js
1859
+ var require_parse_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1860
+ const looseOption = Object.freeze({ loose: true });
1861
+ const emptyOpts = Object.freeze({});
1862
+ const parseOptions = (options) => {
1863
+ if (!options) return emptyOpts;
1864
+ if (typeof options !== "object") return looseOption;
1865
+ return options;
1866
+ };
1867
+ module.exports = parseOptions;
1868
+ }));
1869
+ //#endregion
1870
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/constants.js
1871
+ var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1872
+ module.exports = {
1873
+ MAX_LENGTH: 256,
1874
+ MAX_SAFE_COMPONENT_LENGTH: 16,
1875
+ MAX_SAFE_BUILD_LENGTH: 250,
1876
+ MAX_SAFE_INTEGER: Number.MAX_SAFE_INTEGER ||
1877
+ /* istanbul ignore next */ 9007199254740991,
1878
+ RELEASE_TYPES: [
1879
+ "major",
1880
+ "premajor",
1881
+ "minor",
1882
+ "preminor",
1883
+ "patch",
1884
+ "prepatch",
1885
+ "prerelease"
1886
+ ],
1887
+ SEMVER_SPEC_VERSION: "2.0.0",
1888
+ FLAG_INCLUDE_PRERELEASE: 1,
1889
+ FLAG_LOOSE: 2
1890
+ };
1891
+ }));
1892
+ //#endregion
1893
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/debug.js
1894
+ var require_debug = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1895
+ module.exports = typeof process === "object" && process.env && process.env.NODE_DEBUG && /\bsemver\b/i.test(process.env.NODE_DEBUG) ? (...args) => console.error("SEMVER", ...args) : () => {};
1896
+ }));
1897
+ //#endregion
1898
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/re.js
1899
+ var require_re = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1900
+ const { MAX_SAFE_COMPONENT_LENGTH, MAX_SAFE_BUILD_LENGTH, MAX_LENGTH } = require_constants();
1901
+ const debug = require_debug();
1902
+ exports = module.exports = {};
1903
+ const re = exports.re = [];
1904
+ const safeRe = exports.safeRe = [];
1905
+ const src = exports.src = [];
1906
+ const safeSrc = exports.safeSrc = [];
1907
+ const t = exports.t = {};
1908
+ let R = 0;
1909
+ const LETTERDASHNUMBER = "[a-zA-Z0-9-]";
1910
+ const safeRegexReplacements = [
1911
+ ["\\s", 1],
1912
+ ["\\d", MAX_LENGTH],
1913
+ [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH]
1914
+ ];
1915
+ const makeSafeRegex = (value) => {
1916
+ for (const [token, max] of safeRegexReplacements) value = value.split(`${token}*`).join(`${token}{0,${max}}`).split(`${token}+`).join(`${token}{1,${max}}`);
1917
+ return value;
1918
+ };
1919
+ const createToken = (name, value, isGlobal) => {
1920
+ const safe = makeSafeRegex(value);
1921
+ const index = R++;
1922
+ debug(name, index, value);
1923
+ t[name] = index;
1924
+ src[index] = value;
1925
+ safeSrc[index] = safe;
1926
+ re[index] = new RegExp(value, isGlobal ? "g" : void 0);
1927
+ safeRe[index] = new RegExp(safe, isGlobal ? "g" : void 0);
1928
+ };
1929
+ createToken("NUMERICIDENTIFIER", "0|[1-9]\\d*");
1930
+ createToken("NUMERICIDENTIFIERLOOSE", "\\d+");
1931
+ createToken("NONNUMERICIDENTIFIER", `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`);
1932
+ createToken("MAINVERSION", `(${src[t.NUMERICIDENTIFIER]})\\.(${src[t.NUMERICIDENTIFIER]})\\.(${src[t.NUMERICIDENTIFIER]})`);
1933
+ createToken("MAINVERSIONLOOSE", `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.(${src[t.NUMERICIDENTIFIERLOOSE]})\\.(${src[t.NUMERICIDENTIFIERLOOSE]})`);
1934
+ createToken("PRERELEASEIDENTIFIER", `(?:${src[t.NONNUMERICIDENTIFIER]}|${src[t.NUMERICIDENTIFIER]})`);
1935
+ createToken("PRERELEASEIDENTIFIERLOOSE", `(?:${src[t.NONNUMERICIDENTIFIER]}|${src[t.NUMERICIDENTIFIERLOOSE]})`);
1936
+ createToken("PRERELEASE", `(?:-(${src[t.PRERELEASEIDENTIFIER]}(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`);
1937
+ createToken("PRERELEASELOOSE", `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]}(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`);
1938
+ createToken("BUILDIDENTIFIER", `${LETTERDASHNUMBER}+`);
1939
+ createToken("BUILD", `(?:\\+(${src[t.BUILDIDENTIFIER]}(?:\\.${src[t.BUILDIDENTIFIER]})*))`);
1940
+ createToken("FULLPLAIN", `v?${src[t.MAINVERSION]}${src[t.PRERELEASE]}?${src[t.BUILD]}?`);
1941
+ createToken("FULL", `^${src[t.FULLPLAIN]}$`);
1942
+ createToken("LOOSEPLAIN", `[v=\\s]*${src[t.MAINVERSIONLOOSE]}${src[t.PRERELEASELOOSE]}?${src[t.BUILD]}?`);
1943
+ createToken("LOOSE", `^${src[t.LOOSEPLAIN]}$`);
1944
+ createToken("GTLT", "((?:<|>)?=?)");
1945
+ createToken("XRANGEIDENTIFIERLOOSE", `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`);
1946
+ createToken("XRANGEIDENTIFIER", `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`);
1947
+ createToken("XRANGEPLAIN", `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})(?:\\.(${src[t.XRANGEIDENTIFIER]})(?:\\.(${src[t.XRANGEIDENTIFIER]})(?:${src[t.PRERELEASE]})?${src[t.BUILD]}?)?)?`);
1948
+ createToken("XRANGEPLAINLOOSE", `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})(?:${src[t.PRERELEASELOOSE]})?${src[t.BUILD]}?)?)?`);
1949
+ createToken("XRANGE", `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`);
1950
+ createToken("XRANGELOOSE", `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`);
1951
+ createToken("COERCEPLAIN", `(^|[^\\d])(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}})(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`);
1952
+ createToken("COERCE", `${src[t.COERCEPLAIN]}(?:$|[^\\d])`);
1953
+ createToken("COERCEFULL", src[t.COERCEPLAIN] + `(?:${src[t.PRERELEASE]})?(?:${src[t.BUILD]})?(?:$|[^\\d])`);
1954
+ createToken("COERCERTL", src[t.COERCE], true);
1955
+ createToken("COERCERTLFULL", src[t.COERCEFULL], true);
1956
+ createToken("LONETILDE", "(?:~>?)");
1957
+ createToken("TILDETRIM", `(\\s*)${src[t.LONETILDE]}\\s+`, true);
1958
+ exports.tildeTrimReplace = "$1~";
1959
+ createToken("TILDE", `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`);
1960
+ createToken("TILDELOOSE", `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`);
1961
+ createToken("LONECARET", "(?:\\^)");
1962
+ createToken("CARETTRIM", `(\\s*)${src[t.LONECARET]}\\s+`, true);
1963
+ exports.caretTrimReplace = "$1^";
1964
+ createToken("CARET", `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`);
1965
+ createToken("CARETLOOSE", `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`);
1966
+ createToken("COMPARATORLOOSE", `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`);
1967
+ createToken("COMPARATOR", `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`);
1968
+ createToken("COMPARATORTRIM", `(\\s*)${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true);
1969
+ exports.comparatorTrimReplace = "$1$2$3";
1970
+ createToken("HYPHENRANGE", `^\\s*(${src[t.XRANGEPLAIN]})\\s+-\\s+(${src[t.XRANGEPLAIN]})\\s*$`);
1971
+ createToken("HYPHENRANGELOOSE", `^\\s*(${src[t.XRANGEPLAINLOOSE]})\\s+-\\s+(${src[t.XRANGEPLAINLOOSE]})\\s*$`);
1972
+ createToken("STAR", "(<|>)?=?\\s*\\*");
1973
+ createToken("GTE0", "^\\s*>=\\s*0\\.0\\.0\\s*$");
1974
+ createToken("GTE0PRE", "^\\s*>=\\s*0\\.0\\.0-0\\s*$");
1975
+ }));
1976
+ //#endregion
1977
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/identifiers.js
1978
+ var require_identifiers = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1979
+ const numeric = /^[0-9]+$/;
1980
+ const compareIdentifiers = (a, b) => {
1981
+ if (typeof a === "number" && typeof b === "number") return a === b ? 0 : a < b ? -1 : 1;
1982
+ const anum = numeric.test(a);
1983
+ const bnum = numeric.test(b);
1984
+ if (anum && bnum) {
1985
+ a = +a;
1986
+ b = +b;
1987
+ }
1988
+ return a === b ? 0 : anum && !bnum ? -1 : bnum && !anum ? 1 : a < b ? -1 : 1;
1989
+ };
1990
+ const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a);
1991
+ module.exports = {
1992
+ compareIdentifiers,
1993
+ rcompareIdentifiers
1994
+ };
1995
+ }));
1996
+ //#endregion
1997
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/classes/semver.js
1998
+ var require_semver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1999
+ const debug = require_debug();
2000
+ const { MAX_LENGTH, MAX_SAFE_INTEGER } = require_constants();
2001
+ const { safeRe: re, t } = require_re();
2002
+ const parseOptions = require_parse_options();
2003
+ const { compareIdentifiers } = require_identifiers();
2004
+ const isPrereleaseIdentifier = (prerelease, identifier) => {
2005
+ const identifiers = identifier.split(".");
2006
+ if (identifiers.length > prerelease.length) return false;
2007
+ for (let i = 0; i < identifiers.length; i++) if (compareIdentifiers(prerelease[i], identifiers[i]) !== 0) return false;
2008
+ return true;
2009
+ };
2010
+ module.exports = class SemVer {
2011
+ constructor(version, options) {
2012
+ options = parseOptions(options);
2013
+ if (version instanceof SemVer) {
2014
+ if (version.loose === !!options.loose && version.includePrerelease === !!options.includePrerelease) return version;
2015
+ else version = version.version;
2016
+ } else if (typeof version !== "string") throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`);
2017
+ if (version.length > MAX_LENGTH) throw new TypeError(`version is longer than ${MAX_LENGTH} characters`);
2018
+ debug("SemVer", version, options);
2019
+ this.options = options;
2020
+ this.loose = !!options.loose;
2021
+ this.includePrerelease = !!options.includePrerelease;
2022
+ const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]);
2023
+ if (!m) throw new TypeError(`Invalid Version: ${version}`);
2024
+ this.raw = version;
2025
+ this.major = +m[1];
2026
+ this.minor = +m[2];
2027
+ this.patch = +m[3];
2028
+ if (this.major > MAX_SAFE_INTEGER || this.major < 0) throw new TypeError("Invalid major version");
2029
+ if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) throw new TypeError("Invalid minor version");
2030
+ if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) throw new TypeError("Invalid patch version");
2031
+ if (!m[4]) this.prerelease = [];
2032
+ else this.prerelease = m[4].split(".").map((id) => {
2033
+ if (/^[0-9]+$/.test(id)) {
2034
+ const num = +id;
2035
+ if (num >= 0 && num < MAX_SAFE_INTEGER) return num;
2036
+ }
2037
+ return id;
2038
+ });
2039
+ this.build = m[5] ? m[5].split(".") : [];
2040
+ this.format();
2041
+ }
2042
+ format() {
2043
+ this.version = `${this.major}.${this.minor}.${this.patch}`;
2044
+ if (this.prerelease.length) this.version += `-${this.prerelease.join(".")}`;
2045
+ return this.version;
2046
+ }
2047
+ toString() {
2048
+ return this.version;
2049
+ }
2050
+ compare(other) {
2051
+ debug("SemVer.compare", this.version, this.options, other);
2052
+ if (!(other instanceof SemVer)) {
2053
+ if (typeof other === "string" && other === this.version) return 0;
2054
+ other = new SemVer(other, this.options);
2055
+ }
2056
+ if (other.version === this.version) return 0;
2057
+ return this.compareMain(other) || this.comparePre(other);
2058
+ }
2059
+ compareMain(other) {
2060
+ if (!(other instanceof SemVer)) other = new SemVer(other, this.options);
2061
+ if (this.major < other.major) return -1;
2062
+ if (this.major > other.major) return 1;
2063
+ if (this.minor < other.minor) return -1;
2064
+ if (this.minor > other.minor) return 1;
2065
+ if (this.patch < other.patch) return -1;
2066
+ if (this.patch > other.patch) return 1;
2067
+ return 0;
2068
+ }
2069
+ comparePre(other) {
2070
+ if (!(other instanceof SemVer)) other = new SemVer(other, this.options);
2071
+ if (this.prerelease.length && !other.prerelease.length) return -1;
2072
+ else if (!this.prerelease.length && other.prerelease.length) return 1;
2073
+ else if (!this.prerelease.length && !other.prerelease.length) return 0;
2074
+ let i = 0;
2075
+ do {
2076
+ const a = this.prerelease[i];
2077
+ const b = other.prerelease[i];
2078
+ debug("prerelease compare", i, a, b);
2079
+ if (a === void 0 && b === void 0) return 0;
2080
+ else if (b === void 0) return 1;
2081
+ else if (a === void 0) return -1;
2082
+ else if (a === b) continue;
2083
+ else return compareIdentifiers(a, b);
2084
+ } while (++i);
2085
+ }
2086
+ compareBuild(other) {
2087
+ if (!(other instanceof SemVer)) other = new SemVer(other, this.options);
2088
+ let i = 0;
2089
+ do {
2090
+ const a = this.build[i];
2091
+ const b = other.build[i];
2092
+ debug("build compare", i, a, b);
2093
+ if (a === void 0 && b === void 0) return 0;
2094
+ else if (b === void 0) return 1;
2095
+ else if (a === void 0) return -1;
2096
+ else if (a === b) continue;
2097
+ else return compareIdentifiers(a, b);
2098
+ } while (++i);
2099
+ }
2100
+ inc(release, identifier, identifierBase) {
2101
+ if (release.startsWith("pre")) {
2102
+ if (!identifier && identifierBase === false) throw new Error("invalid increment argument: identifier is empty");
2103
+ if (identifier) {
2104
+ const match = `-${identifier}`.match(this.options.loose ? re[t.PRERELEASELOOSE] : re[t.PRERELEASE]);
2105
+ if (!match || match[1] !== identifier) throw new Error(`invalid identifier: ${identifier}`);
2106
+ }
2107
+ }
2108
+ switch (release) {
2109
+ case "premajor":
2110
+ this.prerelease.length = 0;
2111
+ this.patch = 0;
2112
+ this.minor = 0;
2113
+ this.major++;
2114
+ this.inc("pre", identifier, identifierBase);
2115
+ break;
2116
+ case "preminor":
2117
+ this.prerelease.length = 0;
2118
+ this.patch = 0;
2119
+ this.minor++;
2120
+ this.inc("pre", identifier, identifierBase);
2121
+ break;
2122
+ case "prepatch":
2123
+ this.prerelease.length = 0;
2124
+ this.inc("patch", identifier, identifierBase);
2125
+ this.inc("pre", identifier, identifierBase);
2126
+ break;
2127
+ case "prerelease":
2128
+ if (this.prerelease.length === 0) this.inc("patch", identifier, identifierBase);
2129
+ this.inc("pre", identifier, identifierBase);
2130
+ break;
2131
+ case "release":
2132
+ if (this.prerelease.length === 0) throw new Error(`version ${this.raw} is not a prerelease`);
2133
+ this.prerelease.length = 0;
2134
+ break;
2135
+ case "major":
2136
+ if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0) this.major++;
2137
+ this.minor = 0;
2138
+ this.patch = 0;
2139
+ this.prerelease = [];
2140
+ break;
2141
+ case "minor":
2142
+ if (this.patch !== 0 || this.prerelease.length === 0) this.minor++;
2143
+ this.patch = 0;
2144
+ this.prerelease = [];
2145
+ break;
2146
+ case "patch":
2147
+ if (this.prerelease.length === 0) this.patch++;
2148
+ this.prerelease = [];
2149
+ break;
2150
+ case "pre": {
2151
+ const base = Number(identifierBase) ? 1 : 0;
2152
+ if (this.prerelease.length === 0) this.prerelease = [base];
2153
+ else {
2154
+ let i = this.prerelease.length;
2155
+ while (--i >= 0) if (typeof this.prerelease[i] === "number") {
2156
+ this.prerelease[i]++;
2157
+ i = -2;
2158
+ }
2159
+ if (i === -1) {
2160
+ if (identifier === this.prerelease.join(".") && identifierBase === false) throw new Error("invalid increment argument: identifier already exists");
2161
+ this.prerelease.push(base);
2162
+ }
2163
+ }
2164
+ if (identifier) {
2165
+ let prerelease = [identifier, base];
2166
+ if (identifierBase === false) prerelease = [identifier];
2167
+ if (isPrereleaseIdentifier(this.prerelease, identifier)) {
2168
+ const prereleaseBase = this.prerelease[identifier.split(".").length];
2169
+ if (isNaN(prereleaseBase)) this.prerelease = prerelease;
2170
+ } else this.prerelease = prerelease;
2171
+ }
2172
+ break;
2173
+ }
2174
+ default: throw new Error(`invalid increment argument: ${release}`);
2175
+ }
2176
+ this.raw = this.format();
2177
+ if (this.build.length) this.raw += `+${this.build.join(".")}`;
2178
+ return this;
2179
+ }
2180
+ };
2181
+ }));
2182
+ //#endregion
2183
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/compare.js
2184
+ var require_compare = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2185
+ const SemVer = require_semver();
2186
+ const compare = (a, b, loose) => new SemVer(a, loose).compare(new SemVer(b, loose));
2187
+ module.exports = compare;
2188
+ }));
2189
+ //#endregion
2190
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/eq.js
2191
+ var require_eq = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2192
+ const compare = require_compare();
2193
+ const eq = (a, b, loose) => compare(a, b, loose) === 0;
2194
+ module.exports = eq;
2195
+ }));
2196
+ //#endregion
2197
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/neq.js
2198
+ var require_neq = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2199
+ const compare = require_compare();
2200
+ const neq = (a, b, loose) => compare(a, b, loose) !== 0;
2201
+ module.exports = neq;
2202
+ }));
2203
+ //#endregion
2204
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/gt.js
2205
+ var require_gt = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2206
+ const compare = require_compare();
2207
+ const gt = (a, b, loose) => compare(a, b, loose) > 0;
2208
+ module.exports = gt;
2209
+ }));
2210
+ //#endregion
2211
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/gte.js
2212
+ var require_gte = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2213
+ const compare = require_compare();
2214
+ const gte = (a, b, loose) => compare(a, b, loose) >= 0;
2215
+ module.exports = gte;
2216
+ }));
2217
+ //#endregion
2218
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/lt.js
2219
+ var require_lt = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2220
+ const compare = require_compare();
2221
+ const lt = (a, b, loose) => compare(a, b, loose) < 0;
2222
+ module.exports = lt;
2223
+ }));
2224
+ //#endregion
2225
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/lte.js
2226
+ var require_lte = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2227
+ const compare = require_compare();
2228
+ const lte = (a, b, loose) => compare(a, b, loose) <= 0;
2229
+ module.exports = lte;
2230
+ }));
2231
+ //#endregion
2232
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/cmp.js
2233
+ var require_cmp = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2234
+ const eq = require_eq();
2235
+ const neq = require_neq();
2236
+ const gt = require_gt();
2237
+ const gte = require_gte();
2238
+ const lt = require_lt();
2239
+ const lte = require_lte();
2240
+ const cmp = (a, op, b, loose) => {
2241
+ switch (op) {
2242
+ case "===":
2243
+ if (typeof a === "object") a = a.version;
2244
+ if (typeof b === "object") b = b.version;
2245
+ return a === b;
2246
+ case "!==":
2247
+ if (typeof a === "object") a = a.version;
2248
+ if (typeof b === "object") b = b.version;
2249
+ return a !== b;
2250
+ case "":
2251
+ case "=":
2252
+ case "==": return eq(a, b, loose);
2253
+ case "!=": return neq(a, b, loose);
2254
+ case ">": return gt(a, b, loose);
2255
+ case ">=": return gte(a, b, loose);
2256
+ case "<": return lt(a, b, loose);
2257
+ case "<=": return lte(a, b, loose);
2258
+ default: throw new TypeError(`Invalid operator: ${op}`);
2259
+ }
2260
+ };
2261
+ module.exports = cmp;
2262
+ }));
2263
+ //#endregion
2264
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/classes/comparator.js
2265
+ var require_comparator = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2266
+ const ANY = Symbol("SemVer ANY");
2267
+ module.exports = class Comparator {
2268
+ static get ANY() {
2269
+ return ANY;
2270
+ }
2271
+ constructor(comp, options) {
2272
+ options = parseOptions(options);
2273
+ if (comp instanceof Comparator) {
2274
+ if (comp.loose === !!options.loose) return comp;
2275
+ else comp = comp.value;
2276
+ }
2277
+ comp = comp.trim().split(/\s+/).join(" ");
2278
+ debug("comparator", comp, options);
2279
+ this.options = options;
2280
+ this.loose = !!options.loose;
2281
+ this.parse(comp);
2282
+ if (this.semver === ANY) this.value = "";
2283
+ else this.value = this.operator + this.semver.version;
2284
+ debug("comp", this);
2285
+ }
2286
+ parse(comp) {
2287
+ const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR];
2288
+ const m = comp.match(r);
2289
+ if (!m) throw new TypeError(`Invalid comparator: ${comp}`);
2290
+ this.operator = m[1] !== void 0 ? m[1] : "";
2291
+ if (this.operator === "=") this.operator = "";
2292
+ if (!m[2]) this.semver = ANY;
2293
+ else this.semver = new SemVer(m[2], this.options.loose);
2294
+ }
2295
+ toString() {
2296
+ return this.value;
2297
+ }
2298
+ test(version) {
2299
+ debug("Comparator.test", version, this.options.loose);
2300
+ if (this.semver === ANY || version === ANY) return true;
2301
+ if (typeof version === "string") try {
2302
+ version = new SemVer(version, this.options);
2303
+ } catch (er) {
2304
+ return false;
2305
+ }
2306
+ return cmp(version, this.operator, this.semver, this.options);
2307
+ }
2308
+ intersects(comp, options) {
2309
+ if (!(comp instanceof Comparator)) throw new TypeError("a Comparator is required");
2310
+ if (this.operator === "") {
2311
+ if (this.value === "") return true;
2312
+ return new Range(comp.value, options).test(this.value);
2313
+ } else if (comp.operator === "") {
2314
+ if (comp.value === "") return true;
2315
+ return new Range(this.value, options).test(comp.semver);
2316
+ }
2317
+ options = parseOptions(options);
2318
+ if (options.includePrerelease && (this.value === "<0.0.0-0" || comp.value === "<0.0.0-0")) return false;
2319
+ if (!options.includePrerelease && (this.value.startsWith("<0.0.0") || comp.value.startsWith("<0.0.0"))) return false;
2320
+ if (this.operator.startsWith(">") && comp.operator.startsWith(">")) return true;
2321
+ if (this.operator.startsWith("<") && comp.operator.startsWith("<")) return true;
2322
+ if (this.semver.version === comp.semver.version && this.operator.includes("=") && comp.operator.includes("=")) return true;
2323
+ if (cmp(this.semver, "<", comp.semver, options) && this.operator.startsWith(">") && comp.operator.startsWith("<")) return true;
2324
+ if (cmp(this.semver, ">", comp.semver, options) && this.operator.startsWith("<") && comp.operator.startsWith(">")) return true;
2325
+ return false;
2326
+ }
2327
+ };
2328
+ const parseOptions = require_parse_options();
2329
+ const { safeRe: re, t } = require_re();
2330
+ const cmp = require_cmp();
2331
+ const debug = require_debug();
2332
+ const SemVer = require_semver();
2333
+ const Range = require_range();
2334
+ }));
2335
+ //#endregion
2336
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/classes/range.js
2337
+ var require_range = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2338
+ const SPACE_CHARACTERS = /\s+/g;
2339
+ module.exports = class Range {
2340
+ constructor(range, options) {
2341
+ options = parseOptions(options);
2342
+ if (range instanceof Range) {
2343
+ if (range.loose === !!options.loose && range.includePrerelease === !!options.includePrerelease) return range;
2344
+ else return new Range(range.raw, options);
2345
+ }
2346
+ if (range instanceof Comparator) {
2347
+ this.raw = range.value;
2348
+ this.set = [[range]];
2349
+ this.formatted = void 0;
2350
+ return this;
2351
+ }
2352
+ this.options = options;
2353
+ this.loose = !!options.loose;
2354
+ this.includePrerelease = !!options.includePrerelease;
2355
+ this.raw = range.trim().replace(SPACE_CHARACTERS, " ");
2356
+ this.set = this.raw.split("||").map((r) => this.parseRange(r.trim())).filter((c) => c.length);
2357
+ if (!this.set.length) throw new TypeError(`Invalid SemVer Range: ${this.raw}`);
2358
+ if (this.set.length > 1) {
2359
+ const first = this.set[0];
2360
+ this.set = this.set.filter((c) => !isNullSet(c[0]));
2361
+ if (this.set.length === 0) this.set = [first];
2362
+ else if (this.set.length > 1) {
2363
+ for (const c of this.set) if (c.length === 1 && isAny(c[0])) {
2364
+ this.set = [c];
2365
+ break;
2366
+ }
2367
+ }
2368
+ }
2369
+ this.formatted = void 0;
2370
+ }
2371
+ get range() {
2372
+ if (this.formatted === void 0) {
2373
+ this.formatted = "";
2374
+ for (let i = 0; i < this.set.length; i++) {
2375
+ if (i > 0) this.formatted += "||";
2376
+ const comps = this.set[i];
2377
+ for (let k = 0; k < comps.length; k++) {
2378
+ if (k > 0) this.formatted += " ";
2379
+ this.formatted += comps[k].toString().trim();
2380
+ }
2381
+ }
2382
+ }
2383
+ return this.formatted;
2384
+ }
2385
+ format() {
2386
+ return this.range;
2387
+ }
2388
+ toString() {
2389
+ return this.range;
2390
+ }
2391
+ parseRange(range) {
2392
+ range = range.replace(BUILDSTRIPRE, "");
2393
+ const memoKey = ((this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) | (this.options.loose && FLAG_LOOSE)) + ":" + range;
2394
+ const cached = cache.get(memoKey);
2395
+ if (cached) return cached;
2396
+ const loose = this.options.loose;
2397
+ const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE];
2398
+ range = range.replace(hr, hyphenReplace(this.options.includePrerelease));
2399
+ debug("hyphen replace", range);
2400
+ range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace);
2401
+ debug("comparator trim", range);
2402
+ range = range.replace(re[t.TILDETRIM], tildeTrimReplace);
2403
+ debug("tilde trim", range);
2404
+ range = range.replace(re[t.CARETTRIM], caretTrimReplace);
2405
+ debug("caret trim", range);
2406
+ let rangeList = range.split(" ").map((comp) => parseComparator(comp, this.options)).join(" ").split(/\s+/).map((comp) => replaceGTE0(comp, this.options));
2407
+ if (loose) rangeList = rangeList.filter((comp) => {
2408
+ debug("loose invalid filter", comp, this.options);
2409
+ return !!comp.match(re[t.COMPARATORLOOSE]);
2410
+ });
2411
+ debug("range list", rangeList);
2412
+ const rangeMap = /* @__PURE__ */ new Map();
2413
+ const comparators = rangeList.map((comp) => new Comparator(comp, this.options));
2414
+ for (const comp of comparators) {
2415
+ if (isNullSet(comp)) return [comp];
2416
+ rangeMap.set(comp.value, comp);
2417
+ }
2418
+ if (rangeMap.size > 1 && rangeMap.has("")) rangeMap.delete("");
2419
+ const result = [...rangeMap.values()];
2420
+ cache.set(memoKey, result);
2421
+ return result;
2422
+ }
2423
+ intersects(range, options) {
2424
+ if (!(range instanceof Range)) throw new TypeError("a Range is required");
2425
+ return this.set.some((thisComparators) => {
2426
+ return isSatisfiable(thisComparators, options) && range.set.some((rangeComparators) => {
2427
+ return isSatisfiable(rangeComparators, options) && thisComparators.every((thisComparator) => {
2428
+ return rangeComparators.every((rangeComparator) => {
2429
+ return thisComparator.intersects(rangeComparator, options);
2430
+ });
2431
+ });
2432
+ });
2433
+ });
2434
+ }
2435
+ test(version) {
2436
+ if (!version) return false;
2437
+ if (typeof version === "string") try {
2438
+ version = new SemVer(version, this.options);
2439
+ } catch (er) {
2440
+ return false;
2441
+ }
2442
+ for (let i = 0; i < this.set.length; i++) if (testSet(this.set[i], version, this.options)) return true;
2443
+ return false;
2444
+ }
2445
+ };
2446
+ const cache = new (require_lrucache())();
2447
+ const parseOptions = require_parse_options();
2448
+ const Comparator = require_comparator();
2449
+ const debug = require_debug();
2450
+ const SemVer = require_semver();
2451
+ const { safeRe: re, src, t, comparatorTrimReplace, tildeTrimReplace, caretTrimReplace } = require_re();
2452
+ const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require_constants();
2453
+ const BUILDSTRIPRE = new RegExp(src[t.BUILD], "g");
2454
+ const isNullSet = (c) => c.value === "<0.0.0-0";
2455
+ const isAny = (c) => c.value === "";
2456
+ const isSatisfiable = (comparators, options) => {
2457
+ let result = true;
2458
+ const remainingComparators = comparators.slice();
2459
+ let testComparator = remainingComparators.pop();
2460
+ while (result && remainingComparators.length) {
2461
+ result = remainingComparators.every((otherComparator) => {
2462
+ return testComparator.intersects(otherComparator, options);
2463
+ });
2464
+ testComparator = remainingComparators.pop();
2465
+ }
2466
+ return result;
2467
+ };
2468
+ const parseComparator = (comp, options) => {
2469
+ comp = comp.replace(re[t.BUILD], "");
2470
+ debug("comp", comp, options);
2471
+ comp = replaceCarets(comp, options);
2472
+ debug("caret", comp);
2473
+ comp = replaceTildes(comp, options);
2474
+ debug("tildes", comp);
2475
+ comp = replaceXRanges(comp, options);
2476
+ debug("xrange", comp);
2477
+ comp = replaceStars(comp, options);
2478
+ debug("stars", comp);
2479
+ return comp;
2480
+ };
2481
+ const isX = (id) => !id || id.toLowerCase() === "x" || id === "*";
2482
+ const invalidXRangeOrder = (M, m, p) => isX(M) && !isX(m) || isX(m) && p && !isX(p);
2483
+ const replaceTildes = (comp, options) => {
2484
+ return comp.trim().split(/\s+/).map((c) => replaceTilde(c, options)).join(" ");
2485
+ };
2486
+ const replaceTilde = (comp, options) => {
2487
+ const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE];
2488
+ const z = options.includePrerelease ? "-0" : "";
2489
+ return comp.replace(r, (_, M, m, p, pr) => {
2490
+ debug("tilde", comp, _, M, m, p, pr);
2491
+ let ret;
2492
+ if (isX(M)) ret = "";
2493
+ else if (isX(m)) ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`;
2494
+ else if (isX(p)) ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`;
2495
+ else if (pr) {
2496
+ debug("replaceTilde pr", pr);
2497
+ ret = `>=${M}.${m}.${p}-${pr} <${M}.${+m + 1}.0-0`;
2498
+ } else ret = `>=${M}.${m}.${p} <${M}.${+m + 1}.0-0`;
2499
+ debug("tilde return", ret);
2500
+ return ret;
2501
+ });
2502
+ };
2503
+ const replaceCarets = (comp, options) => {
2504
+ return comp.trim().split(/\s+/).map((c) => replaceCaret(c, options)).join(" ");
2505
+ };
2506
+ const replaceCaret = (comp, options) => {
2507
+ debug("caret", comp, options);
2508
+ const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET];
2509
+ const z = options.includePrerelease ? "-0" : "";
2510
+ return comp.replace(r, (_, M, m, p, pr) => {
2511
+ debug("caret", comp, _, M, m, p, pr);
2512
+ let ret;
2513
+ if (isX(M)) ret = "";
2514
+ else if (isX(m)) ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`;
2515
+ else if (isX(p)) {
2516
+ if (M === "0") ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`;
2517
+ else ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`;
2518
+ } else if (pr) {
2519
+ debug("replaceCaret pr", pr);
2520
+ if (M === "0") {
2521
+ if (m === "0") ret = `>=${M}.${m}.${p}-${pr} <${M}.${m}.${+p + 1}-0`;
2522
+ else ret = `>=${M}.${m}.${p}-${pr} <${M}.${+m + 1}.0-0`;
2523
+ } else ret = `>=${M}.${m}.${p}-${pr} <${+M + 1}.0.0-0`;
2524
+ } else {
2525
+ debug("no pr");
2526
+ if (M === "0") {
2527
+ if (m === "0") ret = `>=${M}.${m}.${p} <${M}.${m}.${+p + 1}-0`;
2528
+ else ret = `>=${M}.${m}.${p} <${M}.${+m + 1}.0-0`;
2529
+ } else ret = `>=${M}.${m}.${p} <${+M + 1}.0.0-0`;
2530
+ }
2531
+ debug("caret return", ret);
2532
+ return ret;
2533
+ });
2534
+ };
2535
+ const replaceXRanges = (comp, options) => {
2536
+ debug("replaceXRanges", comp, options);
2537
+ return comp.split(/\s+/).map((c) => replaceXRange(c, options)).join(" ");
2538
+ };
2539
+ const replaceXRange = (comp, options) => {
2540
+ comp = comp.trim();
2541
+ const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE];
2542
+ return comp.replace(r, (ret, gtlt, M, m, p, pr) => {
2543
+ debug("xRange", comp, ret, gtlt, M, m, p, pr);
2544
+ if (invalidXRangeOrder(M, m, p)) return comp;
2545
+ const xM = isX(M);
2546
+ const xm = xM || isX(m);
2547
+ const xp = xm || isX(p);
2548
+ const anyX = xp;
2549
+ if (gtlt === "=" && anyX) gtlt = "";
2550
+ pr = options.includePrerelease ? "-0" : "";
2551
+ if (xM) {
2552
+ if (gtlt === ">" || gtlt === "<") ret = "<0.0.0-0";
2553
+ else ret = "*";
2554
+ } else if (gtlt && anyX) {
2555
+ if (xm) m = 0;
2556
+ p = 0;
2557
+ if (gtlt === ">") {
2558
+ gtlt = ">=";
2559
+ if (xm) {
2560
+ M = +M + 1;
2561
+ m = 0;
2562
+ p = 0;
2563
+ } else {
2564
+ m = +m + 1;
2565
+ p = 0;
2566
+ }
2567
+ } else if (gtlt === "<=") {
2568
+ gtlt = "<";
2569
+ if (xm) M = +M + 1;
2570
+ else m = +m + 1;
2571
+ }
2572
+ if (gtlt === "<") pr = "-0";
2573
+ ret = `${gtlt + M}.${m}.${p}${pr}`;
2574
+ } else if (xm) ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`;
2575
+ else if (xp) ret = `>=${M}.${m}.0${pr} <${M}.${+m + 1}.0-0`;
2576
+ debug("xRange return", ret);
2577
+ return ret;
2578
+ });
2579
+ };
2580
+ const replaceStars = (comp, options) => {
2581
+ debug("replaceStars", comp, options);
2582
+ return comp.trim().replace(re[t.STAR], "");
2583
+ };
2584
+ const replaceGTE0 = (comp, options) => {
2585
+ debug("replaceGTE0", comp, options);
2586
+ return comp.trim().replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], "");
2587
+ };
2588
+ const hyphenReplace = (incPr) => ($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr) => {
2589
+ if (isX(fM)) from = "";
2590
+ else if (isX(fm)) from = `>=${fM}.0.0${incPr ? "-0" : ""}`;
2591
+ else if (isX(fp)) from = `>=${fM}.${fm}.0${incPr ? "-0" : ""}`;
2592
+ else if (fpr) from = `>=${from}`;
2593
+ else from = `>=${from}${incPr ? "-0" : ""}`;
2594
+ if (isX(tM)) to = "";
2595
+ else if (isX(tm)) to = `<${+tM + 1}.0.0-0`;
2596
+ else if (isX(tp)) to = `<${tM}.${+tm + 1}.0-0`;
2597
+ else if (tpr) to = `<=${tM}.${tm}.${tp}-${tpr}`;
2598
+ else if (incPr) to = `<${tM}.${tm}.${+tp + 1}-0`;
2599
+ else to = `<=${to}`;
2600
+ return `${from} ${to}`.trim();
2601
+ };
2602
+ const testSet = (set, version, options) => {
2603
+ for (let i = 0; i < set.length; i++) if (!set[i].test(version)) return false;
2604
+ if (version.prerelease.length && !options.includePrerelease) {
2605
+ for (let i = 0; i < set.length; i++) {
2606
+ debug(set[i].semver);
2607
+ if (set[i].semver === Comparator.ANY) continue;
2608
+ if (set[i].semver.prerelease.length > 0) {
2609
+ const allowed = set[i].semver;
2610
+ if (allowed.major === version.major && allowed.minor === version.minor && allowed.patch === version.patch) return true;
2611
+ }
2612
+ }
2613
+ return false;
2614
+ }
2615
+ return true;
2616
+ };
2617
+ }));
2618
+ //#endregion
2619
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/satisfies.js
2620
+ var require_satisfies = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2621
+ const Range = require_range();
2622
+ const satisfies = (version, range, options) => {
2623
+ try {
2624
+ range = new Range(range, options);
2625
+ } catch (er) {
2626
+ return false;
2627
+ }
2628
+ return range.test(version);
2629
+ };
2630
+ module.exports = satisfies;
2631
+ }));
2632
+ //#endregion
2633
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/parse.js
2634
+ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2635
+ const SemVer = require_semver();
2636
+ const parse = (version, options, throwErrors = false) => {
2637
+ if (version instanceof SemVer) return version;
2638
+ try {
2639
+ return new SemVer(version, options);
2640
+ } catch (er) {
2641
+ if (!throwErrors) return null;
2642
+ throw er;
2643
+ }
2644
+ };
2645
+ module.exports = parse;
2646
+ }));
2647
+ //#endregion
2648
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/valid.js
2649
+ var require_valid$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2650
+ const parse = require_parse();
2651
+ const valid = (version, options) => {
2652
+ const v = parse(version, options);
2653
+ return v ? v.version : null;
2654
+ };
2655
+ module.exports = valid;
2656
+ }));
2657
+ //#endregion
2658
+ //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/ranges/valid.js
2659
+ var require_valid = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2660
+ const Range = require_range();
2661
+ const validRange = (range, options) => {
2662
+ try {
2663
+ return new Range(range, options).range || "*";
2664
+ } catch (er) {
2665
+ return null;
2666
+ }
2667
+ };
2668
+ module.exports = validRange;
2669
+ }));
2670
+ //#endregion
2671
+ //#region package.json
2672
+ var import_satisfies = /* @__PURE__ */ __toESM(require_satisfies(), 1);
2673
+ var import_valid = /* @__PURE__ */ __toESM(require_valid$1(), 1);
2674
+ var import_valid$1 = /* @__PURE__ */ __toESM(require_valid(), 1);
2675
+ var optionalDependencies = {
2676
+ "@tsdown/exe": "0.23.0",
2677
+ "tsdown": "0.23.0"
2678
+ };
2679
+ //#endregion
2680
+ //#region src/utils/build-helpers.ts
2681
+ const BUILD_RUNTIMES = [
2682
+ "bun",
2683
+ "deno",
2684
+ "node"
2685
+ ];
2686
+ const BUN_TARGETS = {
2687
+ runtime: "Bun",
2688
+ targets: [
2689
+ "bun-linux-x64",
2690
+ "bun-linux-arm64",
2691
+ "bun-linux-x64-musl",
2692
+ "bun-linux-arm64-musl",
2693
+ "bun-darwin-x64",
2694
+ "bun-darwin-arm64",
2695
+ "bun-windows-x64",
2696
+ "bun-windows-arm64"
2697
+ ],
2698
+ info: {
2699
+ "bun-linux-x64": {
2700
+ alias: "linux-x64",
2701
+ platformKey: "linux-x64",
2702
+ os: "linux",
2703
+ cpu: "x64",
2704
+ libc: "glibc"
2705
+ },
2706
+ "bun-linux-arm64": {
2707
+ alias: "linux-arm64",
2708
+ platformKey: "linux-arm64",
2709
+ os: "linux",
2710
+ cpu: "arm64",
2711
+ libc: "glibc"
2712
+ },
2713
+ "bun-linux-x64-musl": {
2714
+ alias: "linux-x64-musl",
2715
+ platformKey: "linux-x64-musl",
2716
+ os: "linux",
2717
+ cpu: "x64",
2718
+ libc: "musl"
2719
+ },
2720
+ "bun-linux-arm64-musl": {
1862
2721
  alias: "linux-arm64-musl",
1863
2722
  platformKey: "linux-arm64-musl",
1864
2723
  os: "linux",
@@ -1942,8 +2801,57 @@ const DENO_TARGETS = {
1942
2801
  }
1943
2802
  }
1944
2803
  };
1945
- /** `--target` value that stands for this machine's canonical target. */
1946
- const HOST_TARGET = "host";
2804
+ const NODE_TARGETS = {
2805
+ runtime: "Node",
2806
+ targets: [
2807
+ "linux-x64",
2808
+ "linux-arm64",
2809
+ "darwin-x64",
2810
+ "darwin-arm64",
2811
+ "win-x64",
2812
+ "win-arm64"
2813
+ ],
2814
+ info: {
2815
+ "linux-x64": {
2816
+ alias: "linux-x64",
2817
+ platformKey: "linux-x64",
2818
+ os: "linux",
2819
+ cpu: "x64",
2820
+ libc: "glibc"
2821
+ },
2822
+ "linux-arm64": {
2823
+ alias: "linux-arm64",
2824
+ platformKey: "linux-arm64",
2825
+ os: "linux",
2826
+ cpu: "arm64",
2827
+ libc: "glibc"
2828
+ },
2829
+ "darwin-x64": {
2830
+ alias: "darwin-x64",
2831
+ platformKey: "darwin-x64",
2832
+ os: "darwin",
2833
+ cpu: "x64"
2834
+ },
2835
+ "darwin-arm64": {
2836
+ alias: "darwin-arm64",
2837
+ platformKey: "darwin-arm64",
2838
+ os: "darwin",
2839
+ cpu: "arm64"
2840
+ },
2841
+ "win-x64": {
2842
+ alias: "windows-x64",
2843
+ platformKey: "win32-x64",
2844
+ os: "win32",
2845
+ cpu: "x64"
2846
+ },
2847
+ "win-arm64": {
2848
+ alias: "windows-arm64",
2849
+ platformKey: "win32-arm64",
2850
+ os: "win32",
2851
+ cpu: "arm64"
2852
+ }
2853
+ }
2854
+ };
1947
2855
  /**
1948
2856
  * Canonical targets for `--target` inputs, deduplicated in input order. No
1949
2857
  * inputs means every target of the table; `host` means this machine's target.
@@ -2043,14 +2951,234 @@ function bunCompileTarget(target, runner, host = hostTarget(BUN_TARGETS)) {
2043
2951
  /**
2044
2952
  * Refuse the host target when only the `BUN_BE_BUN` fallback runner is
2045
2953
  * available and no `-baseline` alias can stand in for it (arm64 hosts).
2954
+ * Pass the selected `runner` to judge that compiler instead of looking up
2955
+ * `bun` on PATH again.
2046
2956
  */
2047
- function assertTargetsBuildableWithoutBun(targets, host = hostTarget(BUN_TARGETS)) {
2048
- if (host === null || !targets.includes(host) || bunBaselineAlias(host) !== null || which("bun") !== null) return;
2957
+ function assertTargetsBuildableWithoutBun(targets, host = hostTarget(BUN_TARGETS), runner) {
2958
+ const externalBun = runner ? runner.env.BUN_BE_BUN !== "1" : which("bun") !== null;
2959
+ if (host === null || !targets.includes(host) || bunBaselineAlias(host) !== null || externalBun) return;
2049
2960
  const others = targets.filter((target) => target !== host);
2050
2961
  const alternative = others.length > 0 ? `pass --target with the other targets (e.g. ${others.map((target) => `--target ${target}`).join(" ")})` : "build a different target";
2051
2962
  throw new Error(`Cannot build ${host} without a separate bun executable on PATH.\n Bun reuses the running crust executable as the base for its own platform, which yields a binary that crashes on start.
2052
2963
  Install Bun (https://bun.sh), or ${alternative}.`);
2053
2964
  }
2965
+ /** The external `deno` on PATH; Deno has no embedded fallback. */
2966
+ function resolveDenoBuildRunner() {
2967
+ const denoPath = which("deno");
2968
+ if (!denoPath) throw new Error("Deno is required for the deno runtime but was not found on PATH.\n Install Deno from https://deno.com/ and try again.");
2969
+ return {
2970
+ command: denoPath,
2971
+ env: { ...process.env }
2972
+ };
2973
+ }
2974
+ /** The external `node` on PATH, which runs tsdown's executable builder; there is no embedded fallback. */
2975
+ function resolveNodeBuildRunner() {
2976
+ const nodePath = which("node");
2977
+ if (!nodePath) throw new Error("Node is required for node standalone binaries but was not found on PATH.\n Binaries embed the selected node's version; install Node (https://nodejs.org) or put it first on PATH with your version manager.");
2978
+ return {
2979
+ command: nodePath,
2980
+ env: { ...process.env }
2981
+ };
2982
+ }
2983
+ const RUNTIME_LABELS = {
2984
+ bun: "Bun",
2985
+ deno: "Deno",
2986
+ node: "Node"
2987
+ };
2988
+ const COMPILER_VERSION_TIMEOUT_MS = 3e4;
2989
+ /**
2990
+ * Runtime version reported by the runner itself (`<command> --version`), never
2991
+ * the runtime hosting this process: `bun` prints `1.4.2`, `deno` prints
2992
+ * `deno 2.9.6 (…)`, `node` prints `v24.21.0`. Under the `BUN_BE_BUN` fallback
2993
+ * this is the embedded Bun, which is the compiler. `cwd` must be the directory
2994
+ * compilation runs in: a version-manager shim picks its runtime from it. A
2995
+ * probe still running after {@link COMPILER_VERSION_TIMEOUT_MS} is killed and
2996
+ * fails the build, before `.crust/` is replaced.
2997
+ */
2998
+ async function readCompilerVersion(runtime, runner, cwd) {
2999
+ const { exitCode, stdout, stderr } = await runProcess(runner.command, ["--version"], {
3000
+ env: runner.env,
3001
+ cwd,
3002
+ stdio: "collect",
3003
+ timeout: COMPILER_VERSION_TIMEOUT_MS
3004
+ });
3005
+ const reported = runtime === "deno" ? /^deno (\S+)/.exec(stdout.trim())?.[1] : stdout.trim();
3006
+ const version = exitCode === 0 && reported !== void 0 ? (0, import_valid.default)(reported) : null;
3007
+ if (version === null) {
3008
+ const output = [stderr.trim(), stdout.trim()].filter(Boolean).join("\n");
3009
+ throw new Error(`Could not read the ${RUNTIME_LABELS[runtime]} version from ${runner.command} --version (exit ${exitCode})${output ? `:\n${output}` : "."}`);
3010
+ }
3011
+ return version;
3012
+ }
3013
+ function isVersionRange(value) {
3014
+ return typeof value === "string" && (0, import_valid$1.default)(value) !== null;
3015
+ }
3016
+ /**
3017
+ * Check the selected compiler against the project's `engines.<runtime>` with
3018
+ * npm semver semantics. An absent constraint allows any version; an exact
3019
+ * value is a requirement, not a request to install that version.
3020
+ */
3021
+ function assertCompilerSatisfiesEngines(compiler, userPackageJson) {
3022
+ if (userPackageJson === void 0 || !isJsonObject(userPackageJson)) return;
3023
+ const { engines } = userPackageJson;
3024
+ if (engines === void 0) return;
3025
+ if (!isJsonObject(engines)) throw new Error("package.json engines must be an object of runtime version ranges.");
3026
+ const constraint = engines[compiler.runtime];
3027
+ if (constraint === void 0) return;
3028
+ const field = `package.json engines.${compiler.runtime}`;
3029
+ if (!isVersionRange(constraint)) throw new Error(`${field} is not a valid semver range: ${JSON.stringify(constraint)}.\n Use an exact version or range such as "1.4.2" or ">=1.4.0".`);
3030
+ if (!(0, import_satisfies.default)(compiler.version, constraint)) {
3031
+ const label = RUNTIME_LABELS[compiler.runtime];
3032
+ throw new Error(`${label} ${compiler.version} (${compiler.runner.command}) does not satisfy ${field} "${constraint}".\n Binaries embed the selected compiler's ${label} version; crust does not install or upgrade it.\n Put a matching ${compiler.runtime} first on PATH (e.g. with your version manager), or update ${field}.`);
3033
+ }
3034
+ }
3035
+ /**
3036
+ * Select the binary compiler once for a build: the Bun runner (external bun
3037
+ * first, then the embedded fallback) or the external deno. Returns its actual
3038
+ * version, read in the project directory `cwd`, after validating it against
3039
+ * `engines`. Pass `runner` and the same `cwd` on to the `exec*`/`buildEntrypoint`
3040
+ * helpers so every step uses this same compiler.
3041
+ */
3042
+ async function resolveBinaryCompiler(runtime, userPackageJson, cwd) {
3043
+ const runner = runtime === "bun" ? resolveBunBuildRunner() : resolveDenoBuildRunner();
3044
+ const compiler = {
3045
+ runtime,
3046
+ runner,
3047
+ version: await readCompilerVersion(runtime, runner, cwd)
3048
+ };
3049
+ assertCompilerSatisfiesEngines(compiler, userPackageJson);
3050
+ return compiler;
3051
+ }
3052
+ /**
3053
+ * Crust's own optional dependencies that build Node standalone binaries.
3054
+ * Crust's staged root and platform packages declare them
3055
+ * (scripts/stage-node-exe-dependencies.ts) so an installed crust resolves them
3056
+ * next to itself; an install that skipped them only affects Node binaries.
3057
+ */
3058
+ const NODE_EXE_BACKEND_PACKAGES = ["tsdown", "@tsdown/exe"];
3059
+ /**
3060
+ * A file of Crust's own installation that the backend is resolved from, never
3061
+ * the project or PATH. A compiled crust serves its modules from Bun's virtual
3062
+ * filesystem (`/$bunfs/`, `B:/~BUN/` on Windows), so there it is the
3063
+ * executable, inside its platform package.
3064
+ */
3065
+ function crustInstallPath(moduleUrl = import.meta.url, execPath = process.execPath) {
3066
+ return /^file:\/\/\/(?:\$bunfs|[A-Za-z]:\/~BUN)\//.test(moduleUrl) ? execPath : fileURLToPath(moduleUrl);
3067
+ }
3068
+ /**
3069
+ * `<dir>/node_modules/<name>/package.json` for the nearest `dir` at or above
3070
+ * `from`: Node's node_modules lookup without its NODE_PATH and global-folder
3071
+ * fallbacks, so only an installation next to `from` can supply the package.
3072
+ * Package managers hoist crust's backend into the project's node_modules, so
3073
+ * the found package may be the project's own: callers check its version.
3074
+ */
3075
+ function findInstalledPackageJson(from, name) {
3076
+ for (let dir = from;; dir = dirname(dir)) {
3077
+ const candidate = join(dir, "node_modules", name, "package.json");
3078
+ if (existsSync(candidate)) return candidate;
3079
+ if (dirname(dir) === dir) return null;
3080
+ }
3081
+ }
3082
+ function isVersionedPackageJson(value) {
3083
+ return isJsonObject(value) && typeof value.version === "string";
3084
+ }
3085
+ function isTsdownPackageJson(value) {
3086
+ return isVersionedPackageJson(value) && value.engines !== void 0 && isJsonObject(value.engines) && value.engines.node !== void 0 && isVersionRange(value.engines.node);
3087
+ }
3088
+ function createNodeExeProbeScript(tsdownPackageJson) {
3089
+ return `// Generated by crust build to read tsdown's executable minimum; deleted when it exits.
3090
+ import { createRequire } from "node:module";
3091
+ import { pathToFileURL } from "node:url";
3092
+
3093
+ const internal = createRequire(${JSON.stringify(tsdownPackageJson)}).resolve("tsdown/internal");
3094
+ console.log((await import(pathToFileURL(internal).href)).NODE_SEA_MIN_VERSION);
3095
+ `;
3096
+ }
3097
+ const NODE_TOOLCHAIN_GUIDANCE = " Binaries embed the selected node's version; crust does not install or upgrade it.\n Put a supported node first on PATH (e.g. with your version manager).";
3098
+ /**
3099
+ * Checks the selected Node against the installed backend's two separate
3100
+ * requirements: tsdown's package `engines.node` and its executable builder's
3101
+ * minimum (skipped while unknown). Both come from the installed tsdown, not
3102
+ * from this file.
3103
+ */
3104
+ function assertNodeExeBackendSupports(compiler, backend) {
3105
+ const selected = `Node ${compiler.version} (${compiler.runner.command})`;
3106
+ if (!(0, import_satisfies.default)(compiler.version, backend.engines)) throw new Error(`${selected} is not supported by tsdown ${backend.version}, which builds node standalone binaries: its package requires Node "${backend.engines}".\n${NODE_TOOLCHAIN_GUIDANCE}`);
3107
+ if (backend.seaMinVersion !== void 0 && !(0, import_satisfies.default)(compiler.version, `>=${backend.seaMinVersion}`)) throw new Error(`${selected} cannot build standalone executables: tsdown ${backend.version}'s executable builder requires Node ${backend.seaMinVersion} or later.\n${NODE_TOOLCHAIN_GUIDANCE}`);
3108
+ }
3109
+ /**
3110
+ * Finds Crust's installed tsdown and @tsdown/exe from `installPath`, at the
3111
+ * versions its package.json `optionalDependencies` pin, and validates the
3112
+ * selected Node against tsdown's requirements, loading the executable
3113
+ * builder's minimum under that Node. `cwd` is the project
3114
+ * directory, as for every other compiler step.
3115
+ */
3116
+ async function resolveNodeExeBackend(compiler, cwd, installPath = crustInstallPath()) {
3117
+ const reinstall = ` @crustjs/crust ships ${NODE_EXE_BACKEND_PACKAGES.join(" and ")} as optional dependencies, which package managers skip when optional dependencies are disabled or the installing node does not satisfy tsdown's engines.\n Reinstall @crustjs/crust with optional dependencies enabled under a node that can build Node binaries (e.g. the one on PATH here).`;
3118
+ const packageJsonPath = findInstalledPackageJson(dirname(installPath), "tsdown");
3119
+ if (packageJsonPath === null) throw new Error(`tsdown, which builds node standalone binaries, is not installed with crust (${installPath}).\n${reinstall}`);
3120
+ const exePackageJsonPath = findInstalledPackageJson(dirname(realpathSync(packageJsonPath)), "@tsdown/exe");
3121
+ if (exePackageJsonPath === null) throw new Error(`@tsdown/exe is not installed beside crust's tsdown (${packageJsonPath}).\n${reinstall}`);
3122
+ for (const [name, path] of [["tsdown", packageJsonPath], ["@tsdown/exe", exePackageJsonPath]]) {
3123
+ const installed = JSON.parse(readFileSync(path, "utf8"));
3124
+ const version = isVersionedPackageJson(installed) ? installed.version : "without a version";
3125
+ const pinned = optionalDependencies[name];
3126
+ if (!(0, import_satisfies.default)(version, pinned)) throw new Error(`${name} ${version} (${path}) is not the ${name} ${pinned} that crust builds node standalone binaries with.\n${reinstall}`);
3127
+ }
3128
+ const tsdownPackage = JSON.parse(readFileSync(packageJsonPath, "utf8"));
3129
+ if (!isTsdownPackageJson(tsdownPackage)) throw new Error(`Unexpected tsdown package metadata in ${packageJsonPath}.\n${reinstall}`);
3130
+ const backend = {
3131
+ packageJsonPath,
3132
+ version: tsdownPackage.version,
3133
+ engines: tsdownPackage.engines.node
3134
+ };
3135
+ assertNodeExeBackendSupports(compiler, backend);
3136
+ const workDir = await mkdtemp(join(tmpdir(), "crust-node-exe-probe-"));
3137
+ try {
3138
+ const scriptPath = join(workDir, "probe.mjs");
3139
+ await writeFile(scriptPath, createNodeExeProbeScript(packageJsonPath));
3140
+ const { exitCode, stdout, stderr } = await runProcess(compiler.runner.command, [scriptPath], {
3141
+ env: compiler.runner.env,
3142
+ cwd,
3143
+ stdio: "collect"
3144
+ });
3145
+ const seaMinVersion = exitCode === 0 ? (0, import_valid.default)(stdout.trim()) : null;
3146
+ if (seaMinVersion === null) {
3147
+ const output = [stderr.trim(), stdout.trim()].filter(Boolean).join("\n");
3148
+ throw new Error(`tsdown ${backend.version}'s executable builder could not be loaded with ${compiler.runner.command} (exit ${exitCode}).\n${reinstall}${output ? `\n${output}` : ""}`);
3149
+ }
3150
+ const resolved = {
3151
+ ...backend,
3152
+ seaMinVersion
3153
+ };
3154
+ assertNodeExeBackendSupports(compiler, resolved);
3155
+ return resolved;
3156
+ } finally {
3157
+ await rm(workDir, {
3158
+ recursive: true,
3159
+ force: true
3160
+ });
3161
+ }
3162
+ }
3163
+ /**
3164
+ * Select the Node binary compiler once for a build: the external `node`, its
3165
+ * version read in the project directory `cwd`, validated against the
3166
+ * project's `engines.node` and then the installed tsdown's requirements.
3167
+ * Pass it to {@link execNodeBinaryBuild} for every command and target.
3168
+ */
3169
+ async function resolveNodeBinaryCompiler(userPackageJson, cwd) {
3170
+ const runner = resolveNodeBuildRunner();
3171
+ const compiler = {
3172
+ runtime: "node",
3173
+ runner,
3174
+ version: await readCompilerVersion("node", runner, cwd)
3175
+ };
3176
+ assertCompilerSatisfiesEngines(compiler, userPackageJson);
3177
+ return {
3178
+ ...compiler,
3179
+ backend: await resolveNodeExeBackend(compiler, cwd)
3180
+ };
3181
+ }
2054
3182
  /**
2055
3183
  * Module source the generated driver imports for a `crust.bunPlugins` specifier.
2056
3184
  *
@@ -2100,7 +3228,7 @@ if (!result.success) {
2100
3228
  for (const log of result.logs) console.error(log);
2101
3229
  process.exit(1);
2102
3230
  }
2103
- if (options.build.target === "node") {
3231
+ if (!options.build.compile) {
2104
3232
  if (result.outputs.length !== 1) {
2105
3233
  // Same refusal as \`bun build --outfile\`: a file-type asset import yields entry + asset.
2106
3234
  console.error("error: cannot write multiple output files without an output directory");
@@ -2110,7 +3238,7 @@ if (options.build.target === "node") {
2110
3238
  }
2111
3239
  `;
2112
3240
  }
2113
- async function runBunPluginDriver(build, outfilePath, bunPlugins, envFiles, cwd) {
3241
+ async function runBunPluginDriver(build, outfilePath, bunPlugins, envFiles, cwd, runner) {
2114
3242
  const driverPath = join(cwd, `.crust-build-${randomBytes(6).toString("hex")}.ts`);
2115
3243
  await writeFile(driverPath, createBunPluginDriverScript({
2116
3244
  plugins: bunPlugins.map((specifier) => ({
@@ -2121,7 +3249,7 @@ async function runBunPluginDriver(build, outfilePath, bunPlugins, envFiles, cwd)
2121
3249
  outfile: outfilePath
2122
3250
  }));
2123
3251
  try {
2124
- await runBuildProcess(resolveBunBuildRunner(), [...toBunEnvFileArgs(envFiles), driverPath], outfilePath, cwd);
3252
+ await runBuildProcess(runner, [...toBunEnvFileArgs(envFiles), driverPath], outfilePath, cwd);
2125
3253
  } finally {
2126
3254
  await rm(driverPath, { force: true });
2127
3255
  }
@@ -2141,10 +3269,10 @@ async function runBunPluginDriver(build, outfilePath, bunPlugins, envFiles, cwd)
2141
3269
  * @param envFiles - Optional env files to load during build
2142
3270
  * @param bunPlugins - Bun bundler plugin specifiers; when present the build
2143
3271
  * runs through the generated `Bun.build` driver instead of `bun build`
3272
+ * @param runner - The selected Bun compiler (`resolveBinaryCompiler`)
2144
3273
  * @throws {Error} If the build fails
2145
3274
  */
2146
- async function execBuild(entryPath, outfilePath, minify, target, envFiles, cwd, bunPlugins = []) {
2147
- const runner = resolveBunBuildRunner();
3275
+ async function execBuild(entryPath, outfilePath, minify, target, envFiles, cwd, bunPlugins = [], runner = resolveBunBuildRunner()) {
2148
3276
  const compileTarget = bunCompileTarget(target, runner);
2149
3277
  if (bunPlugins.length > 0) {
2150
3278
  await runBunPluginDriver({
@@ -2157,7 +3285,7 @@ async function execBuild(entryPath, outfilePath, minify, target, envFiles, cwd,
2157
3285
  outfile: outfilePath,
2158
3286
  autoloadBunfig: false
2159
3287
  }
2160
- }, outfilePath, bunPlugins, envFiles, cwd);
3288
+ }, outfilePath, bunPlugins, envFiles, cwd, runner);
2161
3289
  return;
2162
3290
  }
2163
3291
  await runBuildProcess(runner, createBunCompileArgs(entryPath, outfilePath, minify, compileTarget, envFiles), outfilePath, cwd);
@@ -2201,22 +3329,33 @@ async function runBuildProcess(runner, args, outfilePath, cwd) {
2201
3329
  throw new Error(`Build failed for ${outfilePath}${output ? `:\n${output}` : ""}`);
2202
3330
  }
2203
3331
  }
2204
- async function execNodeBuild(entryPath, outfilePath, minify, envFiles, cwd, bunPlugins = []) {
3332
+ /** Bundle a Node runtime package entry: ESM for Node behind `#!/usr/bin/env node`. */
3333
+ async function execNodeBuild(entryPath, outfilePath, minify, envFiles, cwd, bunPlugins = [], runner = resolveBunBuildRunner()) {
3334
+ await execScriptBuild("node", entryPath, outfilePath, minify, envFiles, cwd, bunPlugins, runner);
3335
+ }
3336
+ /**
3337
+ * Bundle a Bun runtime package entry: Bun-targeted ESM behind
3338
+ * `#!/usr/bin/env bun`, so the installed bin launches with Bun rather than Node.
3339
+ */
3340
+ async function execBunPackageBuild(entryPath, outfilePath, minify, envFiles, cwd, bunPlugins = [], runner = resolveBunBuildRunner()) {
3341
+ await execScriptBuild("bun", entryPath, outfilePath, minify, envFiles, cwd, bunPlugins, runner);
3342
+ }
3343
+ async function execScriptBuild(target, entryPath, outfilePath, minify, envFiles, cwd, bunPlugins, runner) {
2205
3344
  if (bunPlugins.length > 0) await runBunPluginDriver({
2206
3345
  entrypoints: [entryPath],
2207
3346
  minify,
2208
3347
  env: "PUBLIC_*",
2209
- target: "node",
3348
+ target,
2210
3349
  format: "esm"
2211
- }, outfilePath, bunPlugins, envFiles, cwd);
2212
- else await runBuildProcess(resolveBunBuildRunner(), [
3350
+ }, outfilePath, bunPlugins, envFiles, cwd, runner);
3351
+ else await runBuildProcess(runner, [
2213
3352
  "build",
2214
3353
  ...toBunEnvFileArgs(envFiles),
2215
3354
  "--env=PUBLIC_*",
2216
3355
  "--define",
2217
3356
  CRUST_BUILD_DEFINE_ARG,
2218
3357
  "--target",
2219
- "node",
3358
+ target,
2220
3359
  "--format",
2221
3360
  "esm",
2222
3361
  "--outfile",
@@ -2225,40 +3364,246 @@ async function execNodeBuild(entryPath, outfilePath, minify, envFiles, cwd, bunP
2225
3364
  entryPath
2226
3365
  ], outfilePath, cwd);
2227
3366
  const output = await readFile(outfilePath, "utf8");
2228
- await writeFile(outfilePath, "#!/usr/bin/env node\n" + output.replace(/^#![^\n]*(?:\n|$)/, ""));
3367
+ const shebang = `#!/usr/bin/env ${target}\n`;
3368
+ await writeFile(outfilePath, shebang + output.replace(/^#![^\n]*(?:\n|$)/, ""));
2229
3369
  if (process.platform !== "win32") await chmod(outfilePath, 493);
2230
3370
  }
2231
- async function execDenoBuild(entryPath, outfilePath, target, cwd) {
2232
- const denoPath = which("deno");
2233
- if (!denoPath) throw new Error("Deno is required for the deno runtime but was not found on PATH.\n Install Deno from https://deno.com/ and try again.");
2234
- await runBuildProcess({
2235
- command: denoPath,
2236
- env: { ...process.env }
2237
- }, createDenoCompileArgs(entryPath, outfilePath, target), outfilePath, cwd);
3371
+ async function execDenoBuild(entryPath, outfilePath, target, cwd, runner = resolveDenoBuildRunner()) {
3372
+ await runBuildProcess(runner, createDenoCompileArgs(entryPath, outfilePath, target), outfilePath, cwd);
3373
+ }
3374
+ /** tsdown's executable target for a Node target, embedding exactly `nodeVersion`. */
3375
+ function nodeExeTarget(target, nodeVersion) {
3376
+ const { os, cpu } = NODE_TARGETS.info[target];
3377
+ return {
3378
+ platform: os === "win32" ? "win" : os,
3379
+ arch: cpu,
3380
+ nodeVersion
3381
+ };
2238
3382
  }
2239
3383
  /**
2240
- * Prepare a CLI entry's Command Snapshot in the user's project context.
3384
+ * Script the selected Node runs to turn one Bun bundle into one target's
3385
+ * executable with tsdown's programmatic `build`. Every value is embedded via
3386
+ * JSON.stringify.
3387
+ */
3388
+ function createNodeExeBuildScript(options) {
3389
+ return `// Generated by crust build for a Node standalone binary; deleted when the build finishes.
3390
+ import { createRequire } from "node:module";
3391
+ import { pathToFileURL } from "node:url";
3392
+
3393
+ const options = ${JSON.stringify(options)};
3394
+ const { build } = await import(pathToFileURL(createRequire(options.tsdownPackageJson).resolve("tsdown")).href);
3395
+ await build({
3396
+ config: false,
3397
+ cwd: options.cwd,
3398
+ entry: { app: options.entry },
3399
+ outDir: options.bundleDir,
3400
+ format: "esm",
3401
+ platform: "node",
3402
+ dts: false,
3403
+ // Rolldown reprints the Bun bundle, so it minifies again.
3404
+ minify: options.minify,
3405
+ logLevel: "warn",
3406
+ report: false,
3407
+ // Bun already inlined the PUBLIC_* constants; inline nothing from this environment.
3408
+ envPrefix: [],
3409
+ // Bun leaves only Node built-ins as imports; fail on anything else, which
3410
+ // an executable shipping no node_modules could not resolve.
3411
+ deps: { alwaysBundle: () => true, onlyBundle: false, onlyImport: [] },
3412
+ exe: { outDir: options.exeDir, fileName: "app", targets: [options.target] },
3413
+ });
3414
+ `;
3415
+ }
3416
+ /**
3417
+ * Build a single entry file into a Node standalone executable: Bun first
3418
+ * bundles it for Node exactly as a Node runtime package ({@link execNodeBuild}:
3419
+ * `PUBLIC_*` constants from `envFiles` and the environment, each module's own
3420
+ * `import.meta.main` and `require.main === module`, CommonJS and ESM inputs),
3421
+ * then tsdown's executable builder (Node SEA), run by the selected external
3422
+ * Node, turns that bundle into an executable embedding exactly its version
3423
+ * for `target`. tsdown takes a single entry per executable, so callers build
3424
+ * every command separately. Matching official Node binaries for targets are
3425
+ * downloaded, checksum-verified, and cached by @tsdown/exe. Builds for darwin
3426
+ * are only code-signed on a macOS host.
2241
3427
  *
2242
- * The entry runs as a subprocess with `CRUST_INTERNAL_SNAPSHOT_PATH` pointing
2243
- * to a temporary file. `.execute()` validates and writes the command graph and
2244
- * adjacent Build Report, then exits before any following entrypoint code can run.
3428
+ * @param compiler - The selected Node compiler (`resolveNodeBinaryCompiler`)
3429
+ * @param bunRunner - The Bun that bundles (`resolveBunBuildRunner`)
3430
+ * @param onWarning - Receives the backend's warnings (such as a skipped code
3431
+ * signature) after a successful build
3432
+ * @throws {Error} If the build fails
3433
+ */
3434
+ async function execNodeBinaryBuild(entryPath, outfilePath, minify, target, envFiles, cwd, compiler, bunRunner, onWarning = () => {}) {
3435
+ const workDir = await mkdtemp(join(tmpdir(), "crust-node-exe-"));
3436
+ try {
3437
+ const scriptPath = join(workDir, "build.mjs");
3438
+ const bunBundle = join(workDir, "app.mjs");
3439
+ const exeDir = join(workDir, "exe");
3440
+ await execNodeBuild(resolve(cwd, entryPath), bunBundle, minify, envFiles, cwd, [], bunRunner);
3441
+ await writeFile(scriptPath, createNodeExeBuildScript({
3442
+ tsdownPackageJson: compiler.backend.packageJsonPath,
3443
+ cwd,
3444
+ entry: bunBundle,
3445
+ bundleDir: join(workDir, "bundle"),
3446
+ exeDir,
3447
+ minify,
3448
+ target: nodeExeTarget(target, compiler.version)
3449
+ }));
3450
+ const { exitCode, stdout, stderr } = await runProcess(compiler.runner.command, [scriptPath], {
3451
+ env: compiler.runner.env,
3452
+ cwd,
3453
+ stdio: "collect"
3454
+ });
3455
+ const output = [stderr.trim(), stdout.trim()].filter(Boolean).join("\n");
3456
+ if (exitCode !== 0) throw new Error(`Build failed for ${outfilePath}${output ? `:\n${output}` : ""}`);
3457
+ const produced = await readdir(exeDir);
3458
+ if (produced.length !== 1) throw new Error(`Build failed for ${outfilePath}: expected one executable from tsdown, found ${JSON.stringify(produced)}.`);
3459
+ await copyFile(join(exeDir, produced[0]), outfilePath);
3460
+ if (output) onWarning(output);
3461
+ } finally {
3462
+ await rm(workDir, {
3463
+ recursive: true,
3464
+ force: true,
3465
+ maxRetries: 3,
3466
+ retryDelay: 100
3467
+ });
3468
+ }
3469
+ }
3470
+ function createNodeExeProvisionScript(tsdownPackageJson, targets) {
3471
+ return `// Generated by crust build to provision Node binaries for targets; deleted when it exits.
3472
+ import { realpathSync } from "node:fs";
3473
+ import { createRequire } from "node:module";
3474
+ import { pathToFileURL } from "node:url";
3475
+
3476
+ const exe = createRequire(realpathSync(${JSON.stringify(tsdownPackageJson)})).resolve("@tsdown/exe");
3477
+ const { resolveNodeBinary } = await import(pathToFileURL(exe).href);
3478
+ for (const { name, target } of ${JSON.stringify(targets)}) {
3479
+ try {
3480
+ await resolveNodeBinary(target, {});
3481
+ } catch (error) {
3482
+ console.error(name + ": " + error.message + (error.cause?.message ? "\\n" + error.cause.message : ""));
3483
+ process.exit(1);
3484
+ }
3485
+ }
3486
+ `;
3487
+ }
3488
+ /**
3489
+ * Provisions the Node binary every target embeds before anything is staged,
3490
+ * with the selected Node and the call tsdown's executable builder makes per
3491
+ * target (@tsdown/exe's `resolveNodeBinary`): an uncached official archive of
3492
+ * exactly `compiler.version` is downloaded, checksum-verified, unpacked with
3493
+ * the tool that target's archive needs on this host, and cached, where
3494
+ * {@link execNodeBinaryBuild} then finds it. A missing unpacking tool, a failed
3495
+ * download, or a checksum mismatch therefore fails here, before `.crust/` is
3496
+ * replaced; cached targets need nothing.
3497
+ *
3498
+ * @throws {Error} If a target cannot be provisioned
3499
+ */
3500
+ async function provisionNodeExeTargets(targets, cwd, compiler) {
3501
+ const workDir = await mkdtemp(join(tmpdir(), "crust-node-exe-provision-"));
3502
+ try {
3503
+ const scriptPath = join(workDir, "provision.mjs");
3504
+ await writeFile(scriptPath, createNodeExeProvisionScript(compiler.backend.packageJsonPath, targets.map((name) => ({
3505
+ name,
3506
+ target: nodeExeTarget(name, compiler.version)
3507
+ }))));
3508
+ const { exitCode, stdout, stderr } = await runProcess(compiler.runner.command, [scriptPath], {
3509
+ env: compiler.runner.env,
3510
+ cwd,
3511
+ stdio: "collect"
3512
+ });
3513
+ if (exitCode !== 0) {
3514
+ const output = [stderr.trim(), stdout.trim()].filter(Boolean).join("\n");
3515
+ throw new Error(`Could not provision the Node ${compiler.version} binary that node standalone binaries embed${output ? `:\n${output}` : "."}\n tsdown downloads each target's official Node archive once and unpacks it with tar (GNU tar also needs xz for linux targets), or unzip for win targets on non-Windows hosts.
3516
+ Install the missing tool, or build only targets this machine can provision (--target).`);
3517
+ }
3518
+ } finally {
3519
+ await rm(workDir, {
3520
+ recursive: true,
3521
+ force: true
3522
+ });
3523
+ }
3524
+ }
3525
+ /**
3526
+ * Oldest `deno` accepted to bundle a Deno runtime package. Deno 2.4 resolves a
3527
+ * package.json project's npm dependencies from the registry instead of its
3528
+ * installed node_modules, so it can bundle a different Crust than the project
3529
+ * installed. Tested on Linux x64 with Deno 2.5.0 through 2.9.6.
3530
+ */
3531
+ const DENO_PACKAGE_MIN_VERSION = "2.5.0";
3532
+ /**
3533
+ * Select the external deno that bundles a Deno runtime package, reading its
3534
+ * version in the project directory `cwd`. Call it before staging is replaced.
3535
+ * A runtime package embeds no Deno, so `engines.deno` stays a consumer
3536
+ * requirement and is not checked against this bundler.
3537
+ */
3538
+ async function resolveDenoPackageBundler(cwd, runner = resolveDenoBuildRunner()) {
3539
+ const version = await readCompilerVersion("deno", runner, cwd);
3540
+ if (!(0, import_satisfies.default)(version, `>=2.5.0`, { includePrerelease: true })) throw new Error(`Deno ${version} (${runner.command}) cannot bundle a Deno runtime package; deno ${DENO_PACKAGE_MIN_VERSION} or newer is required.\n Older deno bundle resolves package.json dependencies from the npm registry instead of the installed node_modules.
3541
+ crust does not install or upgrade Deno; put a newer deno first on PATH (e.g. with your version manager).`);
3542
+ return {
3543
+ runtime: "deno",
3544
+ runner,
3545
+ version
3546
+ };
3547
+ }
3548
+ /**
3549
+ * Bundle a Deno runtime package entry with native `deno bundle`, which resolves
3550
+ * dependencies through the project's own Deno setup (deno.json imports,
3551
+ * package.json with its installed node_modules) and inlines them into one file.
2245
3552
  *
2246
- * Runs with the same bun as compilation (`resolveBunBuildRunner`): bun on
2247
- * PATH, or a compiled standalone crust executable as `BUN_BE_BUN=1`, so
2248
- * arbitrary `.ts` entries run without a separate `bun` install.
3553
+ * Bundle the declared command directly so Deno preserves its `import.meta.main`
3554
+ * semantics without making dependencies main. Prepend the packaged-build marker
3555
+ * (`PACKAGED_BUILD_KEY`) to the bundled code so it runs before the inlined
3556
+ * application graph. The output has no shebang or permission flags: consumers
3557
+ * choose grants with `deno run` / `deno install`.
3558
+ * No env files or plugins: `deno bundle` has no PUBLIC_* filter or Bun plugins.
2249
3559
  */
3560
+ async function execDenoPackageBuild(entryPath, outfilePath, cwd, runner = resolveDenoBuildRunner()) {
3561
+ await runBuildProcess(runner, [
3562
+ "bundle",
3563
+ "--platform=deno",
3564
+ "--packages=bundle",
3565
+ "--output",
3566
+ outfilePath,
3567
+ resolve(cwd, entryPath)
3568
+ ], outfilePath, cwd);
3569
+ const output = await readFile(outfilePath, "utf8");
3570
+ await writeFile(outfilePath, `globalThis[Symbol.for(${JSON.stringify(PACKAGED_BUILD_KEY)})] = true;\n` + output.replace(/^#![^\n]*(?:\n|$)/, ""));
3571
+ }
2250
3572
  const SNAPSHOT_TIMEOUT_MS = 3e4;
3573
+ const SNAPSHOT_FORWARDED_SIGNALS = [
3574
+ "SIGINT",
3575
+ "SIGTERM",
3576
+ "SIGHUP",
3577
+ "SIGQUIT"
3578
+ ];
2251
3579
  function isBuildReport(value) {
2252
3580
  return isJsonObject(value) && Array.isArray(value.extensions) && value.extensions.every((extension) => isJsonObject(extension) && typeof extension.id === "string" && Array.isArray(extension.files) && extension.files.every((file) => typeof file === "string"));
2253
3581
  }
2254
- async function buildEntrypoint(entryPath, outDir, envFiles, io, cwd) {
3582
+ /**
3583
+ * Prepare a CLI entry's Command Snapshot in the user's project context.
3584
+ *
3585
+ * The entry runs as a subprocess with `CRUST_INTERNAL_SNAPSHOT_PATH` pointing
3586
+ * to a temporary file. `.execute()` validates and writes the command graph and
3587
+ * adjacent Build Report, then exits before any following entrypoint code can run.
3588
+ *
3589
+ * Runs with the same bun as compilation (`resolveBunBuildRunner`, or the
3590
+ * pinned Bun `runner`): bun on PATH, or a compiled standalone crust executable
3591
+ * as `BUN_BE_BUN=1`, so arbitrary `.ts` entries run without a separate `bun`
3592
+ * install.
3593
+ *
3594
+ * After `timeoutMs`, the entry's POSIX process group or live Windows process
3595
+ * tree is killed and preparation fails, even if a descendant still holds stderr.
3596
+ * On POSIX, SIGINT, SIGTERM, SIGHUP, SIGQUIT or a synchronous exit of this
3597
+ * process also kills the entry's group, and whatever remains of the group is
3598
+ * killed once preparation settles.
3599
+ */
3600
+ async function buildEntrypoint(entryPath, outDir, envFiles, io, cwd, runner = resolveBunBuildRunner(), timeoutMs = SNAPSHOT_TIMEOUT_MS) {
2255
3601
  const absoluteEntry = resolve(entryPath);
2256
3602
  const snapshotDir = await mkdtemp(join(tmpdir(), "crust-snapshot-"));
2257
3603
  const snapshotPath = join(snapshotDir, "command.json");
2258
3604
  const buildReportPath = join(snapshotDir, "build-report.json");
2259
3605
  try {
2260
- const runner = resolveBunBuildRunner();
2261
- const spawnedAt = Date.now();
3606
+ const detached = process.platform !== "win32";
2262
3607
  const proc = spawn(runner.command, [...toBunEnvFileArgs(envFiles), absoluteEntry], {
2263
3608
  env: {
2264
3609
  ...runner.env,
@@ -2271,15 +3616,47 @@ async function buildEntrypoint(entryPath, outDir, envFiles, io, cwd) {
2271
3616
  "ignore",
2272
3617
  "pipe"
2273
3618
  ],
2274
- timeout: SNAPSHOT_TIMEOUT_MS
3619
+ detached
2275
3620
  });
2276
- const stderrPromise = text(proc.stderr);
2277
- const [exitCode] = await once(proc, "close");
2278
- const stderr = (await stderrPromise).trim();
2279
- if (proc.signalCode !== null) {
2280
- if (Date.now() - spawnedAt >= SNAPSHOT_TIMEOUT_MS) throw new Error(`Command Snapshot preparation timed out after ${SNAPSHOT_TIMEOUT_MS / 1e3}s.\n An Extension build hook may be hanging. Use --no-validate to skip entry preparation and build hooks.`);
2281
- throw new Error(`Command Snapshot preparation was killed by ${proc.signalCode}.${stderr ? `\n${stderr}` : ""}`);
3621
+ let stop;
3622
+ const stopped = new Promise((_, reject) => {
3623
+ stop = (message) => {
3624
+ let failure = new Error(message);
3625
+ try {
3626
+ killProcessTree(proc);
3627
+ } catch (cause) {
3628
+ failure = new Error(`${message}\n Process-tree cleanup failed.`, { cause });
3629
+ }
3630
+ proc.stderr.destroy();
3631
+ reject(failure);
3632
+ };
3633
+ });
3634
+ const timer = setTimeout(() => stop(`Command Snapshot preparation timed out after ${timeoutMs / 1e3}s.\n An Extension build hook may be hanging. Use --no-validate to skip entry preparation and build hooks.`), timeoutMs);
3635
+ const onSignal = (signal) => {
3636
+ removeLifetimeListeners();
3637
+ stop(`Command Snapshot preparation was interrupted by ${signal}.`);
3638
+ if (process.listenerCount(signal) === 0) process.kill(process.pid, signal);
3639
+ };
3640
+ const killRemainingGroup = () => {
3641
+ try {
3642
+ killProcessTree(proc);
3643
+ } catch {}
3644
+ };
3645
+ const removeLifetimeListeners = () => {
3646
+ for (const signal of SNAPSHOT_FORWARDED_SIGNALS) process.removeListener(signal, onSignal);
3647
+ process.removeListener("exit", killRemainingGroup);
3648
+ };
3649
+ if (detached) {
3650
+ for (const signal of SNAPSHOT_FORWARDED_SIGNALS) process.prependListener(signal, onSignal);
3651
+ process.on("exit", killRemainingGroup);
2282
3652
  }
3653
+ const [rawStderr, [exitCode]] = await Promise.race([Promise.all([text(proc.stderr), once(proc, "close")]), stopped]).finally(() => {
3654
+ clearTimeout(timer);
3655
+ removeLifetimeListeners();
3656
+ if (detached) killRemainingGroup();
3657
+ });
3658
+ const stderr = rawStderr.trim();
3659
+ if (proc.signalCode !== null) throw new Error(`Command Snapshot preparation was killed by ${proc.signalCode}.${stderr ? `\n${stderr}` : ""}`);
2283
3660
  if (exitCode !== 0) throw new Error(stderr || "Command Snapshot preparation failed");
2284
3661
  if (stderr) {
2285
3662
  const styled = stderr.split("\n").map((line) => line.startsWith("Warning:") ? `${yellow("Warning:")}${line.slice(8)}` : line).join("\n");
@@ -2335,6 +3712,12 @@ async function buildEntrypoint(entryPath, outDir, envFiles, io, cwd) {
2335
3712
  //#region src/utils/distribute.ts
2336
3713
  /** Project-relative directory that `crust build` owns: wiped per build, read by `crust publish`. */
2337
3714
  const CRUST_DIR = ".crust";
3715
+ /**
3716
+ * What one build distributes: a runtime `package` (a root-only JavaScript
3717
+ * bundle that needs the runtime installed) or standalone `binary` executables
3718
+ * in platform packages behind a Node launcher.
3719
+ */
3720
+ const ARTIFACT_KINDS = ["package", "binary"];
2338
3721
  const MAX_PACKAGE_NAME_LENGTH = 214;
2339
3722
  const METADATA_KEYS = [
2340
3723
  "description",
@@ -2633,9 +4016,10 @@ function copyLicense(cwd, packageDirs) {
2633
4016
  const licensePath = join(cwd, licenseName);
2634
4017
  for (const packageDir of packageDirs) copyFileSync(licensePath, join(packageDir, licenseName));
2635
4018
  }
2636
- function writeDistributionManifest(stageDir, metadata, commands, targets, build) {
4019
+ function writeDistributionManifest(stageDir, metadata, identity, commands, targets, build) {
2637
4020
  const manifest = {
2638
4021
  version: metadata.version,
4022
+ ...identity,
2639
4023
  root: {
2640
4024
  name: metadata.rootPackageName,
2641
4025
  dir: "root",
@@ -2656,7 +4040,17 @@ function writeDistributionManifest(stageDir, metadata, commands, targets, build)
2656
4040
  writeJson(join(stageDir, "manifest.json"), manifest);
2657
4041
  return manifest;
2658
4042
  }
2659
- function stageDistributionPackages(cwd, stageDir, metadata, commands, targets, options) {
4043
+ /** `pkg` without `engines.node`, dropping `engines` once nothing else is left. */
4044
+ function omitNodeEngine(pkg) {
4045
+ const { engines, ...rest } = pkg;
4046
+ if (engines?.node === void 0) return pkg;
4047
+ const others = Object.entries(engines).filter(([runtime]) => runtime !== "node");
4048
+ return others.length > 0 ? {
4049
+ ...rest,
4050
+ engines: Object.fromEntries(others)
4051
+ } : rest;
4052
+ }
4053
+ function stageDistributionPackages(cwd, stageDir, metadata, platformMetadata, commands, targets, options) {
2660
4054
  const rootDir = join(stageDir, "root");
2661
4055
  const rootBinDir = join(rootDir, "bin");
2662
4056
  mkdirSync(rootBinDir, { recursive: true });
@@ -2664,7 +4058,7 @@ function stageDistributionPackages(cwd, stageDir, metadata, commands, targets, o
2664
4058
  copyRootReadme(cwd, rootDir);
2665
4059
  for (const target of targets) {
2666
4060
  mkdirSync(join(target.packageDir, "bin"), { recursive: true });
2667
- writeJson(join(target.packageDir, "package.json"), buildDistributionPlatformPackageJson(metadata, commands, target));
4061
+ writeJson(join(target.packageDir, "package.json"), buildDistributionPlatformPackageJson(platformMetadata, commands, target));
2668
4062
  }
2669
4063
  copyLicense(cwd, [rootDir, ...targets.map((target) => target.packageDir)]);
2670
4064
  }
@@ -2674,15 +4068,20 @@ function stageDistributionPackages(cwd, stageDir, metadata, commands, targets, o
2674
4068
  * Returns every generated package.json, launcher, and compiled command in staging order.
2675
4069
  */
2676
4070
  async function runDistributeBuild(plan, distribution, io, build) {
2677
- const metadata = resolveDistributionMetadata(plan.cwd, plan.userPackageJson);
4071
+ const sourceMetadata = resolveDistributionMetadata(plan.cwd, plan.userPackageJson);
2678
4072
  const commands = plan.entries.map((entry) => entry.command);
2679
4073
  const table = distribution.table;
4074
+ const platformMetadata = plan.runtime === "node" && table ? {
4075
+ ...sourceMetadata,
4076
+ rootPackageJson: omitNodeEngine(sourceMetadata.rootPackageJson)
4077
+ } : sourceMetadata;
4078
+ const metadata = sourceMetadata.exports === void 0 ? platformMetadata : sourceMetadata;
2680
4079
  const distributionTargets = table ? distribution.targets.map((target) => resolveDistributionTarget(table, plan.stageDir, metadata.rootPackageName, target)) : [];
2681
4080
  io.stdout(table ? `Staging ${bold(`${distributionTargets.length}`)} distribution target(s) in ${dim(plan.stageDir)}...` : `Staging a root-only npm package in ${dim(plan.stageDir)}...`);
2682
4081
  const artifactOutDir = plan.validate ? plan.outDir : void 0;
2683
4082
  const artifacts = collectArtifacts(artifactOutDir);
2684
4083
  const includeDirs = collectIncludeDirs(plan.cwd, plan.stageDir, plan.include, artifacts.names);
2685
- stageDistributionPackages(plan.cwd, plan.stageDir, metadata, commands, distributionTargets, {
4084
+ stageDistributionPackages(plan.cwd, plan.stageDir, metadata, platformMetadata, commands, distributionTargets, {
2686
4085
  artifactDirs: [...artifacts.names, ...includeDirs],
2687
4086
  manPages: artifacts.manPages
2688
4087
  });
@@ -2746,7 +4145,14 @@ async function runDistributeBuild(plan, distribution, io, build) {
2746
4145
  command
2747
4146
  });
2748
4147
  }
2749
- writeDistributionManifest(plan.stageDir, metadata, commands, distributionTargets, build);
4148
+ writeDistributionManifest(plan.stageDir, metadata, table ? {
4149
+ runtime: plan.runtime,
4150
+ artifact: "binary",
4151
+ embeddedRuntimeVersion: distribution.embeddedRuntimeVersion
4152
+ } : {
4153
+ runtime: plan.runtime,
4154
+ artifact: "package"
4155
+ }, commands, distributionTargets, build);
2750
4156
  const manifestPath = join(plan.stageDir, "manifest.json");
2751
4157
  io.stdout(`\n${green("✓")} Staged ${bold(`${distributionTargets.length + 1}`)} npm package(s) successfully:`);
2752
4158
  io.stdout(` ${rootDir}`);
@@ -2768,9 +4174,13 @@ function collectIncludeDirs(cwd, stageDir, include, artifactNames) {
2768
4174
  if (!existsSync(dir) || !statSync(dir).isDirectory()) throw new Error(`package.json crust.include entry ${JSON.stringify(entry)} is not a directory: ${dir}`);
2769
4175
  assertResolvesInsideProject(cwd, entry, dir);
2770
4176
  if (isWithin(stageDir, dir) || isWithin(dir, stageDir)) throw new Error(`package.json crust.include entry ${JSON.stringify(entry)} overlaps the build output directory ${stageDir}, which crust build replaces.`);
2771
- if (name.split(sep)[0] === "bin") throw new Error(`package.json crust.include entry ${JSON.stringify(entry)} conflicts with the generated npm bin directory.\n Include a directory with a different top-level name.`);
4177
+ if (name.toLowerCase().split(sep)[0] === "bin") throw new Error(`package.json crust.include entry ${JSON.stringify(entry)} conflicts with the generated npm bin directory.\n Include a directory with a different top-level name.`);
2772
4178
  const posixName = name.replaceAll(sep, "/");
2773
- const overlap = names.find((staged) => staged === posixName || staged.startsWith(`${posixName}/`) || posixName.startsWith(`${staged}/`));
4179
+ const key = posixName.toLowerCase();
4180
+ const overlap = names.find((staged) => {
4181
+ const stagedKey = staged.toLowerCase();
4182
+ return stagedKey === key || stagedKey.startsWith(`${key}/`) || key.startsWith(`${stagedKey}/`);
4183
+ });
2774
4184
  if (overlap !== void 0) throw new Error(`package.json crust.include entry ${JSON.stringify(entry)} overlaps "${overlap}", which is already staged (duplicate include or Extension artifact directory).`);
2775
4185
  names.push(posixName);
2776
4186
  includeDirs.push(posixName);
@@ -2837,7 +4247,8 @@ function collectArtifacts(artifactOutDir) {
2837
4247
  manPages: []
2838
4248
  };
2839
4249
  const names = readdirSync(artifactOutDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
2840
- if (names.includes("bin")) throw new Error(`Artifact directory "bin" in ${artifactOutDir} conflicts with the generated npm bin directory.\n Emit build artifacts under a different top-level name.`);
4250
+ const binName = names.find((name) => name.toLowerCase() === "bin");
4251
+ if (binName !== void 0) throw new Error(`Artifact directory "${binName}" in ${artifactOutDir} conflicts with the generated npm bin directory.\n Emit build artifacts under a different top-level name.`);
2841
4252
  return {
2842
4253
  names,
2843
4254
  manPages: names.includes("man") ? readdirSync(join(artifactOutDir, "man"), { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name).sort() : []
@@ -2848,6 +4259,7 @@ function collectArtifacts(artifactOutDir) {
2848
4259
  /** Also mirrored by `schema/package.json`; build.test.ts guards against drift. */
2849
4260
  const CRUST_CONFIG_KEYS = [
2850
4261
  "runtime",
4262
+ "artifact",
2851
4263
  "targets",
2852
4264
  "bunPlugins",
2853
4265
  "include"
@@ -2855,6 +4267,9 @@ const CRUST_CONFIG_KEYS = [
2855
4267
  function isBuildRuntime(value) {
2856
4268
  return typeof value === "string" && BUILD_RUNTIMES.some((runtime) => runtime === value);
2857
4269
  }
4270
+ function isArtifactKind(value) {
4271
+ return typeof value === "string" && ARTIFACT_KINDS.some((kind) => kind === value);
4272
+ }
2858
4273
  function isString(value) {
2859
4274
  return typeof value === "string";
2860
4275
  }
@@ -2873,6 +4288,10 @@ function readCrustConfig(pkg) {
2873
4288
  if (!isBuildRuntime(crust.runtime)) throw new Error(`Invalid package.json crust.runtime ${JSON.stringify(crust.runtime)}. Valid runtimes: ${BUILD_RUNTIMES.join(", ")}`);
2874
4289
  config.runtime = crust.runtime;
2875
4290
  }
4291
+ if (crust.artifact !== void 0) {
4292
+ if (!isArtifactKind(crust.artifact)) throw new Error(`Invalid package.json crust.artifact ${JSON.stringify(crust.artifact)}. Valid artifacts: ${ARTIFACT_KINDS.join(", ")}`);
4293
+ config.artifact = crust.artifact;
4294
+ }
2876
4295
  if (crust.targets !== void 0) {
2877
4296
  if (!isStringArray(crust.targets) || crust.targets.length === 0) throw new Error("package.json crust.targets must be a non-empty array of canonical compiler targets, e.g. [\"bun-linux-x64\", \"bun-darwin-arm64\"].");
2878
4297
  config.targets = crust.targets;
@@ -2997,19 +4416,44 @@ function resolveBinEntries(cwd, pkg) {
2997
4416
  };
2998
4417
  });
2999
4418
  }
4419
+ /** What earlier releases built for each runtime without being asked; quoted in the migration error. */
4420
+ const IMPLICIT_ARTIFACTS = {
4421
+ bun: "binary",
4422
+ deno: "binary",
4423
+ node: "package"
4424
+ };
4425
+ /** `option` (the `artifact` build option or `--artifact`) > package.json `crust.artifact`; no default. */
4426
+ function resolveArtifact(option, config, runtime) {
4427
+ if (option !== void 0 && !isArtifactKind(option)) throw new Error(`Invalid artifact ${JSON.stringify(option)}. Valid artifacts: ${ARTIFACT_KINDS.join(", ")}`);
4428
+ const artifact = option ?? config.artifact;
4429
+ if (artifact === void 0) {
4430
+ const previous = IMPLICIT_ARTIFACTS[runtime];
4431
+ throw new Error(`crust build needs an artifact kind: pass --artifact package|binary or set package.json "crust": { "artifact": "package" | "binary" }.
4432
+ package: one JavaScript bundle per command that runs on the consumer's installed runtime.
4433
+ binary: standalone executables in per-platform npm packages behind a Node launcher.
4434
+ Earlier versions built a ${previous} for the ${runtime} runtime implicitly; set "artifact": "${previous}" to keep that output.`);
4435
+ }
4436
+ return {
4437
+ runtime,
4438
+ artifact
4439
+ };
4440
+ }
3000
4441
  function planBuild(options, cwd) {
3001
4442
  const userPackageJson = readUserPackageJson(cwd);
3002
4443
  const config = readCrustConfig(userPackageJson);
3003
4444
  const { runtime, source: runtimeSource } = resolveBuildRuntime(userPackageJson, config, cwd);
4445
+ const selection = resolveArtifact(options.artifact, config, runtime);
3004
4446
  const entries = resolveBinEntries(cwd, userPackageJson);
3005
4447
  validatePackageIdentity(userPackageJson, "package.json");
3006
4448
  const envFiles = resolveEnvFilePaths(cwd, options.envFiles);
3007
4449
  const bunPlugins = config.bunPlugins ?? [];
3008
- if (runtime === "node" && options.targets?.length) throw new Error("--target cannot be used with the node runtime.\n Node builds produce one portable JavaScript artifact.");
3009
- if (runtime === "node" && config.targets !== void 0) throw new Error("package.json crust.targets is not supported with the node runtime.\n Node builds produce one portable JavaScript artifact; remove crust.targets or set crust.runtime to bun or deno.");
3010
- if (runtime === "deno" && options.minify) throw new Error("--minify is not supported with the deno runtime.\n deno compile has no minification step; drop the flag.");
3011
- if (runtime === "deno" && envFiles.length > 0) throw new Error("--env-file is not supported with the deno runtime.\n deno compile embeds every variable from the file into the binary — secrets included —\n with no PUBLIC_* filter. Load configuration at runtime instead (e.g. deno run --env-file).");
3012
- if (runtime === "deno" && bunPlugins.length > 0) throw new Error("package.json crust.bunPlugins is not supported with the deno runtime.\n deno compile has no Bun bundler; remove crust.bunPlugins or set crust.runtime to bun.");
4450
+ if (selection.artifact === "package" && options.targets?.length) throw new Error("--target cannot be used with runtime packages (artifact package).\n A runtime package is one portable JavaScript bundle; drop --target or use artifact binary.");
4451
+ if (selection.artifact === "package" && config.targets !== void 0) throw new Error("package.json crust.targets is not supported for runtime packages (artifact package).\n A runtime package is one portable JavaScript bundle; remove crust.targets or set \"artifact\": \"binary\".");
4452
+ const denoTool = selection.artifact === "binary" ? "deno compile" : "deno bundle";
4453
+ if (runtime === "deno" && options.minify) throw new Error(selection.artifact === "binary" ? "--minify is not supported with the deno runtime.\n deno compile has no minification step; drop the flag." : "--minify is not supported with the deno runtime.\n crust does not minify Deno runtime packages; drop the flag.");
4454
+ if (runtime === "deno" && envFiles.length > 0) throw new Error(selection.artifact === "binary" ? "--env-file is not supported with the deno runtime.\n deno compile embeds every variable from the file into the binary — secrets included —\n with no PUBLIC_* filter. Load configuration at runtime instead (e.g. deno run --env-file)." : "--env-file is not supported with the deno runtime.\n deno bundle has no PUBLIC_* build-time constants. Load configuration at runtime instead (e.g. deno run --env-file).");
4455
+ if (runtime === "deno" && bunPlugins.length > 0) throw new Error(`package.json crust.bunPlugins is not supported with the deno runtime.\n ${denoTool} has no Bun bundler; remove crust.bunPlugins or set crust.runtime to bun.`);
4456
+ if (runtime === "node" && selection.artifact === "binary" && bunPlugins.length > 0) throw new Error("package.json crust.bunPlugins is not supported for node standalone binaries.\n Remove crust.bunPlugins, or use artifact package or the bun runtime.");
3013
4457
  const stageDir = resolve(cwd, CRUST_DIR);
3014
4458
  const common = {
3015
4459
  cwd,
@@ -3024,39 +4468,102 @@ function planBuild(options, cwd) {
3024
4468
  validate: options.validate ?? true,
3025
4469
  minify: runtime === "deno" ? false : options.minify ?? true
3026
4470
  };
3027
- if (runtime === "node") return {
4471
+ if (selection.artifact === "package") return {
3028
4472
  ...common,
3029
- runtime
4473
+ runtime: selection.runtime,
4474
+ artifact: selection.artifact
3030
4475
  };
3031
4476
  const targetInputs = options.targets?.length ? options.targets : config.targets;
3032
- if (runtime === "bun") {
3033
- const targets = resolveTargets(BUN_TARGETS, targetInputs);
3034
- assertTargetsBuildableWithoutBun(targets);
3035
- return {
3036
- ...common,
3037
- runtime,
3038
- targets
3039
- };
3040
- }
4477
+ const { runtime: binaryRuntime, artifact } = selection;
4478
+ if (binaryRuntime === "bun") return {
4479
+ ...common,
4480
+ runtime: binaryRuntime,
4481
+ artifact,
4482
+ targets: resolveTargets(BUN_TARGETS, targetInputs)
4483
+ };
4484
+ if (binaryRuntime === "node") return {
4485
+ ...common,
4486
+ runtime: binaryRuntime,
4487
+ artifact,
4488
+ targets: resolveTargets(NODE_TARGETS, targetInputs)
4489
+ };
3041
4490
  return {
3042
4491
  ...common,
3043
- runtime,
4492
+ runtime: binaryRuntime,
4493
+ artifact,
3044
4494
  targets: resolveTargets(DENO_TARGETS, targetInputs)
3045
4495
  };
3046
4496
  }
3047
- /** Bun and Deno stage platform packages behind a Node launcher; Node stages a root-only bundle. */
3048
- async function runStagedBuild(plan, io, reports) {
3049
- if (plan.runtime === "bun") return runDistributeBuild(plan, {
3050
- table: BUN_TARGETS,
3051
- targets: plan.targets,
3052
- execute: (entry, outfile, target) => execBuild(entry, outfile, plan.minify, target, plan.envFiles, plan.cwd, plan.bunPlugins)
3053
- }, io, reports);
3054
- if (plan.runtime === "deno") return runDistributeBuild(plan, {
4497
+ /**
4498
+ * Selects every compiler the build uses, once, before `.crust/` is wiped, so a
4499
+ * missing tool or an engines mismatch keeps the previous stage. Binaries use
4500
+ * the selected compiler's own version (`resolveBinaryCompiler`, or
4501
+ * `resolveNodeBinaryCompiler` with tsdown's requirements for node, whose
4502
+ * targets' embedded Node binaries are also provisioned here); runtime
4503
+ * packages are bundled by Bun (or, for Deno, by `deno bundle`) and embed no
4504
+ * runtime, so engines stay a consumer requirement there and are not checked
4505
+ * against the bundler.
4506
+ */
4507
+ async function selectCompilers(plan, io) {
4508
+ if (plan.artifact === "package" && plan.runtime === "deno") {
4509
+ const bundler = await resolveDenoPackageBundler(plan.cwd);
4510
+ io.stdout(`${dim("Compiler:")} deno ${bundler.version} ${dim(`(${bundler.runner.command}, deno bundle)`)}`);
4511
+ io.stderr("Deno runtime packages are experimental: they are bundled with deno bundle, which Deno marks experimental.");
4512
+ return {
4513
+ snapshotRunner: plan.validate ? resolveBunBuildRunner() : void 0,
4514
+ stage: (reports) => runDistributeBuild(plan, { execute: (entry, outfile) => execDenoPackageBuild(entry, outfile, plan.cwd, bundler.runner) }, io, reports)
4515
+ };
4516
+ }
4517
+ if (plan.artifact === "package") {
4518
+ const runner = resolveBunBuildRunner();
4519
+ const bundle = plan.runtime === "bun" ? execBunPackageBuild : execNodeBuild;
4520
+ return {
4521
+ snapshotRunner: runner,
4522
+ stage: (reports) => runDistributeBuild(plan, { execute: (entry, outfile) => bundle(entry, outfile, plan.minify, plan.envFiles, plan.cwd, plan.bunPlugins, runner) }, io, reports)
4523
+ };
4524
+ }
4525
+ const printCompiler = (compiler) => io.stdout(`${dim("Compiler:")} ${compiler.runtime} ${compiler.version} ${dim(`(${compiler.runner.command})`)}`);
4526
+ if (plan.runtime === "node") {
4527
+ const compiler = await resolveNodeBinaryCompiler(plan.userPackageJson, plan.cwd);
4528
+ printCompiler(compiler);
4529
+ await provisionNodeExeTargets(plan.targets, plan.cwd, compiler);
4530
+ const bunRunner = resolveBunBuildRunner();
4531
+ const distribution = {
4532
+ table: NODE_TARGETS,
4533
+ targets: plan.targets,
4534
+ embeddedRuntimeVersion: compiler.version,
4535
+ execute: (entry, outfile, target) => execNodeBinaryBuild(entry, outfile, plan.minify, target, plan.envFiles, plan.cwd, compiler, bunRunner, io.stderr)
4536
+ };
4537
+ return {
4538
+ snapshotRunner: bunRunner,
4539
+ stage: (reports) => runDistributeBuild(plan, distribution, io, reports)
4540
+ };
4541
+ }
4542
+ const compiler = await resolveBinaryCompiler(plan.runtime, plan.userPackageJson, plan.cwd);
4543
+ printCompiler(compiler);
4544
+ if (plan.runtime === "bun") {
4545
+ assertTargetsBuildableWithoutBun(plan.targets, void 0, compiler.runner);
4546
+ const distribution = {
4547
+ table: BUN_TARGETS,
4548
+ targets: plan.targets,
4549
+ embeddedRuntimeVersion: compiler.version,
4550
+ execute: (entry, outfile, target) => execBuild(entry, outfile, plan.minify, target, plan.envFiles, plan.cwd, plan.bunPlugins, compiler.runner)
4551
+ };
4552
+ return {
4553
+ snapshotRunner: compiler.runner,
4554
+ stage: (reports) => runDistributeBuild(plan, distribution, io, reports)
4555
+ };
4556
+ }
4557
+ const distribution = {
3055
4558
  table: DENO_TARGETS,
3056
4559
  targets: plan.targets,
3057
- execute: (entry, outfile, target) => execDenoBuild(entry, outfile, target, plan.cwd)
3058
- }, io, reports);
3059
- return runDistributeBuild(plan, { execute: (entry, outfile) => execNodeBuild(entry, outfile, plan.minify, plan.envFiles, plan.cwd, plan.bunPlugins) }, io, reports);
4560
+ embeddedRuntimeVersion: compiler.version,
4561
+ execute: (entry, outfile, target) => execDenoBuild(entry, outfile, target, plan.cwd, compiler.runner)
4562
+ };
4563
+ return {
4564
+ snapshotRunner: plan.validate ? resolveBunBuildRunner() : void 0,
4565
+ stage: (reports) => runDistributeBuild(plan, distribution, io, reports)
4566
+ };
3060
4567
  }
3061
4568
  /**
3062
4569
  * Prepares every entry's Command Snapshot and checks that its root command is
@@ -3065,13 +4572,13 @@ async function runStagedBuild(plan, io, reports) {
3065
4572
  * the merge fails on any path two entries both write. Returns each command's
3066
4573
  * Build Report.
3067
4574
  */
3068
- async function prepareEntries(plan, io) {
4575
+ async function prepareEntries(plan, io, runner) {
3069
4576
  const owners = /* @__PURE__ */ new Map();
3070
4577
  const reports = {};
3071
4578
  for (const { command, entryPath } of plan.entries) {
3072
4579
  const entryOutDir = await mkdtemp(join(tmpdir(), "crust-artifacts-"));
3073
4580
  try {
3074
- const { snapshot, build: report } = await buildEntrypoint(entryPath, entryOutDir, plan.envFiles, io, plan.cwd);
4581
+ const { snapshot, build: report } = await buildEntrypoint(entryPath, entryOutDir, plan.envFiles, io, plan.cwd, runner);
3075
4582
  if (snapshot.meta.name !== command) throw new Error(`package.json bin ${JSON.stringify(command)} builds ${entryPath}, whose root command is named ${JSON.stringify(snapshot.meta.name)}.\n The installed command, help, man pages, and skills use the root command name, so new Crust(name) must match the bin key; rename one of them.`);
3076
4583
  printBuildReport(command, report, io.stdout);
3077
4584
  mergeEntryArtifacts(entryOutDir, plan.outDir, command, owners);
@@ -3085,27 +4592,36 @@ async function prepareEntries(plan, io) {
3085
4592
  }
3086
4593
  return reports;
3087
4594
  }
3088
- const activeBuilds = /* @__PURE__ */ new Set();
3089
4595
  /**
3090
- * Stages the publishable npm tree in `<cwd>/.crust`: a root package with one
3091
- * `bin/<command>.js` per package.json `bin` entry, each a Node launcher (Bun,
3092
- * Deno) or the bundle itself (Node), plus one platform package per target for
3093
- * Bun and Deno holding one binary per command. Command names and source
3094
- * entries come from `bin`; the runtime, Bun plugins, and extra directories
3095
- * from package.json `crust`. Compilation and Command Snapshots run in bun
4596
+ * Stages the publishable npm tree in `<cwd>/.crust` for one runtime/artifact
4597
+ * combination: a root package with one `bin/<command>.js` per package.json
4598
+ * `bin` entry, each the command's bundle (artifact `package`) or a Node
4599
+ * launcher (artifact `binary`), plus for binaries one platform package per
4600
+ * target holding one executable per command. Command names and source entries
4601
+ * come from `bin`; the runtime, artifact, Bun plugins, and extra directories
4602
+ * from package.json `crust`. Bundling and Command Snapshots run in bun
3096
4603
  * subprocesses (bun on PATH, or the running Bun executable), so this works
3097
- * under Node as well when Bun is installed.
4604
+ * under Node as well when Bun is installed; Deno binaries and Deno runtime
4605
+ * packages (experimental, deno 2.5.0 or newer) need deno on PATH, and Node
4606
+ * binaries a node on PATH that Crust's tsdown can build executables with.
3098
4607
  *
3099
- * Throws on any failure. Planning failures (bad options or package.json) leave
3100
- * the previous `.crust/` stage untouched; failures after planning leave a
3101
- * wiped stage without a completion `manifest.json`. Overlapping calls for the
3102
- * same real project directory in this process are rejected before staging.
4608
+ * Throws on any failure. Planning and compiler-selection failures (bad
4609
+ * options or package.json, a missing or hung compiler, an engines mismatch) leave the
4610
+ * previous `.crust/` stage untouched; later failures leave a wiped stage
4611
+ * without a completion `manifest.json`. Overlapping calls for the same real
4612
+ * project directory are rejected across processes before staging. An interrupted
4613
+ * process may leave `.crust.lock`; remove it only when no build is running.
3103
4614
  */
3104
4615
  async function build(options = {}) {
3105
4616
  const cwd = resolve(options.cwd ?? process.cwd());
3106
4617
  const project = realpathSync(cwd);
3107
- if (activeBuilds.has(project)) throw new Error(`crust build is already building ${project}`);
3108
- activeBuilds.add(project);
4618
+ const lockDir = join(project, `${CRUST_DIR}.lock`);
4619
+ try {
4620
+ mkdirSync(lockDir);
4621
+ } catch (error) {
4622
+ if (!isErrnoException(error) || error.code !== "EEXIST") throw error;
4623
+ throw new Error(`crust build is already building ${project}, or an interrupted build left its lock.\n Remove ${lockDir} only after confirming no build is running.`, { cause: error });
4624
+ }
3109
4625
  try {
3110
4626
  const onLog = options.onLog ?? (() => {});
3111
4627
  const io = {
@@ -3114,50 +4630,22 @@ async function build(options = {}) {
3114
4630
  };
3115
4631
  const plan = planBuild(options, cwd);
3116
4632
  io.stdout(`${dim("Runtime:")} ${plan.runtime} ${dim(`(${plan.runtimeSource})`)}`);
4633
+ io.stdout(`${dim("Artifact:")} ${plan.artifact}`);
4634
+ const compilers = await selectCompilers(plan, io);
3117
4635
  rmSync(plan.stageDir, {
3118
4636
  recursive: true,
3119
4637
  force: true
3120
4638
  });
3121
- const reports = plan.validate ? await prepareEntries(plan, io) : void 0;
3122
- const artifacts = await runStagedBuild(plan, io, reports);
4639
+ const reports = plan.validate ? await prepareEntries(plan, io, compilers.snapshotRunner) : void 0;
4640
+ const artifacts = await compilers.stage(reports);
3123
4641
  return {
3124
4642
  stageDir: plan.stageDir,
3125
4643
  artifacts,
3126
4644
  ...reports ? { reports } : {}
3127
4645
  };
3128
4646
  } finally {
3129
- activeBuilds.delete(project);
4647
+ rmdirSync(lockDir);
3130
4648
  }
3131
4649
  }
3132
- defineCommand("build", { description: "Build your CLI for Bun, Deno, or Node" }, (command) => command.flags({
3133
- name: "target",
3134
- type: "string",
3135
- multiple: true,
3136
- description: `Canonical compiler target(s), or "${HOST_TARGET}" for this machine; repeatable. Omit to stage package.json crust.targets, or every Bun/Deno target`,
3137
- short: "t"
3138
- }, {
3139
- name: "env-file",
3140
- type: "string",
3141
- multiple: true,
3142
- description: "Explicit env file(s) used for build-time constants; repeatable"
3143
- }, {
3144
- name: "validate",
3145
- type: "boolean",
3146
- description: "Materialize command definitions before compiling; --no-validate also skips Extension build hooks",
3147
- default: true
3148
- }, {
3149
- name: "minify",
3150
- type: "boolean",
3151
- description: "Minify the output (default for bun and node; unsupported for deno)"
3152
- }).action(async ({ flags, stdout, stderr }) => {
3153
- await build({
3154
- cwd: process.cwd(),
3155
- targets: flags.target,
3156
- envFiles: flags["env-file"],
3157
- minify: flags.minify,
3158
- validate: flags.validate,
3159
- onLog: (line, stream) => (stream === "stderr" ? stderr : stdout)(line)
3160
- });
3161
- }));
3162
4650
  //#endregion
3163
4651
  export { build };