@gjsify/cli 0.36.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/dist/cli.gjs.mjs +3 -3
  2. package/lib/bundler-pick.js +61 -87
  3. package/lib/cli-fail.d.ts +4 -7
  4. package/lib/cli-fail.js +29 -55
  5. package/lib/commands/affected.js +161 -286
  6. package/lib/commands/install.d.ts +63 -66
  7. package/lib/commands/install.js +372 -536
  8. package/lib/templates/install.mjs.tmpl +38 -68
  9. package/lib/types/cli-build-options.d.ts +33 -67
  10. package/lib/types/command.d.ts +9 -12
  11. package/lib/types/config-data.d.ts +125 -221
  12. package/lib/utils/auth-npmrc.d.ts +5 -10
  13. package/lib/utils/auth-npmrc.js +11 -21
  14. package/lib/utils/clear-targets.d.ts +8 -11
  15. package/lib/utils/clear-targets.js +22 -29
  16. package/lib/utils/copy-targets.d.ts +13 -19
  17. package/lib/utils/copy-targets.js +31 -44
  18. package/lib/utils/dep-aggregation.d.ts +7 -15
  19. package/lib/utils/dep-aggregation.js +9 -22
  20. package/lib/utils/detect-native-packages.d.ts +83 -116
  21. package/lib/utils/detect-native-packages.js +111 -149
  22. package/lib/utils/dir-link.d.ts +29 -48
  23. package/lib/utils/dir-link.js +33 -56
  24. package/lib/utils/discover-showcases.d.ts +6 -7
  25. package/lib/utils/discover-showcases.js +11 -15
  26. package/lib/utils/dlx-cache.d.ts +29 -38
  27. package/lib/utils/dlx-cache.js +53 -63
  28. package/lib/utils/force-exit.d.ts +0 -3
  29. package/lib/utils/force-exit.js +13 -24
  30. package/lib/utils/gi-search-path.d.ts +16 -24
  31. package/lib/utils/gi-search-path.js +54 -82
  32. package/lib/utils/gjs-bundle-guard.d.ts +5 -13
  33. package/lib/utils/gjs-bundle-guard.js +35 -57
  34. package/lib/utils/gjs-source-escape.d.ts +2 -2
  35. package/lib/utils/gjs-source-escape.js +21 -39
  36. package/lib/utils/gjsify-shim.d.ts +17 -20
  37. package/lib/utils/gjsify-shim.js +80 -120
  38. package/lib/utils/html-entry.d.ts +8 -12
  39. package/lib/utils/html-entry.js +19 -28
  40. package/lib/utils/install-backend-native.d.ts +123 -161
  41. package/lib/utils/install-backend-native.js +536 -737
  42. package/lib/utils/install-backend.d.ts +42 -57
  43. package/lib/utils/install-backend.js +16 -21
  44. package/lib/utils/install-cache-fs.d.ts +10 -15
  45. package/lib/utils/install-cache-fs.js +18 -26
  46. package/lib/utils/install-lock.d.ts +5 -6
  47. package/lib/utils/install-lock.js +46 -65
  48. package/lib/utils/install-packument-cache.d.ts +27 -33
  49. package/lib/utils/install-packument-cache.js +44 -57
  50. package/lib/utils/install-progress.d.ts +2 -9
  51. package/lib/utils/install-progress.js +14 -39
  52. package/lib/utils/install-tarball-cache.d.ts +14 -20
  53. package/lib/utils/install-tarball-cache.js +41 -59
  54. package/lib/utils/library-output.d.ts +9 -22
  55. package/lib/utils/library-output.js +19 -37
  56. package/lib/utils/load-npmrc.d.ts +5 -6
  57. package/lib/utils/load-npmrc.js +18 -27
  58. package/lib/utils/node-bundle-guard.d.ts +4 -8
  59. package/lib/utils/node-bundle-guard.js +21 -35
  60. package/lib/utils/node-script.js +33 -54
  61. package/lib/utils/node-version.d.ts +5 -14
  62. package/lib/utils/node-version.js +15 -24
  63. package/lib/utils/normalize-bundler-options.d.ts +7 -11
  64. package/lib/utils/normalize-bundler-options.js +24 -42
  65. package/lib/utils/npm-oidc.d.ts +23 -37
  66. package/lib/utils/npm-oidc.js +28 -53
  67. package/lib/utils/npm-otp-cache.d.ts +4 -4
  68. package/lib/utils/npm-otp-cache.js +20 -25
  69. package/lib/utils/npm-otp.d.ts +20 -30
  70. package/lib/utils/npm-otp.js +32 -51
  71. package/lib/utils/onboard-probe.d.ts +7 -13
  72. package/lib/utils/onboard-probe.js +16 -27
  73. package/lib/utils/parse-spec.d.ts +3 -3
  74. package/lib/utils/parse-spec.js +4 -5
  75. package/lib/utils/pkg-json-edit.d.ts +7 -12
  76. package/lib/utils/pkg-json-edit.js +13 -26
  77. package/lib/utils/pkg-json.d.ts +11 -18
  78. package/lib/utils/pkg-json.js +19 -28
  79. package/lib/utils/prefixed-output.d.ts +3 -4
  80. package/lib/utils/prefixed-output.js +13 -19
  81. package/lib/utils/prompt.d.ts +3 -4
  82. package/lib/utils/prompt.js +14 -18
  83. package/lib/utils/publish-diagnose.d.ts +11 -16
  84. package/lib/utils/publish-diagnose.js +15 -28
  85. package/lib/utils/publish-headers.d.ts +7 -11
  86. package/lib/utils/publish-headers.js +36 -65
  87. package/lib/utils/publishable-packages.d.ts +4 -6
  88. package/lib/utils/publishable-packages.js +14 -18
  89. package/lib/utils/resolve-gjs-entry.d.ts +5 -8
  90. package/lib/utils/resolve-gjs-entry.js +10 -27
  91. package/lib/utils/resolve-npm-package.d.ts +6 -11
  92. package/lib/utils/resolve-npm-package.js +29 -63
  93. package/lib/utils/resolve-plugin-by-name.d.ts +11 -19
  94. package/lib/utils/resolve-plugin-by-name.js +9 -14
  95. package/lib/utils/run-gjs.d.ts +16 -21
  96. package/lib/utils/run-gjs.js +31 -56
  97. package/lib/utils/run-lifecycle-script.d.ts +5 -9
  98. package/lib/utils/run-lifecycle-script.js +23 -42
  99. package/lib/utils/run-node.d.ts +21 -33
  100. package/lib/utils/run-node.js +43 -64
  101. package/lib/utils/runtimes.d.ts +25 -35
  102. package/lib/utils/runtimes.js +34 -46
  103. package/lib/utils/spawn.d.ts +32 -48
  104. package/lib/utils/spawn.js +51 -84
  105. package/lib/utils/stdin.d.ts +2 -2
  106. package/lib/utils/stdin.js +10 -25
  107. package/lib/utils/suggest.d.ts +6 -7
  108. package/lib/utils/suggest.js +8 -10
  109. package/lib/utils/system-gi.d.ts +18 -27
  110. package/lib/utils/system-gi.js +60 -114
  111. package/lib/utils/trust-registry.d.ts +4 -8
  112. package/lib/utils/trust-registry.js +7 -15
  113. package/lib/utils/win32-command.d.ts +8 -14
  114. package/lib/utils/win32-command.js +29 -43
  115. package/lib/utils/workspace-root.js +7 -12
  116. package/package.json +18 -18
