@crustjs/crust-darwin-x64 0.5.5 → 0.6.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,20 +1,20 @@
1
- import { accessSync, constants, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, rmdirSync, statSync, writeFileSync } from "node:fs";
1
+ import { copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, rmdirSync, statSync, writeFileSync } from "node:fs";
2
2
  import { chmod, copyFile, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
- import { delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
5
- import { fileURLToPath, pathToFileURL } from "node:url";
4
+ import { dirname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
6
5
  import { randomBytes } from "node:crypto";
7
- import { spawn, spawnSync } from "node:child_process";
6
+ import { fileURLToPath, pathToFileURL } from "node:url";
7
+ import { spawn } from "node:child_process";
8
8
  import { once } from "node:events";
9
9
  import { text } from "node:stream/consumers";
10
- //#region \0rolldown/runtime.js
10
+ // \0rolldown/runtime.js
11
11
  var __create = Object.create;
12
12
  var __defProp = Object.defineProperty;
13
13
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
14
14
  var __getOwnPropNames = Object.getOwnPropertyNames;
15
15
  var __getProtoOf = Object.getPrototypeOf;
16
16
  var __hasOwnProp = Object.prototype.hasOwnProperty;
17
- var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
17
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod, mod.exports), cb = null), mod.exports);
18
18
  var __copyProps = (to, from, except, desc) => {
19
19
  if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
20
20
  key = keys[i];
@@ -29,26 +29,40 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
29
  value: mod,
30
30
  enumerable: true
31
31
  }) : target, mod));
32
- //#endregion
33
- //#region ../utils/dist/artifacts.js
32
+ // ../utils/dist/signals.js
33
+ /** SigIgn bits from the first read, taken before Crust installs any listener. */
34
+ let ignoredMask;
34
35
  /**
35
- * Set by `crust build` while it prepares the Command Snapshot: the absolute
36
- * entry-isolated directory Extension build hooks write into. Core reads it
37
- * to run the hooks; `resolveArtifactDir` reads it so sections evaluated during
38
- * that run see the artifacts being built instead of the wiped `.crust/root`.
39
- */
40
- const BUILD_OUT_DIR_ENV = "CRUST_INTERNAL_BUILD_OUT_DIR";
41
- /**
42
- * `Symbol.for` registry key of the packaged-build marker, for bundlers without a
43
- * `define` option (native `deno bundle`). `crust build` prepends
44
- * `globalThis[Symbol.for(PACKAGED_BUILD_KEY)] = true` to the command bundle,
45
- * before the inlined application graph, so every bundled copy sees it.
46
- * The registry key is shared across module copies; the value is never read from
47
- * the environment, so detection needs no Deno env permission.
36
+ * Whether `signal` is inherited as ignored, such as SIGHUP under `nohup` or SIGINT for a
37
+ * background job in a script. Adding a listener replaces that disposition and removing the
38
+ * last one restores the default action, so code that listens temporarily skips ignored
39
+ * signals to keep the process immune to them. Only Bun keeps an inherited ignore; Node
40
+ * resets it at startup, so this is always false there and skips the read.
41
+ *
42
+ * shortcut: Linux-only (`/proc/self/status`); macOS Bun still loses the ignore. Remove once
43
+ * Bun restores the previous disposition on listener removal (oven-sh/bun#47574).
48
44
  */
49
- const PACKAGED_BUILD_KEY = "crustjs.packagedBuild";
50
- //#endregion
51
- //#region ../core/dist/invocation-BstcKEMM.js
45
+ function isSignalIgnored(signal) {
46
+ if (process.platform !== "linux" || process.versions.bun === void 0) return false;
47
+ ignoredMask ??= readIgnoredMask();
48
+ const number = process.getBuiltinModule("node:os").constants.signals[signal];
49
+ return number !== void 0 && number <= 32 && (ignoredMask >>> number - 1 & 1) === 1;
50
+ }
51
+ function readIgnoredMask() {
52
+ try {
53
+ const status = process.getBuiltinModule("node:fs").readFileSync("/proc/self/status", "utf8");
54
+ const hex = /^SigIgn:\s*([0-9a-f]+)$/m.exec(status)?.[1];
55
+ return hex ? Number.parseInt(hex.slice(-8), 16) : 0;
56
+ } catch {
57
+ return 0;
58
+ }
59
+ }
60
+ // ../utils/dist/error.js
61
+ /** Narrows a caught Node.js system error to its stable errno contract. */
62
+ function isErrnoException(error) {
63
+ return error instanceof Error && "code" in error && typeof error.code === "string";
64
+ }
65
+ // ../core/dist/invocation-B_3ZsKUB.js
52
66
  /**
53
67
  * A typed error for runtime recipe, Extension, Context, documentation, argv, and validation failures.
54
68
  *
@@ -81,6 +95,9 @@ var CrustError = class extends Error {
81
95
  this.code = code;
82
96
  this.details = details[0];
83
97
  }
98
+ static [Symbol.hasInstance](value) {
99
+ return Function.prototype[Symbol.hasInstance].call(this, value);
100
+ }
84
101
  is(code) {
85
102
  return Object.is(this.code, code);
86
103
  }
@@ -96,7 +113,6 @@ var CrustError = class extends Error {
96
113
  };
97
114
  }
98
115
  };
99
- Object.freeze({ [Symbol("crust.handled")]: true });
100
116
  /**
101
117
  * Snapshot subprocess protocol used by first-party build tooling.
102
118
  *
@@ -106,13 +122,23 @@ Object.freeze({ [Symbol("crust.handled")]: true });
106
122
  * without dispatching a Command Action. In-process callers use `Crust.snapshot()`.
107
123
  *
108
124
  * Only source entries run by `crust build` honor it: finished Bun/Node bundles carry
109
- * `process.env.CRUST_INTERNAL_BUILD === "1"` as a literal and compile the protocol out;
110
- * packaged Deno bundles set the `isPackagedBuild()` marker and skip it without
111
- * reading the environment.
125
+ * `process.env.CRUST_INTERNAL_BUILD === "1"` as a literal and compile the protocol out.
112
126
  */
113
127
  const SNAPSHOT_PATH_ENV = "CRUST_INTERNAL_SNAPSHOT_PATH";
114
- //#endregion
115
- //#region ../core/dist/tooling.js
128
+ // ../utils/dist/artifacts.js
129
+ /**
130
+ * Set by `crust build` while it prepares the Command Snapshot: the absolute
131
+ * entry-isolated directory Extension build hooks write into. Core reads it
132
+ * to run the hooks; `resolveArtifactDir` reads it so sections evaluated during
133
+ * that run see the artifacts being built instead of the wiped `.crust/root`.
134
+ */
135
+ const BUILD_OUT_DIR_ENV = "CRUST_INTERNAL_BUILD_OUT_DIR";
136
+ // ../utils/dist/json.js
137
+ /** Narrow a JSON document root to an object (not array/scalar/null). */
138
+ function isJsonObject(value) {
139
+ return typeof value === "object" && value !== null && !Array.isArray(value);
140
+ }
141
+ // ../core/dist/tooling.js
116
142
  const INSTALLED_COMMAND_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
117
143
  /** {@link isInstalledCommandName}'s rule as prose, for error messages: "Use <rule>." */
118
144
  const INSTALLED_COMMAND_NAME_RULE = "letters, digits, \".\", \"_\", and \"-\", starting with a letter or digit";
@@ -124,83 +150,18 @@ const INSTALLED_COMMAND_NAME_RULE = "letters, digits, \".\", \"_\", and \"-\", s
124
150
  function isInstalledCommandName(name) {
125
151
  return INSTALLED_COMMAND_NAME_PATTERN.test(name);
126
152
  }
127
- new Intl.Segmenter(void 0, { granularity: "grapheme" });
128
- new RegExp([
153
+ [
129
154
  String.raw`(?:\x1b\[|\x9b)[^\x1b\x18\x1a\x9c\x40-\x7e]*[\x40-\x7e\x18\x1a\x9c]?`,
130
155
  String.raw`(?:\x1b\]|\x9d)[^\x07\x1b\x18\x1a\x9c]*(?:\x07|\x1b\\|[\x18\x1a\x9c])?`,
131
156
  String.raw`(?:\x1b[PX^_]|[\x90\x98\x9e\x9f])[^\x1b\x18\x1a\x9c]*(?:\x1b\\|[\x18\x1a\x9c])?`,
132
157
  String.raw`\x1b[\x20-\x2f][^\x1b]?`,
133
158
  String.raw`\x1b[\x18\x1a\x9c\x30-\x7e]?`
134
- ].join("|"), "g");
135
- const modifierCodes = {
136
- /** Bold / increased intensity. */
137
- bold: [1, 22],
138
- /** Dim / decreased intensity. */
139
- dim: [2, 22],
140
- italic: [3, 23],
141
- underline: [4, 24],
142
- /** Inverse / reverse video. */
143
- inverse: [7, 27],
144
- /** Hidden / conceal. */
145
- hidden: [8, 28],
146
- /** Strikethrough / crossed out. */
147
- strikethrough: [9, 29]
148
- };
149
- const colorCodes = {
150
- black: [30, 39],
151
- red: [31, 39],
152
- green: [32, 39],
153
- yellow: [33, 39],
154
- blue: [34, 39],
155
- magenta: [35, 39],
156
- cyan: [36, 39],
157
- white: [37, 39],
158
- /** Bright black (gray). */
159
- gray: [90, 39],
160
- brightRed: [91, 39],
161
- brightGreen: [92, 39],
162
- brightYellow: [93, 39],
163
- brightBlue: [94, 39],
164
- brightMagenta: [95, 39],
165
- brightCyan: [96, 39],
166
- brightWhite: [97, 39],
167
- bgBlack: [40, 49],
168
- bgRed: [41, 49],
169
- bgGreen: [42, 49],
170
- bgYellow: [43, 49],
171
- bgBlue: [44, 49],
172
- bgMagenta: [45, 49],
173
- bgCyan: [46, 49],
174
- bgWhite: [47, 49],
175
- bgBrightBlack: [100, 49],
176
- bgBrightRed: [101, 49],
177
- bgBrightGreen: [102, 49],
178
- bgBrightYellow: [103, 49],
179
- bgBrightBlue: [104, 49],
180
- bgBrightMagenta: [105, 49],
181
- bgBrightCyan: [106, 49],
182
- bgBrightWhite: [107, 49]
183
- };
184
- const styleMethodCodes = {
185
- ...modifierCodes,
186
- ...colorCodes
187
- };
188
- const styleMethodNames = Object.freeze(Object.keys(styleMethodCodes));
189
- const styleMethodPairs = Object.freeze(Object.fromEntries(styleMethodNames.map((name) => {
190
- const [open, close] = styleMethodCodes[name];
191
- return [name, {
192
- open: `\x1b[${open}m`,
193
- close: `\x1b[${close}m`
194
- }];
195
- })));
196
- function isModifierName(name) {
197
- return Object.hasOwn(modifierCodes, name);
198
- }
159
+ ].join("|");
199
160
  function read(overrides, key, environment) {
200
161
  return overrides !== void 0 && key in overrides ? overrides[key] : environment;
201
162
  }
202
163
  function readTTY(overrides) {
203
- return read(overrides, "isTTY", process.stdout?.isTTY) ?? false;
164
+ return (overrides !== void 0 && "isTTY" in overrides ? overrides.isTTY : process.stdout?.isTTY) ?? false;
204
165
  }
