@crustjs/crust-linux-x64 0.4.0 → 0.5.0

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