@@ -1,40 +1,21 @@
1
1
  #!/usr/bin/env -S gjs -m
2
2
  /**
3
- * gjsify universal installer — bootstraps `@gjsify/cli` (or any GJS app
4
- * published to npm) on a system that has only `gjs` (and `curl`/`wget`)
5
- * available, without requiring Node.js or `npm`.
3
+ * gjsify universal installer — bootstraps `@gjsify/cli` (or any GJS app on npm)
4
+ * on a system that has only `gjs` and libsoup: no Node, no npm. Decision and
5
+ * history: docs/adr/0002-bootstrap-bundle-minimization.md.
6
6
  *
7
- * Usage:
8
- * gjs -m install.mjs # install / update @gjsify/cli
9
- * gjs -m install.mjs --target @scope/x # install any other GJS-runnable npm package
10
- * gjs -m install.mjs --tag next # pick an npm dist-tag or pinned version
11
- * gjs -m install.mjs --force # reinstall even if already present
12
- * gjs -m install.mjs --help
7
+ * Two stages: download a self-contained GJS bundle of `@gjsify/cli`
8
+ * (`cli.gjs.mjs`) from this repo's GitHub releases and verify its SHA-256, then
9
+ * spawn it as `gjs -m <bundle> install -g <target>@<tag>`. Dependency
10
+ * resolution, native prebuilds, lockfiles and the `~/.local/bin` launchers all
11
+ * live in that bundle — this bootstrapper re-implements none of it on purpose.
13
12
  *
14
- * How it works (two-stage bootstrap):
15
- * 1. Download a small self-contained GJS bundle of the @gjsify/cli
16
- * (`cli.gjs.mjs`) from this repo's GitHub releases. Verify SHA-256.
17
- * 2. Spawn that bundle: `gjs -m <bundle> install -g <target>@<tag>`. The
18
- * bundle handles transitive dependency resolution, native prebuilds,
19
- * lockfiles, and the `~/.local/bin` launchers — all the things this
20
- * thin bootstrapper deliberately does NOT re-implement.
13
+ * `gjsify generate-installer` re-emits this file for end-user GJS apps with the
14
+ * marked constants substituted.
21
15
  *
22
- * Generated by `gjsify generate-installer` for end-user GJS apps: in that
23
- * mode the constants below (BOOTSTRAP_URL, DEFAULT_TARGET, DEFAULT_BIN_NAME)
24
- * are pre-substituted to the consumer's package + custom bootstrap URL.
25
- *
26
- * Test hooks (set by tests/e2e/install-script/run.mjs):
27
- * GJSIFY_INSTALL_BOOTSTRAP_URL override the cli.gjs.mjs download origin
28
- * (accepts file:// for offline tests)
29
- * GJSIFY_INSTALL_BOOTSTRAP_SHA256_URL override the .sha256 companion URL.
30
- * An EMPTY string is the only way to install
31
- * without verification, and it warns loudly; a
32
- * digest URL that is set but unreachable is a
33
- * hard failure, never a silent downgrade.
34
- * GJSIFY_GLOBAL_PREFIX override install prefix (forwarded to cli)
35
- * GJSIFY_GLOBAL_BIN_DIR override bin dir (forwarded to cli)
36
- * GJSIFY_INSTALL_REGISTRY override npm registry (forwarded as npm_config_registry)
37
- * GJSIFY_INSTALL_BOOTSTRAP_CACHE override the bootstrap cache dir
16
+ * `--help` documents the user-facing flags and env vars. Two more exist only for
17
+ * tests/e2e/install-script/run.mjs: GJSIFY_INSTALL_BOOTSTRAP_SHA256_URL and
18
+ * GJSIFY_INSTALL_BOOTSTRAP_CACHE.
38
19
  */