205
166
  function readForceColor(overrides) {
206
167
  return read(overrides, "forceColor", process.env.FORCE_COLOR);
@@ -250,7 +211,7 @@ function detectDepth(colorTerm, term) {
250
211
  * / `TERM` conventions; no bespoke environment variables are introduced.
251
212
  *
252
213
  * @param mode - The color emission mode.
253
- * @param overrides - Optional overrides for deterministic testing.
214
+ * @param overrides - Optional capability inputs, such as another stream's TTY state or fixed test values.
254
215
  * @returns The resolved {@link ColorDepth} tier.
255
216
  *
256
217
  * @example
@@ -307,748 +268,6 @@ function resolveModifierCapability(mode, overrides) {
307
268
  if (forceColor !== void 0) return !forceColorDisables(forceColor);
308
269
  return readTTY(overrides);
309
270
  }
310
- const namedColorValues = {
311
- aliceblue: [
312
- 240,
313
- 248,
314
- 255
315
- ],
316
- antiquewhite: [
317
- 250,
318
- 235,
319
- 215
320
- ],
321
- aqua: [
322
- 0,
323
- 255,
324
- 255
325
- ],
326
- aquamarine: [
327
- 127,
328
- 255,
329
- 212
330
- ],
331
- azure: [
332
- 240,
333
- 255,
334
- 255
335
- ],
336
- beige: [
337
- 245,
338
- 245,
339
- 220
340
- ],
341
- bisque: [
342
- 255,
343
- 228,
344
- 196
345
- ],
346
- black: [
347
- 0,
348
- 0,
349
- 0
350
- ],
351
- blanchedalmond: [
352
- 255,
353
- 235,
354
- 205
355
- ],
356
- blue: [
357
- 0,
358
- 0,
359
- 255
360
- ],
361
- blueviolet: [
362
- 138,
363
- 43,
364
- 226
365
- ],
366
- brown: [
367
- 165,
368
- 42,
369
- 42
370
- ],
371
- burlywood: [
372
- 222,
373
- 184,
374
- 135
375
- ],
376
- cadetblue: [
377
- 95,
378
- 158,
379
- 160
380
- ],
381
- chartreuse: [
382
- 127,
383
- 255,
384
- 0
385
- ],
386
- chocolate: [
387
- 210,
388
- 105,
389
- 30
390
- ],
391
- coral: [
392
- 255,
393
- 127,
394
- 80
395
- ],
396
- cornflowerblue: [
397
- 100,
398
- 149,
399
- 237
400
- ],
401
- cornsilk: [
402
- 255,
403
- 248,
404
- 220
405
- ],
406
- crimson: [
407
- 220,
408
- 20,
409
- 60
410
- ],
411
- cyan: [
412
- 0,
413
- 255,
414
- 255
415
- ],
416
- darkblue: [
417
- 0,
418
- 0,
419
- 139
420
- ],
421
- darkcyan: [
422
- 0,
423
- 139,
424
- 139
425
- ],
426
- darkgoldenrod: [
427
- 184,
428
- 134,
429
- 11
430
- ],
431
- darkgray: [
432
- 169,
433
- 169,
434
- 169
435
- ],
436
- darkgreen: [
437
- 0,
438
- 100,
439
- 0
440
- ],
441
- darkgrey: [
442
- 169,
443
- 169,
444
- 169
445
- ],
446
- darkkhaki: [
447
- 189,
448
- 183,
449
- 107
450
- ],
451
- darkmagenta: [
452
- 139,
453
- 0,
454
- 139
455
- ],
456
- darkolivegreen: [
457
- 85,
458
- 107,
459
- 47
460
- ],
461
- darkorange: [
462
- 255,
463
- 140,
464
- 0
465
- ],
466
- darkorchid: [
467
- 153,
468
- 50,
469
- 204
470
- ],
471
- darkred: [
472
- 139,
473
- 0,
474
- 0
475
- ],
476
- darksalmon: [
477
- 233,
478
- 150,
479
- 122
480
- ],
481
- darkseagreen: [
482
- 143,
483
- 188,
484
- 143
485
- ],
486
- darkslateblue: [
487
- 72,
488
- 61,
489
- 139
490
- ],
491
- darkslategray: [
492
- 47,
493
- 79,
494
- 79
495
- ],
496
- darkslategrey: [
497
- 47,
498
- 79,
499
- 79
500
- ],
501
- darkturquoise: [
502
- 0,
503
- 206,
504
- 209
505
- ],
506
- darkviolet: [
507
- 148,
508
- 0,
509
- 211
510
- ],
511
- deeppink: [
512
- 255,
513
- 20,
514
- 147
515
- ],
516
- deepskyblue: [
517
- 0,
518
- 191,
519
- 255
520
- ],
521
- dimgray: [
522
- 105,
523
- 105,
524
- 105
525
- ],
526
- dimgrey: [
527
- 105,
528
- 105,
529
- 105
530
- ],
531
- dodgerblue: [
532
- 30,
533
- 144,
534
- 255
535
- ],
536
- firebrick: [
537
- 178,
538
- 34,
539
- 34
540
- ],
541
- floralwhite: [
542
- 255,
543
- 250,
544
- 240
545
- ],
546
- forestgreen: [
547
- 34,
548
- 139,
549
- 34
550
- ],
551
- fuchsia: [
552
- 255,
553
- 0,
554
- 255
555
- ],
556
- gainsboro: [
557
- 220,
558
- 220,
559
- 220
560
- ],
561
- ghostwhite: [
562
- 248,
563
- 248,
564
- 255
565
- ],
566
- gold: [
567
- 255,
568
- 215,
569
- 0
570
- ],
571
- goldenrod: [
572
- 218,
573
- 165,
574
- 32
575
- ],
576
- gray: [
577
- 128,
578
- 128,
579
- 128
580
- ],
581
- green: [
582
- 0,
583
- 128,
584
- 0
585
- ],
586
- greenyellow: [
587
- 173,
588
- 255,
589
- 47
590
- ],
591
- grey: [
592
- 128,
593
- 128,
594
- 128
595
- ],
596
- honeydew: [
597
- 240,
598
- 255,
599
- 240
600
- ],
601
- hotpink: [
602
- 255,
603
- 105,
604
- 180
605
- ],
606
- indianred: [
607
- 205,
608
- 92,
609
- 92
610
- ],
611
- indigo: [
612
- 75,
613
- 0,
614
- 130
615
- ],
616
- ivory: [
617
- 255,
618
- 255,
619
- 240
620
- ],
621
- khaki: [
622
- 240,
623
- 230,
624
- 140
625
- ],
626
- lavender: [
627
- 230,
628
- 230,
629
- 250
630
- ],
631
- lavenderblush: [
632
- 255,
633
- 240,
634
- 245
635
- ],
636
- lawngreen: [
637
- 124,
638
- 252,
639
- 0
640
- ],
641
- lemonchiffon: [
642
- 255,
643
- 250,
644
- 205
645
- ],
646
- lightblue: [
647
- 173,
648
- 216,
649
- 230
650
- ],
651
- lightcoral: [
652
- 240,
653
- 128,
654
- 128
655
- ],
656
- lightcyan: [
657
- 224,
658
- 255,
659
- 255
660
- ],
661
- lightgoldenrodyellow: [
662
- 250,
663
- 250,
664
- 210
665
- ],
666
- lightgray: [
667
- 211,
668
- 211,
669
- 211
670
- ],
671
- lightgreen: [
672
- 144,
673
- 238,
674
- 144
675
- ],
676
- lightgrey: [
677
- 211,
678
- 211,
679
- 211
680
- ],
681
- lightpink: [
682
- 255,
683
- 182,
684
- 193
685
- ],
686
- lightsalmon: [
687
- 255,
688
- 160,
689
- 122
690
- ],
691
- lightseagreen: [
692
- 32,
693
- 178,
694
- 170
695
- ],
696
- lightskyblue: [
697
- 135,
698
- 206,
699
- 250
700
- ],
701
- lightslategray: [
702
- 119,
703
- 136,
704
- 153
705
- ],
706
- lightslategrey: [
707
- 119,
708
- 136,
709
- 153
710
- ],
711
- lightsteelblue: [
712
- 176,
713
- 196,
714
- 222
715
- ],
716
- lightyellow: [
717
- 255,
718
- 255,
719
- 224
720
- ],
721
- lime: [
722
- 0,
723
- 255,
724
- 0
725
- ],
726
- limegreen: [
727
- 50,
728
- 205,
729
- 50
730
- ],
731
- linen: [
732
- 250,
733
- 240,
734
- 230
735
- ],
736
- magenta: [
737
- 255,
738
- 0,
739
- 255
740
- ],
741
- maroon: [
742
- 128,
743
- 0,
744
- 0
745
- ],
746
- mediumaquamarine: [
747
- 102,
748
- 205,
749
- 170
750
- ],
751
- mediumblue: [
752
- 0,
753
- 0,
754
- 205
755
- ],
756
- mediumorchid: [
757
- 186,
758
- 85,
759
- 211
760
- ],
761
- mediumpurple: [
762
- 147,
763
- 112,
764
- 219
765
- ],
766
- mediumseagreen: [
767
- 60,
768
- 179,
769
- 113
770
- ],
771
- mediumslateblue: [
772
- 123,
773
- 104,
774
- 238
775
- ],
776
- mediumspringgreen: [
777
- 0,
778
- 250,
779
- 154
780
- ],
781
- mediumturquoise: [
782
- 72,
783
- 209,
784
- 204
785
- ],
786
- mediumvioletred: [
787
- 199,
788
- 21,
789
- 133
790
- ],
791
- midnightblue: [
792
- 25,
793
- 25,
794
- 112
795
- ],
796
- mintcream: [
797
- 245,
798
- 255,
799
- 250
800
- ],
801
- mistyrose: [
802
- 255,
803
- 228,
804
- 225
805
- ],
806
- moccasin: [
807
- 255,
808
- 228,
809
- 181
810
- ],
811
- navajowhite: [
812
- 255,
813
- 222,
814
- 173
815
- ],
816
- navy: [
817
- 0,
818
- 0,
819
- 128
820
- ],
821
- oldlace: [
822
- 253,
823
- 245,
824
- 230
825
- ],
826
- olive: [
827
- 128,
828
- 128,
829
- 0
830
- ],
831
- olivedrab: [
832
- 107,
833
- 142,
834
- 35
835
- ],
836
- orange: [
837
- 255,
838
- 165,
839
- 0
840
- ],
841
- orangered: [
842
- 255,
843
- 69,
844
- 0
845
- ],
846
- orchid: [
847
- 218,
848
- 112,
849
- 214
850
- ],
851
- palegoldenrod: [
852
- 238,
853
- 232,
854
- 170
855
- ],
856
- palegreen: [
857
- 152,
858
- 251,
859
- 152
860
- ],
861
- paleturquoise: [
862
- 175,
863
- 238,
864
- 238
865
- ],
866
- palevioletred: [
867
- 219,
868
- 112,
869
- 147
870
- ],
871
- papayawhip: [
872
- 255,
873
- 239,
874
- 213
875
- ],
876
- peachpuff: [
877
- 255,
878
- 218,
879
- 185
880
- ],
881
- peru: [
882
- 205,
883
- 133,
884
- 63
885
- ],
886
- pink: [
887
- 255,
888
- 192,
889
- 203
890
- ],
891
- plum: [
892
- 221,
893
- 160,
894
- 221
895
- ],
896
- powderblue: [
897
- 176,
898
- 224,
899
- 230
900
- ],
901
- purple: [
902
- 128,
903
- 0,
904
- 128
905
- ],
906
- rebeccapurple: [
907
- 102,
908
- 51,
909
- 153
910
- ],
911
- red: [
912
- 255,
913
- 0,
914
- 0
915
- ],
916
- rosybrown: [
917
- 188,
918
- 143,
919
- 143
920
- ],
921
- royalblue: [
922
- 65,
923
- 105,
924
- 225
925
- ],
926
- saddlebrown: [
927
- 139,
928
- 69,
929
- 19
930
- ],
931
- salmon: [
932
- 250,
933
- 128,
934
- 114
935
- ],
936
- sandybrown: [
937
- 244,
938
- 164,
939
- 96
940
- ],
941
- seagreen: [
942
- 46,
943
- 139,
944
- 87
945
- ],
946
- seashell: [
947
- 255,
948
- 245,
949
- 238
950
- ],
951
- sienna: [
952
- 160,
953
- 82,
954
- 45
955
- ],
956
- silver: [
957
- 192,
958
- 192,
959
- 192
960
- ],
961
- skyblue: [
962
- 135,
963
- 206,
964
- 235
965
- ],
966
- slateblue: [
967
- 106,
968
- 90,
969
- 205
970
- ],
971
- slategray: [
972
- 112,
973
- 128,
974
- 144
975
- ],
976
- slategrey: [
977
- 112,
978
- 128,
979
- 144
980
- ],
981
- snow: [
982
- 255,
983
- 250,
984
- 250
985
- ],
986
- springgreen: [
987
- 0,
988
- 255,
989
- 127
990
- ],
991
- steelblue: [
992
- 70,
993
- 130,
994
- 180
995
- ],
996
- tan: [
997
- 210,
998
- 180,
999
- 140
1000
- ],
1001
- teal: [
1002
- 0,
1003
- 128,
1004
- 128
1005
- ],
1006
- thistle: [
1007
- 216,
1008
- 191,
1009
- 216
1010
- ],
1011
- tomato: [
1012
- 255,
1013
- 99,
1014
- 71
1015
- ],
1016
- turquoise: [
1017
- 64,
1018
- 224,
1019
- 208
1020
- ],
1021
- violet: [
1022
- 238,
1023
- 130,
1024
- 238
1025
- ],
1026
- wheat: [
1027
- 245,
1028
- 222,
1029
- 179
1030
- ],
1031
- white: [
1032
- 255,
1033
- 255,
1034
- 255
1035
- ],
1036
- whitesmoke: [
1037
- 245,
1038
- 245,
1039
- 245
1040
- ],
1041
- yellow: [
1042
- 255,
1043
- 255,
1044
- 0
1045
- ],
1046
- yellowgreen: [
1047
- 154,
1048
- 205,
1049
- 50
1050
- ]
1051
- };
1052
271
  /**
1053
272
  * Apply an ANSI style pair to a string with nesting-safe composition.
1054
273
  *
@@ -1081,422 +300,57 @@ function applyStyle(text, style) {
1081
300
  if (result.includes(close)) result = result.replaceAll(close, close + open);
1082
301
  return open + result + close;
1083
302
  }
1084
- /** Foreground close: matches the close of every static fg color (`\x1b[39m`). */
1085
- const FG_CLOSE = "\x1B[39m";
1086
- /** Background close: matches the close of every static bg color (`\x1b[49m`). */
1087
- const BG_CLOSE = "\x1B[49m";
1088
- /** Foreground/background extended-color SGR introducers. */
1089
- const FG_INTRODUCER = "\x1B[38;";
1090
- const BG_INTRODUCER = "\x1B[48;";
1091
- /** Quote supported inputs, falling back to `String()` for hostile runtime values. */
1092
- function describeInput(input) {
1093
- try {
1094
- return JSON.stringify(input) ?? String(input);
1095
- } catch {
1096
- return String(input);
1097
- }
1098
- }
1099
- function isNamedColor(value) {
1100
- return Object.hasOwn(namedColorValues, value);
1101
- }
1102
- /** Parse a supported color into an RGB triple. */
1103
- function parseRgb(input) {
1104
- if (Array.isArray(input)) {
1105
- if (input.length === 3 && input.every((channel) => Number.isInteger(channel) && channel >= 0 && channel <= 255)) return [
1106
- input[0],
1107
- input[1],
1108
- input[2]
1109
- ];
1110
- } else if (typeof input === "string") {
1111
- const value = input.toLowerCase();
1112
- if (isNamedColor(value)) return namedColorValues[value];
1113
- const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(value)?.[1];
1114
- if (hex) {
1115
- const expanded = hex.length === 3 ? hex.split("").map((digit) => digit + digit).join("") : hex;
1116
- return [
1117
- Number.parseInt(expanded.slice(0, 2), 16),
1118
- Number.parseInt(expanded.slice(2, 4), 16),
1119
- Number.parseInt(expanded.slice(4, 6), 16)
1120
- ];
1121
- }
1122
- const rgb = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/.exec(value) ?? /^rgb\(\s*(\d{1,3})\s+(\d{1,3})\s+(\d{1,3})\s*\)$/.exec(value);
1123
- if (rgb) {
1124
- const channels = [
1125
- Number(rgb[1]),
1126
- Number(rgb[2]),
1127
- Number(rgb[3])
1128
- ];
1129
- if (channels.every((channel) => channel <= 255)) return channels;
1130
- }
1131
- }
1132
- throw new TypeError(`Invalid color input: ${describeInput(input)}`);
1133
- }
1134
- function rgbToAnsi256(r, g, b) {
1135
- const levels = [
1136
- 0,
1137
- 95,
1138
- 135,
1139
- 175,
1140
- 215,
1141
- 255
1142
- ];
1143
- const nearest = (channel) => levels.reduce((best, value, index) => Math.abs(value - channel) < Math.abs(levels[best] - channel) ? index : best, 0);
1144
- const [ir, ig, ib] = [
1145
- nearest(r),
1146
- nearest(g),
1147
- nearest(b)
1148
- ];
1149
- const grayIndex = Math.min(23, Math.max(0, Math.round(((r + g + b) / 3 - 8) / 10)));
1150
- const gray = 8 + grayIndex * 10;
1151
- const distance = (cr, cg, cb) => (r - cr) ** 2 + (g - cg) ** 2 + (b - cb) ** 2;
1152
- return distance(gray, gray, gray) < distance(levels[ir], levels[ig], levels[ib]) ? 232 + grayIndex : 16 + 36 * ir + 6 * ig + ib;
1153
- }
1154
- /**
1155
- * Quantize `[r, g, b]` to a foreground SGR parameter (`30`–`37`, `90`–`97`).
1156
- * Same algorithm as `ansi-styles` / `chalk`: bucket each channel at 50%,
1157
- * pack into a 3-bit base color, then add 60 for bright when the max
1158
- * channel rounds up. Call sites add `+10` for backgrounds.
1159
- */
1160
- function rgbToAnsi16Param(r, g, b) {
1161
- const brightnessBucket = Math.round(Math.max(r, g, b) / 127.5);
1162
- if (brightnessBucket === 0) return 30;
1163
- let ansi = 30 + ((Math.round(b / 255) << 2 | Math.round(g / 255) << 1 | Math.round(r / 255)) & 7);
1164
- if (brightnessBucket === 2) ansi += 60;
1165
- return ansi;
1166
- }
1167
- /**
1168
- * Foreground SGR open sequence at `depth`.
1169
- */
1170
- function fgOpen(input, depth) {
1171
- const [r, g, b] = parseRgb(input);
1172
- if (depth === "16") return `\x1b[${rgbToAnsi16Param(r, g, b)}m`;
1173
- if (depth === "256") return `\x1b[38;5;${rgbToAnsi256(r, g, b)}m`;
1174
- return `\x1b[38;2;${r};${g};${b}m`;
1175
- }
1176
- /**
1177
- * Background SGR open sequence at `depth`. For `truecolor` / `256`,
1178
- * derived from {@link fgOpen} by swapping the `\x1b[38;` introducer for
1179
- * `\x1b[48;` (both the truecolor and 256-color forms use it). For `16`,
1180
- * quantized directly to a real background SGR.
1181
- */
1182
- function bgOpen(input, depth) {
1183
- if (depth === "16") {
1184
- const [r, g, b] = parseRgb(input);
1185
- return `\x1b[${rgbToAnsi16Param(r, g, b) + 10}m`;
1186
- }
1187
- return fgOpen(input, depth).replace(FG_INTRODUCER, BG_INTRODUCER);
1188
- }
1189
- function colorPair(kind, input, depth) {
1190
- if (depth === "none") {
1191
- parseRgb(input);
1192
- return {
1193
- open: "",
1194
- close: ""
1195
- };
1196
- }
1197
- return {
1198
- open: kind === "fg" ? fgOpen(input, depth) : bgOpen(input, depth),
1199
- close: kind === "fg" ? FG_CLOSE : BG_CLOSE
1200
- };
1201
- }
1202
- function paint(kind, text, input, depth) {
1203
- const pair = colorPair(kind, input, depth);
1204
- return depth === "none" ? text : applyStyle(text, pair);
1205
- }
1206
- const OSC = "\x1B]";
1207
- const ST = "\x1B\\";
1208
- const HYPERLINK_CLOSE = `${OSC}8;;${ST}`;
1209
- function assertMatches(value, label, pattern, suffix = "") {
1210
- if (!pattern.test(value)) throw new TypeError(`Invalid ${label}: ${JSON.stringify(value)} must contain only printable ASCII characters${suffix}.`);
1211
- }
1212
- function serializeParams(options) {
1213
- const id = options?.id;
1214
- if (id === void 0 || id === "") return "";
1215
- assertMatches(id, "hyperlink id", /^[\x20-\x7e]*$/);
1216
- if (id.includes(":") || id.includes(";")) throw new TypeError("Invalid hyperlink id: \":\" and \";\" are reserved by the OSC 8 format.");
1217
- return `id=${id}`;
1218
- }
1219
- /**
1220
- * Create an OSC 8 hyperlink escape pair for `url`. Backs {@link link} and
1221
- * the `link` method on style instances; also used to validate URLs when
1222
- * hyperlink emission is disabled.
1223
- *
1224
- * @param url - Target URL. Must contain only printable ASCII characters
1225
- * and no spaces (per the OSC 8 spec). URL-encode characters outside
1226
- * that range before passing.
1227
- * @param options - Optional {@link HyperlinkOptions} — currently just
1228
- * `id` for grouping multi-segment links.
1229
- * @returns An {@link AnsiPair} whose `open` carries the URL and `close`
1230
- * terminates the hyperlink.
1231
- * @throws {TypeError} If `url` contains spaces or non-printable ASCII,
1232
- * or if `options.id` contains `":"` / `";"` (reserved by OSC 8) or
1233
- * non-printable characters.
1234
- *
1235
- * @internal
1236
- */
1237
- function linkCode(url, options) {
1238
- assertMatches(url, "hyperlink URL", /^[\x21-\x7e]*$/, " without spaces");
1239
- const params = serializeParams(options);
1240
- return {
1241
- open: `${OSC}8;${params};${url}${ST}`,
1242
- close: HYPERLINK_CLOSE
1243
- };
1244
- }
1245
- /**
1246
- * Wrap `text` in OSC 8 hyperlink escape sequences.
1247
- *
1248
- * @param text - The visible label.
1249
- * @param url - Target URL. Must contain only printable ASCII characters
1250
- * and no spaces (per the OSC 8 spec); URL-encode anything outside that
1251
- * range before passing.
1252
- * @param options - Optional {@link HyperlinkOptions}.
1253
- * @returns The styled string.
1254
- * @throws {TypeError} If `url` contains spaces or non-printable ASCII,
1255
- * or if `options.id` contains `":"` / `";"` (reserved by OSC 8) or
1256
- * non-printable characters.
1257
- *
1258
- * @example
1259
- * ```ts
1260
- * import { link } from "@crustjs/style";
1261
- *
1262
- * console.log(link("docs", "https://crustjs.com"));
1263
- * console.log(link("page 1", "https://example.com/p1", { id: "intro" }));
1264
- * ```
1265
- */
1266
- function link$1(text, url, options) {
1267
- return applyStyle(text, linkCode(url, options));
1268
- }
1269
- const dynamicColorKinds = ["fg", "bg"];
1270
- function stepIsModifier(step) {
1271
- return step.kind === "named" && isModifierName(step.name);
1272
- }
1273
- function stepPair(step, colorDepth) {
1274
- return step.kind === "named" ? styleMethodPairs[step.name] : colorPair(step.kind, step.input, colorDepth);
1275
- }
1276
- function colorStep(kind, input) {
1277
- const step = {
1278
- kind,
1279
- input
1280
- };
1281
- stepPair(step, "truecolor");
1282
- return Array.isArray(input) ? {
1283
- kind,
1284
- input: [
1285
- input[0],
1286
- input[1],
1287
- input[2]
1288
- ]
1289
- } : step;
1290
- }
1291
- function applyChain(text, steps, resolveCapabilities) {
1292
- if (text == null) return "";
1293
- let result = String(text);
1294
- if (result === "") return result;
1295
- const capabilities = resolveCapabilities();
1296
- const { modifiersEnabled, colorsEnabled } = capabilities;
1297
- for (let i = steps.length - 1; i >= 0; i--) {
1298
- const step = steps[i];
1299
- if (step === void 0) continue;
1300
- if (stepIsModifier(step) ? !modifiersEnabled : !colorsEnabled) continue;
1301
- result = applyStyle(result, stepPair(step, capabilities.colorDepth));
1302
- }
1303
- return result;
1304
- }
1305
303
  function isTemplateStringsArray(value) {
1306
304
  return Array.isArray(value) && "raw" in value && Array.isArray(value.raw);
1307
305
  }
1308
- function buildChainableStyleFactory(resolveCapabilities, runtime) {
1309
- const cache = /* @__PURE__ */ new Map();
1310
- const pairDepth = runtime ? "truecolor" : resolveCapabilities().colorDepth;
1311
- function createChainableStyle(steps) {
1312
- const key = steps.map((step) => step.kind === "named" ? step.name : `~${stepPair(step, "truecolor").open}`).join("|");
1313
- const cached = cache.get(key);
1314
- if (cached) return cached;
1315
- const styleFn = ((first, ...rest) => {
1316
- if (isTemplateStringsArray(first)) {
1317
- let text = "";
1318
- for (let i = 0; i < first.length; i++) {
1319
- text += first[i] ?? "";
1320
- if (i < rest.length) text += String(rest[i]);
1321
- }
1322
- return applyChain(text, steps, resolveCapabilities);
1323
- }
1324
- return applyChain(first, steps, resolveCapabilities);
1325
- });
1326
- if (cache.size >= 1024) cache.delete(cache.keys().next().value);
1327
- cache.set(key, styleFn);
1328
- for (const name of styleMethodNames) Object.defineProperty(styleFn, name, {
1329
- configurable: false,
1330
- enumerable: true,
1331
- get() {
1332
- return createChainableStyle([...steps, {
1333
- kind: "named",
1334
- name
1335
- }]);
1336
- }
1337
- });
1338
- for (const kind of dynamicColorKinds) Object.defineProperty(styleFn, kind, {
1339
- configurable: false,
1340
- enumerable: true,
1341
- value: (input) => createChainableStyle([...steps, colorStep(kind, input)]),
1342
- writable: false
1343
- });
1344
- let open = "";
1345
- let close = "";
1346
- for (const step of steps) {
1347
- const pair = stepPair(step, pairDepth);
1348
- open += pair.open;
1349
- close = pair.close + close;
1350
- }
1351
- Object.defineProperty(styleFn, "open", {
1352
- value: open,
1353
- writable: false,
1354
- configurable: false,
1355
- enumerable: true
1356
- });
1357
- Object.defineProperty(styleFn, "close", {
1358
- value: close,
1359
- writable: false,
1360
- configurable: false,
1361
- enumerable: true
1362
- });
1363
- return Object.freeze(styleFn);
306
+ /**
307
+ * Resolve the text of a style function call: a direct `fn(text)` call passes
308
+ * `text` through; a tagged template `` fn`a ${b}` `` joins its cooked strings
309
+ * with each value coerced via `String(...)`.
310
+ */
311
+ function callText(first, values) {
312
+ if (!isTemplateStringsArray(first)) return first;
313
+ let text = "";
314
+ for (let i = 0; i < first.length; i++) {
315
+ text += first[i] ?? "";
316
+ if (i < values.length) text += String(values[i]);
1364
317
  }
1365
- return createChainableStyle;
1366
- }
1367
- function buildStyleMethods(createChainableStyle) {
1368
- const methods = {};
1369
- for (const methodName of styleMethodNames) methods[methodName] = createChainableStyle([{
1370
- kind: "named",
1371
- name: methodName
1372
- }]);
1373
- return methods;
1374
- }
1375
- function resolveStyleCapabilities(options) {
1376
- const mode = options?.mode ?? "auto";
1377
- const modifiersEnabled = resolveModifierCapability(mode, options?.overrides);
1378
- const colorDepth = resolveColorDepth(mode, options?.overrides);
1379
- return {
1380
- modifiersEnabled,
1381
- colorDepth,
1382
- colorsEnabled: colorDepth !== "none",
1383
- trueColorEnabled: colorDepth === "truecolor"
1384
- };
318
+ return text;
1385
319
  }
1386
- function createStyleInstance(options, runtime) {
1387
- const resolveCapabilities = runtime ? resolveStyleCapabilities : (() => {
1388
- const capabilities = resolveStyleCapabilities(options);
1389
- return () => capabilities;
1390
- })();
1391
- const createChainableStyle = buildChainableStyleFactory(resolveCapabilities, runtime);
1392
- const methods = buildStyleMethods(createChainableStyle);
1393
- const instance = {
1394
- get enabled() {
1395
- const { modifiersEnabled, colorsEnabled } = resolveCapabilities();
1396
- return modifiersEnabled || colorsEnabled;
1397
- },
1398
- get colorsEnabled() {
1399
- return resolveCapabilities().colorsEnabled;
1400
- },
1401
- get trueColorEnabled() {
1402
- return resolveCapabilities().trueColorEnabled;
1403
- },
1404
- get colorDepth() {
1405
- return resolveCapabilities().colorDepth;
1406
- },
1407
- link(text, url, hyperlinkOptions) {
1408
- if (resolveCapabilities().modifiersEnabled) return link$1(text, url, hyperlinkOptions);
1409
- linkCode(url, hyperlinkOptions);
1410
- return text;
1411
- },
1412
- ...Object.fromEntries(dynamicColorKinds.map((kind) => [kind, (...args) => {
1413
- if (args.length === 1) return createChainableStyle([colorStep(kind, args[0])]);
1414
- return paint(kind, args[0], args[1], resolveCapabilities().colorDepth);
1415
- }])),
1416
- ...methods
320
+ /** A plain (non-chaining) style function that applies `pair` while `enabled()` is true. */
321
+ function plainStyleFn(pair, enabled) {
322
+ return (first, ...values) => {
323
+ const text = callText(first, values);
324
+ return enabled() ? applyStyle(text, pair) : text == null ? "" : String(text);
1417
325
  };
1418
- return Object.freeze(instance);
1419
326
  }
1420
- /**
1421
- * Default style instance using `"auto"` mode.
1422
- *
1423
- * Emits color ANSI codes when stdout is a TTY and `NO_COLOR` is not set.
1424
- * Non-color modifiers (bold, italic, etc.) follow TTY only; `FORCE_COLOR`,
1425
- * when set, decides unconditionally for both.
1426
- * Import this for convenient access without explicit configuration.
1427
- *
1428
- * @example
1429
- * ```ts
1430
- * import { style } from "@crustjs/style";
1431
- *
1432
- * console.log(style.bold("hello"));
1433
- * console.log(style.red("error"));
1434
- * ```
1435
- */
1436
- const style = createStyleInstance(void 0, true);
1437
- style.black;
1438
- style.red;
1439
- const green = style.green;
1440
- const yellow = style.yellow;
1441
- style.blue;
1442
- style.magenta;
1443
- const cyan = style.cyan;
1444
- style.white;
1445
- style.gray;
1446
- style.brightRed;
1447
- style.brightGreen;
1448
- style.brightYellow;
1449
- style.brightBlue;
1450
- style.brightMagenta;
1451
- style.brightCyan;
1452
- style.brightWhite;
1453
- style.bgBlack;
1454
- style.bgRed;
1455
- style.bgGreen;
1456
- style.bgYellow;
1457
- style.bgBlue;
1458
- style.bgMagenta;
1459
- style.bgCyan;
1460
- style.bgWhite;
1461
- style.bgBrightBlack;
1462
- style.bgBrightRed;
1463
- style.bgBrightGreen;
1464
- style.bgBrightYellow;
1465
- style.bgBrightBlue;
1466
- style.bgBrightMagenta;
1467
- style.bgBrightCyan;
1468
- style.bgBrightWhite;
1469
- const bold = style.bold;
1470
- const dim = style.dim;
1471
- style.italic;
1472
- style.underline;
1473
- style.inverse;
1474
- style.hidden;
1475
- style.strikethrough;
1476
- style.link;
1477
- style.fg;
1478
- style.bg;
1479
- //#endregion
1480
- //#region ../utils/dist/error.js
1481
- /** Narrows a caught Node.js system error to its stable errno contract. */
1482
- function isErrnoException(error) {
1483
- return error instanceof Error && "code" in error && typeof error.code === "string";
1484
- }
1485
- //#endregion
1486
- //#region ../utils/dist/json.js
1487
- /** Narrow a JSON document root to an object (not array/scalar/null). */
1488
- function isJsonObject(value) {
1489
- return typeof value === "object" && value !== null && !Array.isArray(value);
327
+ const modifiersEnabled = () => resolveModifierCapability("auto");
328
+ const colorsEnabled = () => resolveColorDepth("auto") !== "none";
329
+ function modifier(open, close) {
330
+ return plainStyleFn({
331
+ open: `\x1b[${open}m`,
332
+ close: `\x1b[${close}m`
333
+ }, modifiersEnabled);
1490
334
  }
1491
- //#endregion
1492
- //#region ../utils/dist/path.js
335
+ function color(open, close) {
336
+ return plainStyleFn({
337
+ open: `\x1b[${open}m`,
338
+ close: `\x1b[${close}m`
339
+ }, colorsEnabled);
340
+ }
341
+ const green = /* @__PURE__ */ color(32, 39);
342
+ const yellow = /* @__PURE__ */ color(33, 39);
343
+ const cyan = /* @__PURE__ */ color(36, 39);
344
+ const bold = /* @__PURE__ */ modifier(1, 22);
345
+ const dim = /* @__PURE__ */ modifier(2, 22);
346
+ // ../utils/dist/path.js
1493
347
  /** Returns whether child is parent itself or lies below it. */
1494
348
  function isWithin(parent, child) {
349
+ const { isAbsolute, relative, sep } = process.getBuiltinModule("node:path");
1495
350
  const path = relative(parent, child);
1496
351
  return path === "" || !isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`);
1497
352
  }
1498
- //#endregion
1499
- //#region src/artifacts.ts
353
+ // src/artifacts.ts
1500
354
  /**
1501
355
  * `crust.include` directories normalized to cwd-relative POSIX names. They are
1502
356
  * staged exactly like Extension artifacts.
@@ -1591,8 +445,46 @@ function collectArtifacts(artifactOutDir) {
1591
445
  manPages: names.includes("man") ? readdirSync(join(artifactOutDir, "man"), { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name).sort() : []
1592
446
  };
1593
447
  }
1594
- //#endregion
1595
- //#region ../utils/dist/process.js
448
+ // ../utils/dist/process.js
449
+ const LIFETIME_SIGNALS = [
450
+ "SIGINT",
451
+ "SIGTERM",
452
+ "SIGHUP",
453
+ "SIGQUIT"
454
+ ];
455
+ const bindings = /* @__PURE__ */ new Set();
456
+ function onLifetimeSignal(signal) {
457
+ const pending = [...bindings];
458
+ bindings.clear();
459
+ removeLifetimeListeners();
460
+ for (const binding of pending) binding.onSignal(signal);
461
+ if (process.listenerCount(signal) === 0) process.kill(process.pid, signal);
462
+ }
463
+ function onLifetimeExit() {
464
+ for (const binding of bindings) binding.onExit();
465
+ }
466
+ function removeLifetimeListeners() {
467
+ for (const signal of LIFETIME_SIGNALS) process.removeListener(signal, onLifetimeSignal);
468
+ process.removeListener("exit", onLifetimeExit);
469
+ }
470
+ /**
471
+ * Ties a detached POSIX child group to this process, which the group no longer shares
472
+ * terminal signals with. Until the returned release runs, SIGINT, SIGTERM, SIGHUP or SIGQUIT
473
+ * calls `onSignal` once and is then re-raised when no other listener handles it, and a
474
+ * synchronous exit (`process.exit`, an uncaught error) calls `onExit`. SIGKILL and native
475
+ * crashes cannot be observed and still orphan the group. A signal this process inherited as
476
+ * ignored (Bun on Linux, such as SIGHUP under `nohup`) stays ignored and is never listened for.
477
+ */
478
+ function bindToProcessLifetime(binding) {
479
+ if (bindings.size === 0) {
480
+ for (const signal of LIFETIME_SIGNALS) if (!isSignalIgnored(signal)) process.prependListener(signal, onLifetimeSignal);
481
+ process.on("exit", onLifetimeExit);
482
+ }
483
+ bindings.add(binding);
484
+ return () => {
485
+ if (bindings.delete(binding) && bindings.size === 0) removeLifetimeListeners();
486
+ };
487
+ }
1596
488
  const WINDOWS_SHELL_UNSAFE = /[\0\r\n"%!^`<>&|]/;
1597
489
  const WINDOWS_SHELL_META = /([()\][%!^"`<>&|;, *?])/g;
1598
490
  function escapeWindowsShellArgument(value) {
@@ -1604,7 +496,7 @@ function escapeWindowsShellArgument(value) {
1604
496
  /** @internal Build Node's escaped Windows command-shim workaround. */
1605
497
  function getWindowsShimCommand(command, args, shell, platform = process.platform) {
1606
498
  if (shell || platform !== "win32" || !/\.(cmd|bat)$/i.test(command)) return null;
1607
- const shimCommand = win32.normalize(command);
499
+ const shimCommand = process.getBuiltinModule("node:path").win32.normalize(command);
1608
500
  for (const [index, value] of [shimCommand, ...args].entries()) if (WINDOWS_SHELL_UNSAFE.test(value)) {
1609
501
  const label = index === 0 ? "command" : `argument ${index}`;
1610
502
  throw new Error(`Windows command shim ${label} ${JSON.stringify(value)} contains unsafe shell characters`);
@@ -1630,6 +522,7 @@ function killProcessTree(child) {
1630
522
  if (child.pid === void 0) return;
1631
523
  if (process.platform === "win32") {
1632
524
  if (child.exitCode === null && child.signalCode === null) {
525
+ const { spawnSync } = process.getBuiltinModule("node:child_process");
1633
526
  const result = spawnSync("taskkill", [
1634
527
  "/pid",
1635
528
  String(child.pid),
@@ -1663,6 +556,8 @@ async function runProcess(command, args = [], options = {}) {
1663
556
  const windowsShimCommand = getWindowsShimCommand(command, args, options.shell);
1664
557
  const collect = (options.stdio ?? "collect") === "collect";
1665
558
  const collectStdout = collect && options.stdout !== "ignore";
559
+ const detached = options.timeout !== void 0 && process.platform !== "win32";
560
+ const { spawn } = process.getBuiltinModule("node:child_process");
1666
561
  const proc = spawn(windowsShimCommand?.command ?? command, windowsShimCommand?.args ?? args, {
1667
562
  cwd: options.cwd,
1668
563
  env: options.env,
@@ -1672,31 +567,42 @@ async function runProcess(command, args = [], options = {}) {
1672
567
  collectStdout ? "pipe" : "ignore",
1673
568
  "pipe"
1674
569
  ] : "inherit",
1675
- detached: options.timeout !== void 0 && process.platform !== "win32",
570
+ detached,
1676
571
  windowsVerbatimArguments: windowsShimCommand?.windowsVerbatimArguments
1677
572
  });
1678
- let timer;
1679
- const deadline = new Promise((_, reject) => {
1680
- if (options.timeout === void 0) return;
1681
- const timeout = options.timeout;
1682
- timer = setTimeout(() => {
1683
- let failure = /* @__PURE__ */ new Error(`${[command, ...args].join(" ")} did not exit within ${timeout} ms and was killed.`);
573
+ const commandLine = [command, ...args].join(" ");
574
+ let stop;
575
+ const stopped = new Promise((_, reject) => {
576
+ stop = (message, reason) => {
577
+ let failure = new Error(message);
1684
578
  try {
1685
579
  killProcessTree(proc);
1686
580
  } catch (cause) {
1687
- failure = new Error("Process-tree cleanup failed after timeout.", { cause });
581
+ failure = new Error(`Process-tree cleanup failed after ${reason}.`, { cause });
1688
582
  }
1689
583
  proc.stdout?.destroy();
1690
584
  proc.stderr?.destroy();
1691
585
  reject(failure);
1692
- }, timeout);
586
+ };
1693
587
  });
588
+ const timeout = options.timeout;
589
+ const timer = timeout === void 0 ? void 0 : setTimeout(() => stop(`${commandLine} did not exit within ${timeout} ms and was killed.`, "timeout"), timeout);
590
+ const release = detached ? bindToProcessLifetime({
591
+ onSignal: (signal) => stop(`${commandLine} was interrupted by ${signal}.`, signal),
592
+ onExit: () => {
593
+ try {
594
+ killProcessTree(proc);
595
+ } catch {}
596
+ }
597
+ }) : () => {};
598
+ const { text } = process.getBuiltinModule("node:stream/consumers");
599
+ const { once } = process.getBuiltinModule("node:events");
1694
600
  try {
1695
601
  const [stdout, stderr, [exitCode]] = await Promise.race([Promise.all([
1696
602
  collectStdout ? text(proc.stdout) : "",
1697
603
  collect ? text(proc.stderr) : "",
1698
604
  once(proc, "close")
1699
- ]), deadline]);
605
+ ]), stopped]);
1700
606
  return {
1701
607
  exitCode,
1702
608
  stdout,
@@ -1704,9 +610,11 @@ async function runProcess(command, args = [], options = {}) {
1704
610
  };
1705
611
  } finally {
1706
612
  clearTimeout(timer);
613
+ release();
1707
614
  }
1708
615
  }
1709
616
  function isExecutableFile(path) {
617
+ const { accessSync, constants, statSync } = process.getBuiltinModule("node:fs");
1710
618
  try {
1711
619
  accessSync(path, constants.X_OK);
1712
620
  return statSync(path).isFile();
@@ -1716,6 +624,7 @@ function isExecutableFile(path) {
1716
624
  }
1717
625
  /** Resolve a bare executable name from PATH (and PATHEXT on Windows). */
1718
626
  function which(command) {
627
+ const { delimiter, extname, join, resolve, sep } = process.getBuiltinModule("node:path");
1719
628
  if (command.includes(sep) || command.includes("/")) return isExecutableFile(command) ? command : null;
1720
629
  const extensions = process.platform === "win32" && !extname(command) ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";") : [""];
1721
630
  for (const directory of process.env.PATH?.split(delimiter) ?? []) for (const extension of extensions) {
@@ -1724,8 +633,7 @@ function which(command) {
1724
633
  }
1725
634
  return null;
1726
635
  }
1727
- //#endregion
1728
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/lrucache.js
636
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/lrucache.js
1729
637
  var require_lrucache = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1730
638
  var LRUCache = class {
1731
639
  constructor() {
@@ -1757,8 +665,7 @@ var require_lrucache = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1757
665
  };
1758
666
  module.exports = LRUCache;
1759
667
  }));
1760
- //#endregion
1761
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/parse-options.js
668
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/parse-options.js
1762
669
  var require_parse_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1763
670
  const looseOption = Object.freeze({ loose: true });
1764
671
  const emptyOpts = Object.freeze({});
@@ -1769,8 +676,7 @@ var require_parse_options = /* @__PURE__ */ __commonJSMin(((exports, module) =>
1769
676
  };
1770
677
  module.exports = parseOptions;
1771
678
  }));
1772
- //#endregion
1773
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/constants.js
679
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/constants.js
1774
680
  var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1775
681
  module.exports = {
1776
682
  MAX_LENGTH: 256,
@@ -1792,13 +698,11 @@ var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1792
698
  FLAG_LOOSE: 2
1793
699
  };
1794
700
  }));
1795
- //#endregion
1796
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/debug.js
701
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/debug.js
1797
702
  var require_debug = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1798
703
  module.exports = typeof process === "object" && process.env && process.env.NODE_DEBUG && /\bsemver\b/i.test(process.env.NODE_DEBUG) ? (...args) => console.error("SEMVER", ...args) : () => {};
1799
704
  }));
1800
- //#endregion
1801
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/re.js
705
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/re.js
1802
706
  var require_re = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1803
707
  const { MAX_SAFE_COMPONENT_LENGTH, MAX_SAFE_BUILD_LENGTH, MAX_LENGTH } = require_constants();
1804
708
  const debug = require_debug();
@@ -1876,8 +780,7 @@ var require_re = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1876
780
  createToken("GTE0", "^\\s*>=\\s*0\\.0\\.0\\s*$");
1877
781
  createToken("GTE0PRE", "^\\s*>=\\s*0\\.0\\.0-0\\s*$");
1878
782
  }));
1879
- //#endregion
1880
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/identifiers.js
783
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/identifiers.js
1881
784
  var require_identifiers = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1882
785
  const numeric = /^[0-9]+$/;
1883
786
  const compareIdentifiers = (a, b) => {
@@ -1896,8 +799,7 @@ var require_identifiers = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1896
799
  rcompareIdentifiers
1897
800
  };
1898
801
  }));
1899
- //#endregion
1900
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/classes/semver.js
802
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/classes/semver.js
1901
803
  var require_semver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1902
804
  const debug = require_debug();
1903
805
  const { MAX_LENGTH, MAX_SAFE_INTEGER } = require_constants();
@@ -2082,57 +984,49 @@ var require_semver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2082
984
  }
2083
985
  };
2084
986
  }));
2085
- //#endregion
2086
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/compare.js
987
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/compare.js
2087
988
  var require_compare = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2088
989
  const SemVer = require_semver();
2089
990
  const compare = (a, b, loose) => new SemVer(a, loose).compare(new SemVer(b, loose));
2090
991
  module.exports = compare;
2091
992
  }));
2092
- //#endregion
2093
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/eq.js
993
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/eq.js
2094
994
  var require_eq = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2095
995
  const compare = require_compare();
2096
996
  const eq = (a, b, loose) => compare(a, b, loose) === 0;
2097
997
  module.exports = eq;
2098
998
  }));
2099
- //#endregion
2100
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/neq.js
999
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/neq.js
2101
1000
  var require_neq = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2102
1001
  const compare = require_compare();
2103
1002
  const neq = (a, b, loose) => compare(a, b, loose) !== 0;
2104
1003
  module.exports = neq;
2105
1004
  }));
2106
- //#endregion
2107
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/gt.js
1005
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/gt.js
2108
1006
  var require_gt = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2109
1007
  const compare = require_compare();
2110
1008
  const gt = (a, b, loose) => compare(a, b, loose) > 0;
2111
1009
  module.exports = gt;
2112
1010
  }));
2113
- //#endregion
2114
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/gte.js
1011
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/gte.js
2115
1012
  var require_gte = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2116