39
20
 
40
21
  import GLib from 'gi://GLib';
@@ -53,17 +34,16 @@ const DEFAULT_BOOTSTRAP_SHA256_URL = `${DEFAULT_BOOTSTRAP_URL}.sha256`;
53
34
 
54
35
  const USER_AGENT = 'gjsify-installer/1.0';
55
36
 
56
- // The bootstrap cache is CONTENT-ADDRESSED: `cli-<sha256>.gjs.mjs`. See
57
- // `resolveBootstrap()` for why the digest, not a fixed name, keys it.
37
+ // Content-addressed cache: `cli-<sha256>.gjs.mjs`. Why the digest and not a
38
+ // fixed name keys it: `resolveBootstrap()`.
58
39
  const CACHE_PREFIX = 'cli-';
59
40
  const CACHE_SUFFIX = '.gjs.mjs';
60
41
 
61
42
  /**
62
- * Under `--fetch-only` the ONLY thing on stdout must be the bundle path, so
63
- * progress goes to stderr instead. A caller does
64
- * `BOOTSTRAP=$(gjs -m install.mjs --fetch-only)`, and one stray `info()` line
65
- * would make that a path with a log message glued to the front — a failure that
66
- * surfaces as a confusing "file not found" rather than as bad output.
43
+ * Under `--fetch-only` stdout carries nothing but the bundle path, so progress
44
+ * goes to stderr. A caller does `BOOTSTRAP=$(gjs -m install.mjs --fetch-only)`,
45
+ * where one stray `info()` line glues a log message to the front of the path and
46
+ * surfaces later as a confusing "file not found".
67
47
  */