1013
  const compare = require_compare();
2117
1014
  const gte = (a, b, loose) => compare(a, b, loose) >= 0;
2118
1015
  module.exports = gte;
2119
1016
  }));
2120
- //#endregion
2121
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/lt.js
1017
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/lt.js
2122
1018
  var require_lt = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2123
1019
  const compare = require_compare();
2124
1020
  const lt = (a, b, loose) => compare(a, b, loose) < 0;
2125
1021
  module.exports = lt;
2126
1022
  }));
2127
- //#endregion
2128
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/lte.js
1023
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/lte.js
2129
1024
  var require_lte = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2130
1025
  const compare = require_compare();
2131
1026
  const lte = (a, b, loose) => compare(a, b, loose) <= 0;
2132
1027
  module.exports = lte;
2133
1028
  }));
2134
- //#endregion
2135
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/cmp.js
1029
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/cmp.js
2136
1030
  var require_cmp = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2137
1031
  const eq = require_eq();
2138
1032
  const neq = require_neq();
@@ -2163,8 +1057,7 @@ var require_cmp = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2163
1057
  };
2164
1058
  module.exports = cmp;
2165
1059
  }));
2166
- //#endregion
2167
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/classes/comparator.js
1060
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/classes/comparator.js
2168
1061
  var require_comparator = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2169
1062
  const ANY = Symbol("SemVer ANY");
2170
1063
  module.exports = class Comparator {
@@ -2235,8 +1128,7 @@ var require_comparator = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2235
1128
  const SemVer = require_semver();
2236
1129
  const Range = require_range();
2237
1130
  }));
2238
- //#endregion
2239
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/classes/range.js
1131
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/classes/range.js
2240
1132
  var require_range = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2241
1133
  const SPACE_CHARACTERS = /\s+/g;
2242
1134
  module.exports = class Range {
@@ -2518,8 +1410,7 @@ var require_range = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2518
1410
  return true;
2519
1411
  };
2520
1412
  }));
2521
- //#endregion
2522
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/satisfies.js
1413
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/satisfies.js
2523
1414
  var require_satisfies = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2524
1415
  const Range = require_range();
2525
1416
  const satisfies = (version, range, options) => {
@@ -2532,8 +1423,7 @@ var require_satisfies = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2532
1423
  };
2533
1424
  module.exports = satisfies;
2534
1425
  }));
2535
- //#endregion
2536
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/parse.js
1426
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/parse.js
2537
1427
  var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2538
1428
  const SemVer = require_semver();
2539
1429
  const parse = (version, options, throwErrors = false) => {
@@ -2547,8 +1437,7 @@ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2547
1437
  };
2548
1438
  module.exports = parse;
2549
1439
  }));
2550
- //#endregion
2551
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/valid.js
1440
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/valid.js
2552
1441
  var require_valid$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2553
1442
  const parse = require_parse();
2554
1443
  const valid = (version, options) => {
@@ -2557,8 +1446,7 @@ var require_valid$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2557
1446
  };
2558
1447
  module.exports = valid;
2559
1448
  }));
2560
- //#endregion
2561
- //#region ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/ranges/valid.js
1449
+ // ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/ranges/valid.js
2562
1450
  var require_valid = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2563
1451
  const Range = require_range();
2564
1452
  const validRange = (range, options) => {
@@ -2570,16 +1458,11 @@ var require_valid = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2570
1458
  };
2571
1459
  module.exports = validRange;
2572
1460
  }));
2573
- //#endregion
2574
- //#region src/targets.ts
1461
+ // src/targets.ts
2575
1462
  var import_satisfies = /* @__PURE__ */ __toESM(require_satisfies(), 1);
2576
1463
  var import_valid = /* @__PURE__ */ __toESM(require_valid$1(), 1);
2577
1464
  var import_valid$1 = /* @__PURE__ */ __toESM(require_valid(), 1);
2578
- const BUILD_RUNTIMES = [
2579
- "bun",
2580
- "deno",
2581
- "node"
2582
- ];
1465
+ const BUILD_RUNTIMES = ["bun", "node"];
2583
1466
  const BUN_TARGETS = {
2584
1467
  runtime: "Bun",
2585
1468
  targets: [
@@ -2647,57 +1530,6 @@ const BUN_TARGETS = {
2647
1530
  }
2648
1531
  }
2649
1532
  };