68
48
  let logToStderr = false;
69
49
 
@@ -139,7 +119,7 @@ Examples:
139
119
  }
140
120
 
141
121
  function checkGjsVersion() {
142
- // imports.system.version is packed: major*10000 + minor*100 + micro
122
+ // `system.version` is packed: major*10000 + minor*100 + micro
143
123
  const v = system?.version;
144
124
  if (typeof v !== 'number') return;
145
125
  const major = Math.floor(v / 10000);
@@ -197,18 +177,17 @@ function readBytesOrNull(path) {
197
177
  const [, bytes] = Gio.File.new_for_path(path).load_contents(null);
198
178
  return bytes;
199
179
  } catch {
200
- // An absent — or unreadable, e.g. truncated by a killed earlier run —
201
- // cache entry is a MISS, not an error. `load_contents` is a real
202
- // `throws="1"` GIR call, so this catch has a genuine throw path.
180
+ // An absent or unreadable entry (truncated by a killed earlier run) is a
181
+ // MISS, not an error. `load_contents` is `throws="1"`, so this catch has
182
+ // a genuine throw path.
203
183
  return null;
204
184
  }
205
185
  }
206
186
 
207
187
  /**
208
- * Drop every other cached bootstrap so the content-addressed cache stays
209
- * bounded. Each entry is a ~6.6 MB bundle and the digest changes with every
210
- * release — without this, keying the cache by content would trade "the cache
211
- * can never be warm" for "the cache grows forever".
188
+ * Drop every other cached bootstrap. Each entry is a ~6.6 MB bundle and the
189
+ * digest changes every release, so without pruning, content-addressing would
190
+ * trade "the cache can never be warm" for "the cache grows forever".
212
191
  */
213
192
  function pruneCache(dir, keepBasename) {
214
193
  const children = Gio.File.new_for_path(dir).enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
@@ -224,21 +203,15 @@ function pruneCache(dir, keepBasename) {
224
203
  /**
225
204
  * Resolve a VERIFIED bootstrap bundle and return its path.
226
205
  *
227
- * The digest is fetched BEFORE the bundle, and the cache is keyed by it. That
228
- * ordering is the mechanism, not an optimisation — it fixes two independent
229
- * defects that both came from doing it the other way round:
230
- *
231
- * 1. Verification was skippable by anyone who could break ONE request. The
232
- * bundle was downloaded first and verified after, and a failed `.sha256`
233
- * fetch merely logged "skipping verification" and carried on — so a proxy,
234
- * a 404, or a captive portal silently downgraded the install to no
235
- * verification at all. Fetching the digest first makes that impossible:
236
- * no digest, no install.
237
- * 2. The cache could never be warm. It wrote a fixed `cli.gjs.mjs` and never
238
- * read it back, so every run re-downloaded ~6.6 MB. A content-addressed
239
- * name lets a matching entry short-circuit the download entirely.
206
+ * Fetching the digest BEFORE the bundle, and keying the cache by it, is the
207
+ * mechanism rather than an optimisation; the reverse order cost two defects.
208
+ * Verifying after downloading made verification skippable by anyone who could
209
+ * break ONE request — a failed `.sha256` fetch logged "skipping verification"
210
+ * and carried on, so a proxy, a 404 or a captive portal silently downgraded the
211
+ * install. And a fixed `cli.gjs.mjs` name that was never read back meant every
212
+ * run re-downloaded ~6.6 MB; a content-addressed name short-circuits that.
240
213
  *
241
- * Skipping verification is still possible, but only as an EXPLICIT act
214
+ * Skipping verification remains possible only as an EXPLICIT act
242
215
  * (`GJSIFY_INSTALL_BOOTSTRAP_SHA256_URL=''`), and it says so on stderr.
243
216
  */
244
217
  async function resolveBootstrap(session, bootstrapUrl, sha256Url) {
@@ -313,14 +286,12 @@ function buildSpec(target, tag) {
313
286
  }
314
287
 
315
288
  async function runInstall(bundlePath, spec) {
316
- // `gjsify install -g <spec>` is always idempotent — it rewrites the tree
317
- // unconditionally. There is no separate "force" flag to forward; the
318
- // installer's own `--force` is satisfied by the fact that we always
319
- // re-download the bootstrap and re-invoke the CLI.
289
+ // `gjsify install -g <spec>` rewrites the tree unconditionally, so there is
290
+ // no force flag to forward and `--force` needs nothing here.
320
291
  info(`Running: gjs -m <bootstrap> install -g ${spec}`);
321
292
  const argv = ['gjs', '-m', bundlePath, 'install', '-g', spec];
322
- // Forward env vars verbatim so override paths set by tests / power-users
323
- // reach the spawned CLI.
293
+ // No `envp`: the launcher inherits ours, so override env vars set by tests
294
+ // and power-users reach the spawned CLI.
324
295
  const launcher = new Gio.SubprocessLauncher({
325
296
  flags: Gio.SubprocessFlags.NONE,
326
297
  });
@@ -349,7 +320,6 @@ async function main() {
349
320
  }
350
321
 
351
322
  if (opts.fetchOnly) {
352
- // The one thing on stdout. Everything above went to stderr.
353
323
  print(bundlePath);
354
324
  exit(0);
355
325
  }
@@ -1,106 +1,72 @@
1
1
  import type { App } from '@gjsify/rolldown-plugin-gjsify';
2
2
  export interface CliBuildOptions {
3
- /**
4
- * This is an array of files that each serve as an input to the bundling algorithm.
5
- * @see https://esbuild.github.io/api/#entry-points
6
- */
3
+ /** Input files to the bundling algorithm. */
7
4
  entryPoints?: string[];
8
- /** Switch on the verbose mode */
9
5
  verbose?: boolean;
10
- /**
11
- * When enabled, the generated code will be minified instead of pretty-printed.
12
- * @see https://esbuild.github.io/api/#minify
13
- */
6
+ /** Minify the generated code instead of pretty-printing it. */
14
7
  minify?: boolean;
15
- /**
16
- * Override the default output format.
17
- * @see https://esbuild.github.io/api/#format
18
- */
8
+ /** Override the default output format. */
19
9
  format?: 'iife' | 'esm' | 'cjs';
20
- /** Use this if you want to build an application or test, the platform node is usually only used internally to build the tests for Gjsify */
10
+ /** Build an application or test. `node` is mostly internal, for gjsify's tests. */
21
11
  app?: App;
22
- /** Use this if you want to build a library for Gjsify */
12
+ /** Build a library rather than an application. */
23
13
  library?: boolean;
24
- /**
25
- * This option sets the output file name for the build operation.
26
- * This is only applicable if there is a single entry point.
27
- * If there are multiple entry points, you must use the outdir option instead to specify an output directory.
28
- * @see https://esbuild.github.io/api/#outfile
29
- */
14
+ /** Output file name. Single entry point only — otherwise use `outdir`. */
30
15
  outfile?: string;
31
- /**
32
- * This option sets the output directory for the build operation.
33
- * @see https://esbuild.github.io/api/#outdir
34
- */
35
16
  outdir?: string;
36
- /** Enables TypeScript types on runtime using Deepkit's type compiler */
17
+ /** Enables TypeScript types at runtime using Deepkit's type compiler. */
37
18
  reflection?: boolean;
38
- /**
39
- * The log level can be changed to prevent esbuild from printing warning and/or error messages to the terminal
40
- * @see https://esbuild.github.io/api/#log-level
41
- */
19
+ /** How much the bundler prints to the terminal. */
42
20
  logLevel: 'silent' | 'error' | 'warning' | 'info' | 'debug' | 'verbose';
43
- /** An array of glob patterns to exclude matches and aliases */
21
+ /** Glob patterns excluding matches and aliases. */
44
22
  exclude?: string[];
45
23
  /**
46
- * Inject a console shim into GJS builds for clean output (no GLib prefix, ANSI colors work).
47
- * Use --no-console-shim to disable. Only applies to GJS app builds. Default: true.
24
+ * Inject a console shim into GJS app builds for clean output (no GLib prefix,
25
+ * working ANSI colors). Disable with `--no-console-shim`. Default: true.
48
26
  */
49
27
  consoleShim?: boolean;
50
28
  /**
51
- * Comma-separated list of global identifiers your code needs (e.g.
52
- * `"fetch,Buffer,process,URL,crypto"`). Each identifier is mapped to the
53
- * corresponding `@gjsify/<pkg>/register` module and injected into the
54
- * bundle. Only applies to GJS app builds.
29
+ * Comma-separated global identifiers the code needs, e.g.
30
+ * `"fetch,Buffer,process,URL,crypto"`. Each maps to the corresponding
31
+ * `@gjsify/<pkg>/register` module, injected into the bundle. GJS app builds only.
55
32
  */
56
33
  globals?: string;
57
34
  /**
58
- * Prepend a `#!/usr/bin/env -S gjs -m` shebang to the output file and mark
59
- * it executable (chmod 755). Only applies to GJS app builds with a single
60
- * `--outfile`. Default: false.
35
+ * Prepend a `#!/usr/bin/env -S gjs -m` shebang and chmod 755. GJS app builds with
36
+ * a single `--outfile` only. Default: false.
61
37
  */
62
38
  shebang?: boolean;
63
39
  /**
64
- * Module names that should NOT be bundled. Each name remains as a literal
65
- * `import`/`require` in the output and is resolved by the runtime against
66
- * its own `node_modules` (or equivalent) at execution time.
67
- *
68
- * Repeat the flag or pass a comma-separated value:
69
- * `--external typedoc,prettier --external typescript`. Glob-style wildcards
70
- * (`@inquirer/*`, `lodash-*`) are forwarded as-is to esbuild.
71
- *
72
- * @see https://esbuild.github.io/api/#external
40
+ * Module names that must NOT be bundled: each stays a literal `import`/`require`
41
+ * in the output, resolved by the runtime at execution time. Repeat the flag or
42
+ * pass a comma-separated value (`--external typedoc,prettier --external
43
+ * typescript`); glob-style wildcards (`@inquirer/*`, `lodash-*`) pass through to
44
+ * the bundler unchanged.
73
45
  */
74
46
  external?: string[];
75
47
  /**
76
- * Substitute compile-time constants in the bundle. Each entry is a
77
- * `KEY=VALUE` pair where `VALUE` is an arbitrary JS expression — string
78
- * literals must be quoted (`--define VERSION='"1.2.3"'`). Useful for
79
- * upstream packages that read a build-time constant via
48
+ * Compile-time constants as `KEY=VALUE`, where `VALUE` is an arbitrary JS
49
+ * expression — string literals must be quoted (`--define VERSION='"1.2.3"'`).
50
+ * For upstream packages reading a build-time constant via
80
51
  * `typeof __FOO__ !== 'undefined'`.
81
- *
82
- * @see https://esbuild.github.io/api/#define
83
52
  */
84
53
  define?: string[];
85
54
  /**
86
- * Map module specifiers to alternative targets at bundle time. Each entry
87
- * is `FROM=TO` where `FROM` is the imported package name and `TO` is the
88
- * substitute (typically `@gjsify/empty` to drop a heavy dep that the test
89
- * scenario never executes). Layered on top of the built-in alias map.
55
+ * Remap module specifiers at bundle time as `FROM=TO`, typically to
56
+ * `@gjsify/empty` to drop a heavy dep the scenario never executes. Layered on top
57
+ * of the built-in alias map.
90
58
  */
91
59
  alias?: string[];
92
60
  /**
93
- * Comma-separated global identifiers to remove from the auto-detected set.
94
- * Useful for false positives from dead browser-compat code in npm deps
95
- * whose polyfills require unavailable native libraries.
96
- * Example: `--exclude-globals fetch,XMLHttpRequest`
61
+ * Comma-separated global identifiers to drop from the auto-detected set — for
62
+ * false positives from dead browser-compat code in npm deps whose polyfills need
63
+ * unavailable native libraries. Example: `--exclude-globals fetch,XMLHttpRequest`.
97
64
  */
98
65
  excludeGlobals?: string[];
99
66
  /**
100
- * Watch source files and rebuild on change. Logs each rebuild with
101
- * duration; SIGINT/SIGTERM cleanly closes the watcher. Only valid with
102
- * `--app gjs|node|browser` (rejected with `--library`). Requires the
103
- * npm `rolldown` engine — run under Node.
67
+ * Watch and rebuild on change; SIGINT/SIGTERM closes the watcher cleanly. Valid
68
+ * only with `--app gjs|node|browser`, rejected with `--library`, and requires the
69
+ * npm `rolldown` engine — so run it under Node.
104
70
  */
105
71
  watch?: boolean;
106
72
  }
@@ -8,19 +8,16 @@ export interface Command<T = unknown, U = T> {
8
8
  deprecated?: boolean | string;
9
9
  }
10
10
  /**
11
- * A command that definitely runs something — the shape of every command except
12
- * the builder-only parents.
11
+ * A command that definitely runs something — every command except the
12
+ * builder-only parents. Annotate any command a standalone entry invokes directly.
13
13
  *
14
- * WHY IT EXISTS: `Command.handler` has to stay optional for parent commands,
15
- * but that optionality then lands on every CALL site, including the ones
16
- * outside `cli-app.ts`'s yargs registration where a missing handler would be a
17
- * silent no-op rather than a type error. `src/affected-entry.ts` — the entry
18
- * the committed `dist/affected.gjs.mjs` is built from — invoked
19
- * `affectedCommand.handler(...)` through a cast that suppressed exactly that,
20
- * and nothing noticed for as long as the package's tsconfig scoped the compile
21
- * to the import closure of `src/index.ts`. Annotate a command that a standalone
22
- * entry invokes directly with this type, and the guarantee is in the type
23
- * instead of in a cast.
14
+ * `Command.handler` must stay optional for parent commands, but that optionality
15
+ * then lands on every CALL site, including those outside `cli-app.ts`'s yargs
16
+ * registration, where a missing handler is a silent no-op rather than a type
17
+ * error. `src/affected-entry.ts` — the entry `dist/affected.gjs.mjs` is built
18
+ * from — invoked `affectedCommand.handler(...)` through a cast that suppressed
19
+ * exactly that, unnoticed for as long as the tsconfig scoped the compile to the
20
+ * import closure of `src/index.ts`. This puts the guarantee in the type instead.
24
21
  */
25
22
  export type LeafCommand<T = unknown, U = T> = Command<T, U> & {
26
23
  handler: NonNullable<Command<T, U>['handler']>;