2650
- const DENO_TARGETS = {
2651
- runtime: "Deno",
2652
- targets: [
2653
- "x86_64-unknown-linux-gnu",
2654
- "aarch64-unknown-linux-gnu",
2655
- "x86_64-apple-darwin",
2656
- "aarch64-apple-darwin",
2657
- "x86_64-pc-windows-msvc",
2658
- "aarch64-pc-windows-msvc"
2659
- ],
2660
- info: {
2661
- "x86_64-unknown-linux-gnu": {
2662
- alias: "linux-x64",
2663
- platformKey: "linux-x64",
2664
- os: "linux",
2665
- cpu: "x64",
2666
- libc: "glibc"
2667
- },
2668
- "aarch64-unknown-linux-gnu": {
2669
- alias: "linux-arm64",
2670
- platformKey: "linux-arm64",
2671
- os: "linux",
2672
- cpu: "arm64",
2673
- libc: "glibc"
2674
- },
2675
- "x86_64-apple-darwin": {
2676
- alias: "darwin-x64",
2677
- platformKey: "darwin-x64",
2678
- os: "darwin",
2679
- cpu: "x64"
2680
- },
2681
- "aarch64-apple-darwin": {
2682
- alias: "darwin-arm64",
2683
- platformKey: "darwin-arm64",
2684
- os: "darwin",
2685
- cpu: "arm64"
2686
- },
2687
- "x86_64-pc-windows-msvc": {
2688
- alias: "windows-x64",
2689
- platformKey: "win32-x64",
2690
- os: "win32",
2691
- cpu: "x64"
2692
- },
2693
- "aarch64-pc-windows-msvc": {
2694
- alias: "windows-arm64",
2695
- platformKey: "win32-arm64",
2696
- os: "win32",
2697
- cpu: "arm64"
2698
- }
2699
- }
2700
- };
2701
1533
  const NODE_TARGETS = {
2702
1534
  runtime: "Node",
2703
1535
  targets: [
@@ -2786,8 +1618,7 @@ function hostTarget(table) {
2786
1618
  const platformKey = hostPlatformKey();
2787
1619
  return table.targets.find((target) => table.info[target].platformKey === platformKey) ?? null;
2788
1620
  }
2789
- //#endregion
2790
- //#region src/compilers.ts
1621
+ // src/compilers.ts
2791
1622
  /** A finished compiler process's stderr, then stdout, for error messages. */
2792
1623
  function processOutput({ stdout, stderr }) {
2793
1624
  return [stderr.trim(), stdout.trim()].filter(Boolean).join("\n");
@@ -2851,15 +1682,6 @@ function assertTargetsBuildableWithoutBun(targets, runner, host = hostTarget(BUN
2851
1682
  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.
2852
1683
  Install Bun (https://bun.sh), or ${alternative}.`);
2853
1684
  }
2854
- /** The external `deno` on PATH; Deno has no embedded fallback. */
2855
- function resolveDenoBuildRunner() {
2856
- const denoPath = which("deno");
2857
- 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.");
2858
- return {
2859
- command: denoPath,
2860
- env: { ...process.env }
2861
- };
2862
- }
2863
1685
  /** The external `node` on PATH, which runs tsdown's executable builder; there is no embedded fallback. */
2864
1686
  function resolveNodeBuildRunner() {
2865
1687
  const nodePath = which("node");
@@ -2871,14 +1693,13 @@ function resolveNodeBuildRunner() {
2871
1693
  }
2872
1694
  const RUNTIME_LABELS = {
2873
1695
  bun: "Bun",
2874
- deno: "Deno",
2875
1696
  node: "Node"
2876
1697
  };
2877
1698
  const COMPILER_VERSION_TIMEOUT_MS = 3e4;
2878
1699
  /**
2879
1700
  * Runtime version reported by the runner itself (`<command> --version`), never
2880
- * the runtime hosting this process: `bun` prints `1.4.2`, `deno` prints
2881
- * `deno 2.9.6 (…)`, `node` prints `v24.21.0`. Under the `BUN_BE_BUN` fallback
1701
+ * the runtime hosting this process: `bun` prints `1.4.2`, `node` prints
1702
+ * `v24.21.0`. Under the `BUN_BE_BUN` fallback
2882
1703
  * this is the embedded Bun, which is the compiler. `cwd` must be the directory
2883
1704
  * compilation runs in: a version-manager shim picks its runtime from it. A
2884
1705
  * probe still running after {@link COMPILER_VERSION_TIMEOUT_MS} is killed and
@@ -2892,8 +1713,7 @@ async function readCompilerVersion(runtime, runner, cwd) {
2892
1713
  timeout: COMPILER_VERSION_TIMEOUT_MS
2893
1714
  });
2894
1715
  const stdout = result.stdout.trim();
2895
- const reported = runtime === "deno" ? /^deno (\S+)/.exec(stdout)?.[1] : stdout;
2896
- const version = result.exitCode === 0 && reported !== void 0 ? (0, import_valid.default)(reported) : null;
1716
+ const version = result.exitCode === 0 ? (0, import_valid.default)(stdout) : null;
2897
1717
  if (version === null) {
2898
1718
  const output = processOutput(result);
2899
1719
  throw new Error(`Could not read the ${RUNTIME_LABELS[runtime]} version from ${runner.command} --version (exit ${result.exitCode})${output ? `:\n${output}` : "."}`);
@@ -2922,47 +1742,22 @@ function assertCompilerSatisfiesEngines(compiler, userPackageJson) {
2922
1742
  }
2923
1743
  }
2924
1744
  /**
2925
- * Select the binary compiler once for a build: the Bun runner (external bun
2926
- * first, then the embedded fallback) or the external deno. Returns its actual
2927
- * version, read in the project directory `cwd`, after validating it against
2928
- * `engines`. Pass `runner` and the same `cwd` on to the `exec*`/`buildEntrypoint`
1745
+ * Select the Bun binary compiler once for a build: external bun first, then
1746
+ * the embedded fallback. Returns its actual version, read in the project
1747
+ * directory `cwd`, after validating it against `engines`. Pass `runner` and the same `cwd` on to the `exec*`/`buildEntrypoint`
2929
1748
  * helpers so every step uses this same compiler.
2930
1749
  */
2931
- async function resolveBinaryCompiler(runtime, userPackageJson, cwd) {
2932
- const runner = runtime === "bun" ? resolveBunBuildRunner() : resolveDenoBuildRunner();
1750
+ async function resolveBinaryCompiler(userPackageJson, cwd) {
1751
+ const runner = resolveBunBuildRunner();
2933
1752
  const compiler = {
2934
- runtime,
1753
+ runtime: "bun",
2935
1754
  runner,
2936
- version: await readCompilerVersion(runtime, runner, cwd)
1755
+ version: await readCompilerVersion("bun", runner, cwd)
2937
1756
  };
2938
1757
  assertCompilerSatisfiesEngines(compiler, userPackageJson);
2939
1758
  return compiler;
2940
1759
  }
2941
- /**
2942
- * Oldest `deno` accepted to bundle a Deno runtime package. Deno 2.4 resolves a
2943
- * package.json project's npm dependencies from the registry instead of its
2944
- * installed node_modules, so it can bundle a different Crust than the project
2945
- * installed. Tested on Linux x64 with Deno 2.5.0 through 2.9.6.
2946
- */
2947
- const DENO_PACKAGE_MIN_VERSION = "2.5.0";
2948
- /**
2949
- * Select the external deno that bundles a Deno runtime package, reading its
2950
- * version in the project directory `cwd`. Call it before staging is replaced.
2951
- * A runtime package embeds no Deno, so `engines.deno` stays a consumer
2952
- * requirement and is not checked against this bundler.
2953
- */
2954
- async function resolveDenoPackageBundler(cwd, runner = resolveDenoBuildRunner()) {
2955
- const version = await readCompilerVersion("deno", runner, cwd);
2956
- 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.
2957
- crust does not install or upgrade Deno; put a newer deno first on PATH (e.g. with your version manager).`);
2958
- return {
2959
- runtime: "deno",
2960
- runner,
2961
- version
2962
- };
2963
- }
2964
- //#endregion
2965
- //#region src/bundle.ts
1760
+ // src/bundle.ts
2966
1761
  function toBunEnvFileArgs(envFiles) {
2967
1762
  return envFiles.flatMap((envFile) => ["--env-file", envFile]);
2968
1763
  }
@@ -2978,8 +1773,7 @@ function resolveBunPluginSource(specifier, cwd) {
2978
1773
  /**
2979
1774
  * Marks every Bun/Node bundle crust produces so `resolveArtifactDir` (core)
2980
1775
  * can tell a staged bundle from source at runtime. Not a `PUBLIC_*` variable:
2981
- * it is internal to crust, never user-set. Deno compile has no define, but
2982
- * standalone Deno binaries are detected directly.
1776
+ * it is internal to crust, never user-set.
2983
1777
  */
2984
1778
  const CRUST_BUILD_DEFINE = { "process.env.CRUST_INTERNAL_BUILD": "\"1\"" };
2985
1779
  const CRUST_BUILD_DEFINE_ARG = "process.env.CRUST_INTERNAL_BUILD=\"1\"";
@@ -3067,13 +1861,17 @@ async function execBuild(entryPath, outfilePath, target, settings, runner = reso
3067
1861
  target: compileTarget,
3068
1862
  outfile: outfilePath,
3069
1863
  autoloadBunfig: false
3070
- }
1864
+ },
1865
+ ...settings.bytecode ? {
1866
+ bytecode: true,
1867
+ format: "esm"
1868
+ } : {}
3071
1869
  }, outfilePath, settings, runner);
3072
1870
  return;
3073
1871
  }
3074
- await runBuildProcess(runner, createBunCompileArgs(entryPath, outfilePath, settings.minify, compileTarget, settings.envFiles), outfilePath, settings.cwd);
1872
+ await runBuildProcess(runner, createBunCompileArgs(entryPath, outfilePath, settings.minify, compileTarget, settings.envFiles, settings.bytecode), outfilePath, settings.cwd);
3075
1873
  }
3076
- function createBunCompileArgs(entryPath, outfilePath, minify, target, envFiles = []) {
1874
+ function createBunCompileArgs(entryPath, outfilePath, minify, target, envFiles = [], bytecode = false) {
3077
1875
  return [
3078
1876
  "build",
3079
1877
  "--compile",
@@ -3085,17 +1883,7 @@ function createBunCompileArgs(entryPath, outfilePath, minify, target, envFiles =
3085
1883
  "--outfile",
3086
1884
  outfilePath,
3087
1885
  ...minify ? ["--minify"] : [],
3088
- "--target",
3089
- target,
3090
- entryPath
3091
- ];
3092
- }
3093
- function createDenoCompileArgs(entryPath, outfilePath, target) {
3094
- return [
3095
- "compile",
3096
- "-A",
3097
- "--output",
3098
- outfilePath,
1886
+ ...bytecode ? ["--bytecode", "--format=esm"] : [],
3099
1887
  "--target",
3100
1888
  target,
3101
1889
  entryPath
@@ -3147,35 +1935,9 @@ async function execScriptBuild(target, entryPath, outfilePath, settings, runner
3147
1935
  await writeFile(outfilePath, shebang + output.replace(/^#![^\n]*(?:\n|$)/, ""));
3148
1936
  if (process.platform !== "win32") await chmod(outfilePath, 493);
3149
1937
  }
3150
- async function execDenoBuild(entryPath, outfilePath, target, cwd, runner = resolveDenoBuildRunner()) {
3151
- await runBuildProcess(runner, createDenoCompileArgs(entryPath, outfilePath, target), outfilePath, cwd);
3152
- }
3153
- /**
3154
- * Bundle a Deno runtime package entry with native `deno bundle`, which resolves
3155
- * dependencies through the project's own Deno setup (deno.json imports,
3156
- * package.json with its installed node_modules) and inlines them into one file.
3157
- *
3158
- * Bundle the declared command directly so Deno preserves its `import.meta.main`
3159
- * semantics without making dependencies main. Prepend the packaged-build marker
3160
- * (`PACKAGED_BUILD_KEY`) to the bundled code so it runs before the inlined
3161
- * application graph. The output has no shebang or permission flags: consumers
3162
- * choose grants with `deno run` / `deno install`.
3163
- * No env files or plugins: `deno bundle` has no PUBLIC_* filter or Bun plugins.
3164
- */
3165
- async function execDenoPackageBuild(entryPath, outfilePath, cwd, runner = resolveDenoBuildRunner()) {
3166
- await runBuildProcess(runner, [
3167
- "bundle",
3168
- "--platform=deno",
3169
- "--packages=bundle",
3170
- "--output",
3171
- outfilePath,
3172
- resolve(cwd, entryPath)
3173
- ], outfilePath, cwd);
3174
- const output = await readFile(outfilePath, "utf8");
3175
- await writeFile(outfilePath, `globalThis[Symbol.for(${JSON.stringify(PACKAGED_BUILD_KEY)})] = true;\n` + output.replace(/^#![^\n]*(?:\n|$)/, ""));
3176
- }
3177
- //#endregion
3178
- //#region src/launcher.ts
1938
+ // src/launcher.ts
1939
+ /** Node.js the launcher needs for `process.execve`: the same floor as every Crust package. */
1940
+ const LAUNCHER_NODE_RANGE = ">=24";
3179
1941
  /** Platform binary filename: `<command>-<target>`, `.exe` on Windows. */
3180
1942
  function binaryFilename(command, target) {
3181
1943
  return `${command}-${target.target}${target.os === "win32" ? ".exe" : ""}`;
@@ -3190,18 +1952,34 @@ function generateLauncher(command, targets) {
3190
1952
  const supportedPlatforms = targets.map((target) => target.targetAlias).join(", ");
3191
1953
  return `#!/usr/bin/env node
3192
1954
  // Auto-generated by crust build -- do not edit
3193
- import { spawn } from "node:child_process";
3194
- import { chmodSync, existsSync } from "node:fs";
3195
- import { dirname, resolve } from "node:path";
3196
- import process from "node:process";
3197
- import { fileURLToPath } from "node:url";
3198
-
3199
1955
  const NAME = ${JSON.stringify(command)};
1956
+ if (typeof process.getBuiltinModule !== "function" ||
1957
+ (process.platform !== "win32" && typeof process.execve !== "function")) {
1958
+ console.error("[" + NAME + "] Requires Node.js ${LAUNCHER_NODE_RANGE} (found " + process.version + ").");
1959
+ process.exit(1);
1960
+ }
1961
+
1962
+ // Builtins come from process.getBuiltinModule (cheaper than imports) and never from
1963
+ // node:process: importing it touches stdio, which leaves the shared terminal and pipes
1964
+ // non-blocking for the binary after execve.
1965
+ const { chmodSync, closeSync, existsSync, openSync, readSync } = process.getBuiltinModule("node:fs");
1966
+ const { resolve } = process.getBuiltinModule("node:path");
1967
+
3200
1968
  const PLATFORMS = ${JSON.stringify(targetMap, null, " ")};
3201
- const dir = dirname(fileURLToPath(import.meta.url));
1969
+ const dir = import.meta.dirname;
3202
1970
 
3203
- // Same check as Bun's own npm installer: glibc reports its version, musl does not.
3204
1971
  function isMusl() {
1972
+ // The first 2 KB of Node's ELF image hold its interpreter path (detect-libc reads the same
1973
+ // span), which names its libc without the milliseconds a diagnostic report costs on every start.
1974
+ try {
1975
+ const fd = openSync("/proc/self/exe", "r");
1976
+ const head = Buffer.alloc(2048);
1977
+ const interpreter = head.toString("latin1", 0, readSync(fd, head, 0, head.length, 0));
1978
+ closeSync(fd);
1979
+ if (interpreter.includes("/ld-musl-")) return true;
1980
+ if (interpreter.includes("/ld-linux-")) return false;
1981
+ } catch {}
1982
+ // Same check as Bun's own npm installer: glibc reports its version, musl does not.
3205
1983
  try {
3206
1984
  const report = process.report?.getReport();
3207
1985
  if (report?.header) return report.header.glibcVersionRuntime === undefined;
@@ -3239,39 +2017,54 @@ if (!binPath) {
3239
2017
  \tprocess.exit(1);
3240
2018
  }
3241
2019
 
3242
- if (process.platform !== "win32") {
2020
+ if (process.platform === "win32") {
2021
+ \tlaunchWindows(binPath);
2022
+ } else {
3243
2023
  \ttry {
3244
2024
  \t\tchmodSync(binPath, 0o755);
3245
2025
  \t} catch {
3246
- \t\t// Ignore permission adjustment failures and let spawn surface real errors.
2026
+ \t\t// Ignore permission adjustment failures and let execve surface real errors.
3247
2027
  \t}
2028
+ \t// Become the binary: one process, so signals and exit status reach it with no relay.
2029
+ \t// Node ignores SIGPIPE and SIGXFSZ, and ignored signals survive execve; handled ones reset.
2030
+ \tprocess.on("SIGPIPE", () => {});
2031
+ \tprocess.on("SIGXFSZ", () => {});
2032
+ \t// Early execve versions (e.g. Node 24.0.0) default to an empty environment.
2033
+ \tprocess.execve(binPath, [binPath, ...process.argv.slice(2)], process.env);
3248
2034
  }
3249
2035
 
3250
- const child = spawn(binPath, process.argv.slice(2), {
3251
- \tstdio: "inherit",
3252
- });
3253
-
3254
- child.on("error", (error) => {
3255
- \tconsole.error("[" + NAME + "] Failed to launch binary: " + error.message);
3256
- \tprocess.exit(1);
3257
- });
3258
-
3259
- child.on("exit", (code, signal) => {
3260
- \tif (signal) {
3261
- \t\ttry {
3262
- \t\t\tprocess.kill(process.pid, signal);
3263
- \t\t} catch {
3264
- \t\t\tprocess.exit(1);
2036
+ // Windows has no execve. Its console delivers Ctrl-C to the binary directly, and
2037
+ // child.kill("SIGINT") would hard-terminate it, so SIGINT is never forwarded.
2038
+ function launchWindows(binPath) {
2039
+ \tconst { spawn } = process.getBuiltinModule("node:child_process");
2040
+ \tconst child = spawn(binPath, process.argv.slice(2), { stdio: "inherit" });
2041
+ \tconst forward = (signal) => child.kill(signal);
2042
+ \tconst ignore = () => {};
2043
+ \tprocess.on("SIGINT", ignore);
2044
+ \tprocess.on("SIGTERM", forward);
2045
+ \tprocess.on("SIGHUP", forward);
2046
+ \tchild.on("error", (error) => {
2047
+ \t\tconsole.error("[" + NAME + "] Failed to launch binary: " + error.message);
2048
+ \t\tprocess.exit(1);
2049
+ \t});
2050
+ \tchild.on("exit", (code, signal) => {
2051
+ \t\tprocess.off("SIGINT", ignore);
2052
+ \t\tprocess.off("SIGTERM", forward);
2053
+ \t\tprocess.off("SIGHUP", forward);
2054
+ \t\tif (signal) {
2055
+ \t\t\ttry {
2056
+ \t\t\t\tprocess.kill(process.pid, signal);
2057
+ \t\t\t} catch {
2058
+ \t\t\t\tprocess.exit(1);
2059
+ \t\t\t}
2060
+ \t\t\treturn;
3265
2061
  \t\t}
3266
- \t\treturn;
3267
- \t}
3268
-
3269
- \tprocess.exit(code ?? 0);
3270
- });
2062
+ \t\tprocess.exit(code ?? 0);
2063
+ \t});
2064
+ }
3271
2065
  `;
3272
2066
  }
3273
- //#endregion
3274
- //#region src/distribute.ts
2067
+ // src/distribute.ts
3275
2068
  /** Project-relative directory that `crust build` owns: wiped per build, read by `crust publish`. */
3276
2069
  const CRUST_DIR = ".crust";
3277
2070
  /**
@@ -3314,8 +2107,14 @@ function platformBinMap(commands, target) {
3314
2107
  return Object.fromEntries(commands.map((command) => [command, `bin/${binaryFilename(command, target)}`]));
3315
2108
  }
3316
2109
  function buildDistributionRootPackageJson(metadata, commands, targets, { artifactDirs, manPages }) {
2110
+ const { engines } = metadata.rootPackageJson;
2111
+ const launcherEngines = targets.length > 0 && metadata.exports === void 0 ? {
2112
+ ...engines !== void 0 && isJsonObject(engines) ? engines : {},
2113
+ node: LAUNCHER_NODE_RANGE
2114
+ } : void 0;
3317
2115
  return {
3318
2116
  ...metadata.rootPackageJson,
2117
+ ...launcherEngines ? { engines: launcherEngines } : {},
3319
2118
  name: metadata.rootPackageName,
3320
2119
  version: metadata.version,
3321
2120
  type: "module",
@@ -3456,7 +2255,7 @@ function resolveDistributionMetadata(pkgJson, external) {
3456
2255
  version: pkgJson.version,
3457
2256
  rootPackageJson: pickRootMetadata(pkgJson),
3458
2257
  sourceType: pkgJson.type,
3459
- ...pkgJson.exports !== void 0 ? { exports: pkgJson.exports } : {},
2258
+ ...pkgJson.exports !== void 0 && pkgJson.exports !== null ? { exports: pkgJson.exports } : {},
3460
2259
  ...pkgJson.peerDependencies !== void 0 ? validatePeerDependencies(pkgJson.peerDependencies, pkgJson.peerDependenciesMeta) : {},
3461
2260
  ...external.length > 0 ? { dependencies: externalDependencies(pkgJson, external) } : {}
3462
2261
  };
@@ -3639,14 +2438,12 @@ async function runDistributeBuild(plan, distribution, io, build) {
3639
2438
  io.stdout(`\n${dim("Manifest:")} ${manifestPath}`);
3640
2439
  return produced;
3641
2440
  }
3642
- //#endregion
3643
- //#region package.json
2441
+ // package.json
3644
2442
  var optionalDependencies = {
3645
- "@tsdown/exe": "0.23.0",
3646
- "tsdown": "0.23.0"
2443
+ "@tsdown/exe": "0.23.4",
2444
+ "tsdown": "0.23.4"
3647
2445
  };
3648
- //#endregion
3649
- //#region src/node-exe.ts
2446
+ // src/node-exe.ts
3650
2447
  /**
3651
2448
  * Crust's own optional dependencies that build Node standalone binaries.
3652
2449
  * Crust's staged root and platform packages declare them
@@ -3802,7 +2599,7 @@ await build({
3802
2599
  // Bun leaves only Node built-ins as imports; fail on anything else, which
3803
2600
  // an executable shipping no node_modules could not resolve.
3804
2601
  deps: { alwaysBundle: () => true, onlyBundle: false, onlyImport: [] },
3805
- exe: { outDir: options.exeDir, fileName: "app", targets: [options.target] },
2602
+ exe: { outDir: options.exeDir, fileName: "app", targets: [options.target], seaConfig: { useCodeCache: options.useCodeCache } },
3806
2603
  });
3807
2604
  `;
3808
2605
  }
@@ -3824,7 +2621,7 @@ await build({
3824
2621
  * signature) after a successful build
3825
2622
  * @throws {Error} If the build fails
3826
2623
  */
3827
- async function execNodeBinaryBuild(entryPath, outfilePath, target, settings, compiler, bunRunner, onWarning = () => {}) {
2624
+ async function execNodeBinaryBuild(entryPath, outfilePath, target, settings, compiler, bunRunner, onWarning = () => {}, useCodeCache = false) {
3828
2625
  const { cwd, minify } = settings;
3829
2626
  const workDir = await mkdtemp(join(tmpdir(), "crust-node-exe-"));
3830
2627
  try {
@@ -3843,7 +2640,8 @@ async function execNodeBinaryBuild(entryPath, outfilePath, target, settings, com
3843
2640
  bundleDir: join(workDir, "bundle"),
3844
2641
  exeDir,
3845
2642
  minify,
3846
- target: nodeExeTarget(target, compiler.version)
2643
+ target: nodeExeTarget(target, compiler.version),
2644
+ useCodeCache
3847
2645
  }));
3848
2646
  const output = await runBuildProcess(compiler.runner, [scriptPath], outfilePath, cwd);
3849
2647
  const produced = await readdir(exeDir);
@@ -3861,12 +2659,13 @@ async function execNodeBinaryBuild(entryPath, outfilePath, target, settings, com
3861
2659
  }
3862
2660
  function createNodeExeProvisionScript(tsdownPackageJson, targets) {
3863
2661
  return `// Generated by crust build to provision Node binaries for targets; deleted when it exits.
3864
- import { realpathSync } from "node:fs";
2662
+ import { readFileSync, realpathSync, statSync } from "node:fs";
3865
2663
  import { createRequire } from "node:module";
3866
2664
  import { pathToFileURL } from "node:url";
3867
2665
 
3868
2666
  const exe = createRequire(realpathSync(${JSON.stringify(tsdownPackageJson)})).resolve("@tsdown/exe");
3869
- const { resolveNodeBinary } = await import(pathToFileURL(exe).href);
2667
+ const { resolveNodeBinary, getCachedBinaryPath } = await import(pathToFileURL(exe).href);
2668
+ const useCodeCache = {};
3870
2669
  for (const { name, target } of ${JSON.stringify(targets)}) {
3871
2670
  try {
3872
2671
  await resolveNodeBinary(target, {});
@@ -3874,7 +2673,23 @@ for (const { name, target } of ${JSON.stringify(targets)}) {
3874
2673
  console.error(name + ": " + error.message + (error.cause?.message ? "\\n" + error.cause.message : ""));
3875
2674
  process.exit(1);
3876
2675
  }
3877
- }
2676
+ // tsdown runs --build-sea with this Node, which writes the code cache; V8
2677
+ // rejects it (with a warning on every start) unless the embedded Node is this
2678
+ // exact binary with the same V8 flags, so another platform's or a distro build
2679
+ // of the same version, or NODE_OPTIONS such as --jitless, gets none. Decided
2680
+ // once per build: comparing reads both ~150 MB binaries.
2681
+ const embedded = getCachedBinaryPath(target);
2682
+ const host = (process.platform === "win32" ? "win" : process.platform) + "-" + process.arch;
2683
+ useCodeCache[name] = process.env.NODE_OPTIONS
2684
+ ? "NODE_OPTIONS is set"
2685
+ : target.platform + "-" + target.arch !== host
2686
+ ? "cross-built"
2687
+ : statSync(embedded, { throwIfNoEntry: false })?.size === statSync(process.execPath).size &&
2688
+ readFileSync(embedded).equals(readFileSync(process.execPath))
2689
+ ? true
2690
+ : "the build's node is not the official v" + target.nodeVersion + " binary";
2691
+ }
2692
+ console.log(JSON.stringify(useCodeCache));
3878
2693
  `;
3879
2694
  }
3880
2695
  /**
@@ -3887,6 +2702,9 @@ for (const { name, target } of ${JSON.stringify(targets)}) {
3887
2702
  * download, or a checksum mismatch therefore fails here, before `.crust/` is
3888
2703
  * replaced; cached targets need nothing.
3889
2704
  *
2705
+ * @returns Per target, `true` when {@link execNodeBinaryBuild} may embed a V8 code
2706
+ * cache (the selected Node is byte-identical to the binary the target embeds and
2707
+ * `NODE_OPTIONS` is unset), or the reason it may not
3890
2708
  * @throws {Error} If a target cannot be provisioned
3891
2709
  */
3892
2710
  async function provisionNodeExeTargets(targets, cwd, compiler) {
@@ -3899,6 +2717,16 @@ async function provisionNodeExeTargets(targets, cwd, compiler) {
3899
2717
  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.
3900
2718
  Install the missing tool, or build only targets this machine can provision (--target).`);
3901
2719
  }
2720
+ const decided = JSON.parse(provision.stdout.trim().split("\n").at(-1) || "{}");
2721
+ return isJsonObject(decided) ? decided : {};
2722
+ }
2723
+ /** The build log's `Code cache:` value: `on`, `off (reason)`, or per-target groups when they differ. */
2724
+ function formatCodeCache(decisions) {
2725
+ const groups = /* @__PURE__ */ new Map();
2726
+ for (const [target, decision] of Object.entries(decisions)) groups.set(decision, [...groups.get(decision) ?? [], target]);
2727
+ const entries = [...groups];
2728
+ if (entries.length === 1) return entries[0][0] === true ? "on" : `off (${entries[0][0]})`;
2729
+ return entries.map(([decision, targets]) => decision === true ? `on for ${targets.join(", ")}` : `off for ${targets.join(", ")} (${decision})`).join("; ");
3902
2730
  }
3903
2731
  /** Runs a generated `<name>.mjs` with `runner` from a temporary directory that is removed afterwards. */
3904
2732
  async function runGeneratedScript(runner, name, source, cwd) {
@@ -3918,9 +2746,7 @@ async function runGeneratedScript(runner, name, source, cwd) {
3918
2746
  });
3919
2747
  }
3920
2748
  }
3921
- //#endregion
3922
- //#region ../core/dist/index.js
3923
- /** Mint an Extension identity from any non-blank, trimmed string. */
2749
+ // ../core/dist/index.js
3924
2750
  function defineExtensionId(id) {
3925
2751
  if (!id || id !== id.trim()) throw new CrustError("DEFINITION", "Extension id must be a non-empty, trimmed string", {
3926
2752
  subject: "extension",
@@ -3928,15 +2754,8 @@ function defineExtensionId(id) {
3928
2754
  });
3929
2755
  return id;
3930
2756
  }
3931
- //#endregion
3932
- //#region src/snapshot.ts
2757
+ // src/snapshot.ts
3933
2758
  const SNAPSHOT_TIMEOUT_MS = 3e4;
3934
- const SNAPSHOT_FORWARDED_SIGNALS = [
3935
- "SIGINT",
3936
- "SIGTERM",
3937
- "SIGHUP",
3938
- "SIGQUIT"
3939
- ];
3940
2759
  function isBuildReport(value) {
3941
2760
  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"));
3942
2761
  }
@@ -3997,27 +2816,18 @@ async function buildEntrypoint(entryPath, outDir, envFiles, io, cwd, runner = re
3997
2816
  };
3998
2817
  });
3999
2818
  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);
4000
- const onSignal = (signal) => {
4001
- removeLifetimeListeners();
4002
- stop(`Command Snapshot preparation was interrupted by ${signal}.`);
4003
- if (process.listenerCount(signal) === 0) process.kill(process.pid, signal);
4004
- };
4005
2819
  const killRemainingGroup = () => {
4006
2820
  try {
4007
2821
  killProcessTree(proc);
4008
2822
  } catch {}
4009
2823
  };
4010
- const removeLifetimeListeners = () => {
4011
- for (const signal of SNAPSHOT_FORWARDED_SIGNALS) process.removeListener(signal, onSignal);
4012
- process.removeListener("exit", killRemainingGroup);
4013
- };
4014
- if (detached) {
4015
- for (const signal of SNAPSHOT_FORWARDED_SIGNALS) process.prependListener(signal, onSignal);
4016
- process.on("exit", killRemainingGroup);
4017
- }
2824
+ const release = detached ? bindToProcessLifetime({
2825
+ onSignal: (signal) => stop(`Command Snapshot preparation was interrupted by ${signal}.`),
2826
+ onExit: killRemainingGroup
2827
+ }) : () => {};
4018
2828
  const [rawStderr, [exitCode]] = await Promise.race([Promise.all([text(proc.stderr), once(proc, "close")]), stopped]).finally(() => {
4019
2829
  clearTimeout(timer);
4020
- removeLifetimeListeners();
2830
+ release();
4021
2831
  if (detached) killRemainingGroup();
4022
2832
  });
4023
2833
  const stderr = rawStderr.trim();
@@ -4075,8 +2885,7 @@ async function buildEntrypoint(entryPath, outDir, envFiles, io, cwd, runner = re
4075
2885
  });
4076
2886
  }
4077
2887
  }
4078
- //#endregion
4079
- //#region src/build.ts
2888
+ // src/build.ts
4080
2889
  function readUserPackageJson(cwd) {
4081
2890
  const packageJsonPath = join(cwd, "package.json");
4082
2891
  if (!existsSync(packageJsonPath)) throw new Error(`package.json not found in ${cwd}\n crust build requires a package.json with name and version fields.`);
@@ -4165,7 +2974,6 @@ const NON_DEPENDENCY_SECTIONS = [
4165
2974
  function validateExternal(external, pkg, runtime, artifact) {
4166
2975
  if (external.length === 0) return;
4167
2976
  if (artifact === "binary") throw new Error("package.json crust.external is not supported for standalone binaries (artifact binary).\n A binary bundles every dependency; remove crust.external or use artifact package.");
4168
- if (runtime === "deno") throw new Error("package.json crust.external is not supported with the deno runtime.\n crust.external applies to Node and Bun runtime packages; remove crust.external or set crust.runtime to node or bun.");
4169
2977
  const dependencies = pkg.dependencies !== void 0 && isJsonObject(pkg.dependencies) ? pkg.dependencies : {};
4170
2978
  const rejectCrustPackage = (quoted, detail) => {
4171
2979
  throw new Error(`package.json crust.external cannot name ${quoted}${detail}.\n Crust packages must stay bundled: core detects a packaged build through a define the bundler injects, and an external Crust package would load a second copy of core.`);
@@ -4186,23 +2994,17 @@ function validateExternal(external, pkg, runtime, artifact) {
4186
2994
  if (aliased !== void 0) rejectCrustPackage(quoted, ` (an alias of ${aliased})`);
4187
2995
  }
4188
2996
  }
4189
- const DENO_CONFIG_FILES = ["deno.json", "deno.jsonc"];
4190
2997
  /**
4191
2998
  * package.json `crust.runtime` > inference > Bun. Inference uses only
4192
- * unambiguous signals: a Deno config file, or `@types/node` without
4193
- * `@types/bun`. Lockfiles say which package manager installed dependencies,
4194
- * not which runtime runs the CLI, so they are not consulted.
2999
+ * unambiguous signals: `@types/node` without `@types/bun`. Lockfiles say which
3000
+ * package manager installed dependencies, not which runtime runs the CLI, so
3001
+ * they are not consulted.
4195
3002
  */
4196
- function resolveBuildRuntime(pkg, config, cwd) {
3003
+ function resolveBuildRuntime(pkg, config) {
4197
3004
  if (config.runtime !== void 0) return {
4198
3005
  runtime: config.runtime,
4199
3006
  source: "from package.json"
4200
3007
  };
4201
- const denoConfig = DENO_CONFIG_FILES.find((file) => existsSync(join(cwd, file)));
4202
- if (denoConfig) return {
4203
- runtime: "deno",
4204
- source: `inferred from ${denoConfig}`
4205
- };
4206
3008
  if (hasDependency(pkg, "@types/node") && !hasDependency(pkg, "@types/bun")) return {
4207
3009
  runtime: "node",
4208
3010
  source: "inferred from @types/node"
@@ -4285,30 +3087,18 @@ function resolveBinEntries(cwd, pkg) {
4285
3087
  };
4286
3088
  });
4287
3089
  }
4288
- /** What earlier releases built for each runtime without being asked; quoted in the migration error. */
4289
- const IMPLICIT_ARTIFACTS = {
4290
- bun: "binary",
4291
- deno: "binary",
4292
- node: "package"
4293
- };
4294
3090
  /** `option` (the `artifact` build option or `--artifact`) > package.json `crust.artifact`; no default. */
4295
- function resolveArtifact(option, config, runtime) {
3091
+ function resolveArtifact(option, config) {
4296
3092
  if (option !== void 0 && !isArtifactKind(option)) throw new Error(`Invalid artifact ${JSON.stringify(option)}. Valid artifacts: ${ARTIFACT_KINDS.join(", ")}`);
4297
3093
  const artifact = option ?? config.artifact;
4298
- if (artifact === void 0) {
4299
- const previous = IMPLICIT_ARTIFACTS[runtime];
4300
- throw new Error(`crust build needs an artifact kind: pass --artifact package|binary or set package.json "crust": { "artifact": "package" | "binary" }.
4301
- package: one JavaScript bundle per command that runs on the consumer's installed runtime.
4302
- binary: standalone executables in per-platform npm packages behind a Node launcher.
4303
- Earlier versions built a ${previous} for the ${runtime} runtime implicitly; set "artifact": "${previous}" to keep that output.`);
4304
- }
3094
+ if (artifact === void 0) throw new Error("crust build needs an artifact kind: pass --artifact package|binary or set package.json \"crust\": { \"artifact\": \"package\" | \"binary\" }.\n package: one JavaScript bundle per command that runs on the consumer's installed runtime.\n binary: standalone executables in per-platform npm packages behind a Node launcher.");
4305
3095
  return artifact;
4306
3096
  }
4307
3097
  function planBuild(options, cwd) {
4308
3098
  const userPackageJson = readUserPackageJson(cwd);
4309
3099
  const config = readCrustConfig(userPackageJson);
4310
- const { runtime, source: runtimeSource } = resolveBuildRuntime(userPackageJson, config, cwd);
4311
- const artifact = resolveArtifact(options.artifact, config, runtime);
3100
+ const { runtime, source: runtimeSource } = resolveBuildRuntime(userPackageJson, config);
3101
+ const artifact = resolveArtifact(options.artifact, config);
4312
3102
  const entries = resolveBinEntries(cwd, userPackageJson);
4313
3103
  validatePackageIdentity(userPackageJson, "package.json");
4314
3104
  const envFiles = resolveEnvFilePaths(cwd, options.envFiles);
@@ -4316,10 +3106,8 @@ function planBuild(options, cwd) {
4316
3106
  const external = config.external ?? [];
4317
3107
  if (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.");
4318
3108
  if (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\".");
4319
- const denoTool = artifact === "binary" ? "deno compile" : "deno bundle";
4320
- if (runtime === "deno" && options.minify) throw new Error(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.");
4321
- if (runtime === "deno" && envFiles.length > 0) throw new Error(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).");
4322
- 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.`);
3109
+ const bunBinary = runtime === "bun" && artifact === "binary";
3110
+ if (options.bytecode && !bunBinary) throw new Error(artifact === "package" ? "--bytecode is not supported for runtime packages (artifact package).\n Bun bytecode only runs on the exact Bun version that built it, so it is embedded in bun binaries only; drop the flag." : `--bytecode is not supported with the ${runtime} runtime.\n Bytecode is Bun's JavaScriptCore format; Node binaries cannot use it. Drop the flag.`);
4323
3111
  if (runtime === "node" && 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.");
4324
3112
  validateExternal(external, userPackageJson, runtime, artifact);
4325
3113
  const stageDir = resolve(cwd, CRUST_DIR);
@@ -4335,7 +3123,8 @@ function planBuild(options, cwd) {
4335
3123
  outDir: join(stageDir, "artifacts"),
4336
3124
  stageDir,
4337
3125
  validate: options.validate ?? true,
4338
- minify: runtime === "deno" ? false : options.minify ?? true
3126
+ minify: options.minify ?? true,
3127
+ bytecode: bunBinary && (options.bytecode ?? true)
4339
3128
  };
4340
3129
  if (artifact === "package") return {
4341
3130
  ...common,
@@ -4349,17 +3138,11 @@ function planBuild(options, cwd) {
4349
3138
  artifact,
4350
3139
  targets: resolveTargets(BUN_TARGETS, targetInputs)
4351
3140
  };
4352
- if (runtime === "node") return {
4353
- ...common,
4354
- runtime,
4355
- artifact,
4356
- targets: resolveTargets(NODE_TARGETS, targetInputs)
4357
- };
4358
3141
  return {
4359
3142
  ...common,
4360
3143
  runtime,
4361
3144
  artifact,
4362
- targets: resolveTargets(DENO_TARGETS, targetInputs)
3145
+ targets: resolveTargets(NODE_TARGETS, targetInputs)
4363
3146
  };
4364
3147
  }
4365
3148
  /**
@@ -4368,20 +3151,10 @@ function planBuild(options, cwd) {
4368
3151
  * the selected compiler's own version (`resolveBinaryCompiler`, or
4369
3152
  * `resolveNodeBinaryCompiler` with tsdown's requirements for node, whose
4370
3153
  * targets' embedded Node binaries are also provisioned here); runtime
4371
- * packages are bundled by Bun (or, for Deno, by `deno bundle`) and embed no
4372
- * runtime, so engines stay a consumer requirement there and are not checked
4373
- * against the bundler.
3154
+ * packages are bundled by Bun and embed no runtime, so engines stay a consumer
3155
+ * requirement there and are not checked against the bundler.
4374
3156
  */
4375
3157
  async function selectCompilers(plan, io) {
4376
- if (plan.artifact === "package" && plan.runtime === "deno") {
4377
- const bundler = await resolveDenoPackageBundler(plan.cwd);
4378
- io.stdout(`${dim("Compiler:")} deno ${bundler.version} ${dim(`(${bundler.runner.command}, deno bundle)`)}`);
4379
- io.stderr("Deno runtime packages are experimental: they are bundled with deno bundle, which Deno marks experimental.");
4380
- return {
4381
- snapshotRunner: plan.validate ? resolveBunBuildRunner() : void 0,
4382
- stage: (reports) => runDistributeBuild(plan, { execute: (entry, outfile) => execDenoPackageBuild(entry, outfile, plan.cwd, bundler.runner) }, io, reports)
4383
- };
4384
- }
4385
3158
  if (plan.artifact === "package") {
4386
3159
  const runner = resolveBunBuildRunner();
4387
3160
  const target = plan.runtime === "bun" ? "bun" : "node";
@@ -4394,42 +3167,31 @@ async function selectCompilers(plan, io) {
4394
3167
  if (plan.runtime === "node") {
4395
3168
  const compiler = await resolveNodeBinaryCompiler(plan.userPackageJson, plan.cwd);
4396
3169
  printCompiler(compiler);
4397
- await provisionNodeExeTargets(plan.targets, plan.cwd, compiler);
3170
+ const useCodeCache = await provisionNodeExeTargets(plan.targets, plan.cwd, compiler);
3171
+ io.stdout(`${dim("Code cache:")} ${formatCodeCache(useCodeCache)}`);
4398
3172
  const bunRunner = resolveBunBuildRunner();
4399
3173
  const distribution = {
4400
3174
  table: NODE_TARGETS,
4401
3175
  targets: plan.targets,
4402
3176
  embeddedRuntimeVersion: compiler.version,
4403
- execute: (entry, outfile, target) => execNodeBinaryBuild(entry, outfile, target, plan, compiler, bunRunner, io.stderr)
3177
+ execute: (entry, outfile, target) => execNodeBinaryBuild(entry, outfile, target, plan, compiler, bunRunner, io.stderr, useCodeCache[target] === true)
4404
3178
  };
4405
3179
  return {
4406
3180
  snapshotRunner: bunRunner,
4407
3181
  stage: (reports) => runDistributeBuild(plan, distribution, io, reports)
4408
3182
  };
4409
3183
  }
4410
- const compiler = await resolveBinaryCompiler(plan.runtime, plan.userPackageJson, plan.cwd);
3184
+ const compiler = await resolveBinaryCompiler(plan.userPackageJson, plan.cwd);
4411
3185
  printCompiler(compiler);
4412
- if (plan.runtime === "bun") {
4413
- assertTargetsBuildableWithoutBun(plan.targets, compiler.runner);
4414
- const distribution = {
4415
- table: BUN_TARGETS,
4416
- targets: plan.targets,
4417
- embeddedRuntimeVersion: compiler.version,
4418
- execute: (entry, outfile, target) => execBuild(entry, outfile, target, plan, compiler.runner)
4419
- };
4420
- return {
4421
- snapshotRunner: compiler.runner,
4422
- stage: (reports) => runDistributeBuild(plan, distribution, io, reports)
4423
- };
4424
- }
3186
+ assertTargetsBuildableWithoutBun(plan.targets, compiler.runner);
4425
3187
  const distribution = {
4426
- table: DENO_TARGETS,
3188
+ table: BUN_TARGETS,
4427
3189
  targets: plan.targets,
4428
3190
  embeddedRuntimeVersion: compiler.version,
4429
- execute: (entry, outfile, target) => execDenoBuild(entry, outfile, target, plan.cwd, compiler.runner)
3191
+ execute: (entry, outfile, target) => execBuild(entry, outfile, target, plan, compiler.runner)
4430
3192
  };
4431
3193
  return {
4432
- snapshotRunner: plan.validate ? resolveBunBuildRunner() : void 0,
3194
+ snapshotRunner: compiler.runner,
4433
3195
  stage: (reports) => runDistributeBuild(plan, distribution, io, reports)
4434
3196
  };
4435
3197
  }
@@ -4469,10 +3231,8 @@ async function prepareEntries(plan, io, runner) {
4469
3231
  * come from `bin`; the runtime, artifact, Bun plugins, extra directories, and
4470
3232
  * external dependencies from package.json `crust`. Bundling and Command
4471
3233
  * Snapshots run in bun subprocesses (bun on PATH, or the running Bun
4472
- * executable), so this works under Node as well when Bun is installed; Deno
4473
- * binaries and Deno runtime packages (experimental, deno 2.5.0 or newer) need
4474
- * deno on PATH, and Node binaries a node on PATH that Crust's tsdown can build
4475
- * executables with.
3234
+ * executable), so this works under Node as well when Bun is installed; Node
3235
+ * binaries need a node on PATH that Crust's tsdown can build executables with.
4476
3236
  *
4477
3237
  * Throws on any failure. Planning and compiler-selection failures (bad
4478
3238
  * options or package.json, a missing or hung compiler, an engines mismatch) leave the
@@ -4516,5 +3276,4 @@ async function build(options = {}) {
4516
3276
  rmdirSync(lockDir);
4517
3277
  }
4518
3278
  }
4519
- //#endregion
4520
3279
  export { build };