@gjsify/cli 0.51.1 → 0.52.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 (61) hide show
  1. package/dist/cli.gjs.mjs +505 -490
  2. package/lib/cli-app.js +3 -1
  3. package/lib/commands/affected-classify.d.ts +7 -1
  4. package/lib/commands/affected-classify.js +66 -9
  5. package/lib/commands/affected-script-refs.d.ts +20 -0
  6. package/lib/commands/affected-script-refs.js +218 -0
  7. package/lib/commands/affected.js +24 -2
  8. package/lib/commands/flatpak/diff.js +8 -2
  9. package/lib/commands/flatpak/init.js +4 -1
  10. package/lib/commands/flatpak/sources.js +4 -3
  11. package/lib/commands/flatpak/sync-flathub.d.ts +2 -0
  12. package/lib/commands/flatpak/sync-flathub.js +149 -7
  13. package/lib/commands/flatpak/utils.d.ts +10 -0
  14. package/lib/commands/flatpak/utils.js +10 -0
  15. package/lib/commands/index.d.ts +1 -0
  16. package/lib/commands/index.js +1 -0
  17. package/lib/commands/install.js +76 -0
  18. package/lib/commands/link.d.ts +13 -0
  19. package/lib/commands/link.js +243 -0
  20. package/lib/commands/onboard.js +18 -2
  21. package/lib/commands/publish.d.ts +34 -0
  22. package/lib/commands/publish.js +153 -8
  23. package/lib/commands/ship.js +19 -1
  24. package/lib/types/config-data.d.ts +1 -1
  25. package/lib/utils/check-system-deps.d.ts +1 -17
  26. package/lib/utils/check-system-deps.js +10 -68
  27. package/lib/utils/dev-link.d.ts +222 -0
  28. package/lib/utils/dev-link.js +632 -0
  29. package/lib/utils/hang-watchdog.d.ts +95 -0
  30. package/lib/utils/hang-watchdog.js +194 -0
  31. package/lib/utils/install-backend-native.d.ts +14 -0
  32. package/lib/utils/install-backend-native.js +91 -13
  33. package/lib/utils/install-extraneous.d.ts +90 -0
  34. package/lib/utils/install-extraneous.js +120 -0
  35. package/lib/utils/install-tarball-cache.d.ts +37 -9
  36. package/lib/utils/install-tarball-cache.js +59 -14
  37. package/lib/utils/normalize-bundler-options.js +11 -0
  38. package/lib/utils/publish-pins.d.ts +38 -0
  39. package/lib/utils/publish-pins.js +47 -0
  40. package/lib/utils/publish-readback.d.ts +103 -17
  41. package/lib/utils/publish-readback.js +326 -47
  42. package/lib/utils/run-gjs.js +82 -6
  43. package/lib/utils/ship/icns.d.ts +14 -0
  44. package/lib/utils/ship/icns.js +83 -0
  45. package/lib/utils/ship/ico.d.ts +25 -0
  46. package/lib/utils/ship/ico.js +120 -0
  47. package/lib/utils/ship/icons.d.ts +191 -0
  48. package/lib/utils/ship/icons.js +398 -0
  49. package/lib/utils/ship/launcher.js +16 -1
  50. package/lib/utils/ship/layout.d.ts +32 -0
  51. package/lib/utils/ship/layout.js +112 -38
  52. package/lib/utils/ship/msi.d.ts +1 -1
  53. package/lib/utils/ship/msi.js +30 -5
  54. package/lib/utils/ship/pe-launcher.d.ts +18 -0
  55. package/lib/utils/ship/pe-launcher.js +125 -4
  56. package/lib/utils/ship/plan.d.ts +1 -34
  57. package/lib/utils/ship/plan.js +7 -56
  58. package/lib/utils/ship/plist.d.ts +20 -10
  59. package/lib/utils/ship/plist.js +29 -12
  60. package/lib/utils/ship/settings.js +3 -2
  61. package/package.json +20 -20
@@ -118,7 +118,7 @@ export declare const PACKAGE_DEPS: Record<string, string[]>;
118
118
  * Run all dependency checks. Used by `gjsify check` to show full system status.
119
119
  *
120
120
  * Required deps (gjs, gtk4, libsoup3, libadwaita, gobject-introspection,
121
- * blueprint-compiler, pkg-config, meson) are always checked.
121
+ * pkg-config, meson) are always checked.
122
122
  *
123
123
  * Optional deps are checked conditionally based on which @gjsify/* packages
124
124
  * the project (resolved from cwd) actually consumes. If no project context
@@ -130,22 +130,6 @@ export declare function runAllChecks(cwd: string): DepCheck[];
130
130
  * not required). Used by `gjsify showcase` for examples that have no native deps.
131
131
  */
132
132
  export declare function runMinimalChecks(): DepCheck[];
133
- /**
134
- * Is a blueprint-compiler reachable, by the SAME rule the build uses?
135
- *
136
- * Delegates to `@gjsify/vite-plugin-blueprint/resolve` rather than probing PATH
137
- * here, because the two answers must not be able to disagree: on win32 the
138
- * compiler is normally an MSYS2 script that is deliberately NOT on PATH, so a
139
- * PATH-only check would report "missing" on a host where every `.blp` builds
140
- * fine — a check failing for the wrong reason, which is the failure mode this
141
- * file's own history is made of.
142
- *
143
- * `--version` is not run. MSYS2 ships blueprint-compiler as a shebang script
144
- * with no `.exe`, so `checkBinary` cannot execute it on Windows; presence of the
145
- * resolved pair is the honest answer, and the `source` is worth reporting
146
- * because "found via msys2" explains an install PATH does not show.
147
- */
148
- export declare function checkBlueprintCompiler(requiredBy?: string[]): DepCheck;
149
133
  /** Check gwebgl npm package (project first, CLI fallback). Optional — only needed by @gjsify/webgl users. */
150
134
  export declare function checkGwebgl(cwd: string): DepCheck;
151
135
  /**
@@ -15,9 +15,6 @@ import { dirname, join, resolve } from 'node:path';
15
15
  import { createRequire } from 'node:module';
16
16
  import { pathToFileURL } from 'node:url';
17
17
  import { readFileSync, existsSync, readdirSync } from 'node:fs';
18
- // The zero-dependency subpath, deliberately: importing the plugin root would
19
- // pull vite/execa/minify-xml into a system CHECK.
20
- import { resolveBlueprintCompiler } from '@gjsify/vite-plugin-blueprint/resolve';
21
18
  import { isNode } from '@gjsify/rolldown-plugin-gjsify/runtime';
22
19
  import { findSystemTypelib } from './gi-typelib.js';
23
20
  /** Run a binary and return its stdout trimmed, or null if it fails. */
@@ -393,7 +390,7 @@ function computeNeededOptionalDeps(cwd) {
393
390
  * Run all dependency checks. Used by `gjsify check` to show full system status.
394
391
  *
395
392
  * Required deps (gjs, gtk4, libsoup3, libadwaita, gobject-introspection,
396
- * blueprint-compiler, pkg-config, meson) are always checked.
393
+ * pkg-config, meson) are always checked.
397
394
  *
398
395
  * Optional deps are checked conditionally based on which @gjsify/* packages
399
396
  * the project (resolved from cwd) actually consumes. If no project context
@@ -454,53 +451,19 @@ export function runMinimalChecks() {
454
451
  results.push(checkMsvcRuntime());
455
452
  return results;
456
453
  }
457
- /**
458
- * Is a blueprint-compiler reachable, by the SAME rule the build uses?
459
- *
460
- * Delegates to `@gjsify/vite-plugin-blueprint/resolve` rather than probing PATH
461
- * here, because the two answers must not be able to disagree: on win32 the
462
- * compiler is normally an MSYS2 script that is deliberately NOT on PATH, so a
463
- * PATH-only check would report "missing" on a host where every `.blp` builds
464
- * fine — a check failing for the wrong reason, which is the failure mode this
465
- * file's own history is made of.
466
- *
467
- * `--version` is not run. MSYS2 ships blueprint-compiler as a shebang script
468
- * with no `.exe`, so `checkBinary` cannot execute it on Windows; presence of the
469
- * resolved pair is the honest answer, and the `source` is worth reporting
470
- * because "found via msys2" explains an install PATH does not show.
471
- */
472
- export function checkBlueprintCompiler(requiredBy) {
473
- const resolved = resolveBlueprintCompiler();
474
- return {
475
- id: 'blueprint-compiler',
476
- name: 'blueprint-compiler (.blp templates)',
477
- found: resolved !== null,
478
- version: resolved ? `found via ${resolved.source}` : undefined,
479
- severity: 'optional',
480
- requiredBy,
481
- };
482
- }
483
454
  /** Check gwebgl npm package (project first, CLI fallback). Optional — only needed by @gjsify/webgl users. */
484
455
  export function checkGwebgl(cwd) {
485
456
  return checkNpmPackage('gwebgl', 'gwebgl (@gjsify/webgl)', '@gjsify/webgl', cwd, 'optional', ['@gjsify/webgl']);
486
457
  }
487
458
  /**
488
459
  * Required system dependencies — always checked, missing → exit 1.
489
- * Includes the core build toolchain (pkg-config, meson, blueprint-compiler)
490
- * and the foundational libraries (gtk4, libadwaita, libsoup3,
491
- * gobject-introspection) that nearly every gjsify app needs.
460
+ * Includes the core build toolchain (pkg-config, meson) and the foundational
461
+ * libraries (gtk4, libadwaita, libsoup3, gobject-introspection) that nearly
462
+ * every gjsify app needs.
492
463
  */
493
464
  function runRequiredChecks(_cwd) {
494
465
  const results = [];
495
- // Build toolchain. NO blueprint-compiler here: it is checked by
496
- // `checkBlueprintCompiler()` among the build toolchain below, through the
497
- // SAME resolver the build uses. A second `checkBinary` probe lived here and
498
- // was wrong twice over — it reported a PATH miss on win32, where the
499
- // compiler is normally an MSYS2 script deliberately off PATH (the exact case
500
- // that function's comment exists to explain), and it made `system-check`
501
- // exit 1 on any host without it, `.blp` files in the project or not. It also
502
- // printed a second entry under a second display name and put
503
- // `blueprint-compiler` into the install hint twice.
466
+ // Build toolchain.
504
467
  results.push(checkBinary('pkg-config', 'pkg-config', 'pkg-config', ['--version'], 'required'));
505
468
  results.push(checkBinary('meson', 'Meson', 'meson', ['--version'], 'required'));
506
469
  // Foundational libraries — by TYPELIB, the runtime question. See checkGiLibrary.
@@ -549,13 +512,12 @@ function runNativeBuildToolchainChecks() {
549
512
  // Narrower on purpose: only the three bridges that link an upstream Rust
550
513
  // crate by Cargo path-dependency need a Rust toolchain.
551
514
  const rustBridges = ['@gjsify/lightningcss-native', '@gjsify/oxfmt-native', '@gjsify/rolldown-native'];
552
- // Every showcase/app whose window comes from a `.blp` template. NOT a Vala
553
- // bridge and nothing to do with meson — it is a BUILD-time toolchain in the
554
- // same sense, and it belongs here so `gjsify system-check` names it before a
555
- // build does.
556
- const blueprintConsumers = ['@gjsify/vite-plugin-blueprint'];
515
+ // NO blueprint-compiler row, and it is a deletion rather than an omission: ADR 0053 clause 5
516
+ // moved the `.blp` transform into `@gjsify/blueprint`, so a `.blp` template asks nothing of
517
+ // this host any more. A row for it would be this command asking for a toolchain the build
518
+ // cannot use — the exact "check failing for the wrong reason" its own history is made of.
519
+ // ADR 0063 is the deletion and what the compiler is still FOR.
557
520
  return [
558
- checkBlueprintCompiler(blueprintConsumers),
559
521
  checkBinary('ninja', 'Ninja', 'ninja', ['--version'], 'optional', undefined, valaBridges),
560
522
  checkBinary('vala', 'Vala', 'valac', ['--version'], 'optional', (out) => out.replace(/^Vala\s+/i, ''), valaBridges),
561
523
  checkBinary('cargo', 'Cargo (Rust)', 'cargo', ['--version'], 'optional', (out) => out.replace(/^cargo\s+/i, '').split(' ')[0] ?? out, rustBridges),
@@ -596,7 +558,6 @@ const PM_PACKAGES = {
596
558
  apt: {
597
559
  nodejs: 'nodejs',
598
560
  gjs: 'gjs',
599
- 'blueprint-compiler': 'blueprint-compiler',
600
561
  'pkg-config': 'pkg-config',
601
562
  meson: 'meson',
602
563
  ninja: 'ninja-build',
@@ -622,7 +583,6 @@ const PM_PACKAGES = {
622
583
  dnf: {
623
584
  nodejs: 'nodejs',
624
585
  gjs: 'gjs',
625
- 'blueprint-compiler': 'blueprint-compiler',
626
586
  'pkg-config': 'pkgconf-pkg-config',
627
587
  meson: 'meson',
628
588
  ninja: 'ninja-build',
@@ -648,7 +608,6 @@ const PM_PACKAGES = {
648
608
  pacman: {
649
609
  nodejs: 'nodejs',
650
610
  gjs: 'gjs',
651
- 'blueprint-compiler': 'blueprint-compiler',
652
611
  'pkg-config': 'pkgconf',
653
612
  meson: 'meson',
654
613
  ninja: 'ninja',
@@ -674,7 +633,6 @@ const PM_PACKAGES = {
674
633
  zypper: {
675
634
  nodejs: 'nodejs',
676
635
  gjs: 'gjs',
677
- 'blueprint-compiler': 'blueprint-compiler',
678
636
  'pkg-config': 'pkg-config',
679
637
  meson: 'meson',
680
638
  ninja: 'ninja',
@@ -700,7 +658,6 @@ const PM_PACKAGES = {
700
658
  apk: {
701
659
  nodejs: 'nodejs',
702
660
  gjs: 'gjs',
703
- 'blueprint-compiler': 'blueprint-compiler',
704
661
  'pkg-config': 'pkgconf',
705
662
  meson: 'meson',
706
663
  ninja: 'ninja',
@@ -748,7 +705,6 @@ const PM_PACKAGES = {
748
705
  brew: {
749
706
  nodejs: 'node',
750
707
  gjs: 'gjs',
751
- 'blueprint-compiler': 'blueprint-compiler',
752
708
  'pkg-config': 'pkgconf',
753
709
  meson: 'meson',
754
710
  ninja: 'ninja',
@@ -804,24 +760,11 @@ export function buildInstallCommand(pm, missing) {
804
760
  const pkgMap = PM_PACKAGES[pm];
805
761
  const pkgs = [];
806
762
  const npmDeps = [];
807
- const standalone = [];
808
763
  for (const dep of missing) {
809
764
  if (dep.id === 'gwebgl') {
810
765
  npmDeps.push('@gjsify/webgl');
811
766
  continue;
812
767
  }
813
- // blueprint-compiler on Windows is the one dep whose install command is
814
- // not this host's package manager. winget has no package for it, and it
815
- // cannot get one that works: the tool needs PyGObject, which publishes
816
- // no Windows wheel, so MSYS2 — which ships Python, PyGObject and the
817
- // typelibs already fitted together — is the only source. Its command is
818
- // not `winget install`-shaped, so it cannot live in PM_PACKAGES without
819
- // producing a line that looks right and fails.
820
- if (dep.id === 'blueprint-compiler' && process.platform === 'win32') {
821
- standalone.push('pacman -S mingw-w64-ucrt-x86_64-blueprint-compiler mingw-w64-ucrt-x86_64-gtk4 ' +
822
- 'mingw-w64-ucrt-x86_64-libadwaita # inside MSYS2; found automatically afterwards');
823
- continue;
824
- }
825
768
  // No `continue` for `nodejs`. It USED to be unmissable — the row was the
826
769
  // literal `found: true` — and since it became a real probe it can report
827
770
  // absent under GJS, so skipping it here printed "Missing optional: Node.js"
@@ -842,7 +785,6 @@ export function buildInstallCommand(pm, missing) {
842
785
  if (npmDeps.length > 0) {
843
786
  lines.push(`npm install ${npmDeps.join(' ')}`);
844
787
  }
845
- lines.push(...standalone);
846
788
  return lines.length > 0 ? lines.join('\n ') : null;
847
789
  }
848
790
  /**
@@ -0,0 +1,222 @@
1
+ import { type Workspace } from '@gjsify/workspace';
2
+ /** The consumer-local override file. Git-ignored; never committed. */
3
+ export declare const DEV_LINK_FILE = ".gjsify-link.json";
4
+ /**
5
+ * What `.gjsify-link.json` holds.
6
+ *
7
+ * A POINTER plus a selector, never a materialised list of package names: `link`
8
+ * and `install` then derive the same set from the same two inputs, so a checkout
9
+ * that gains or loses a workspace package cannot leave the two disagreeing about
10
+ * what is linked.
11
+ */
12
+ export interface DevLinkOverride {
13
+ /** Format version — an unknown one is refused rather than guessed at. */
14
+ version: 1;
15
+ /** The gjsify checkout to link from. Absolute, as written by `gjsify link`. */
16
+ checkout: string;
17
+ /**
18
+ * Name globs selecting which of its workspace packages take part. EMPTY means
19
+ * every one of them — deliberately not `["*"]`, which would select NOTHING:
20
+ * measured, `@gjsify/workspace`'s glob dialect maps `*` to `[^/]*`, so a bare
21
+ * star never matches a SCOPED name. The spelling for a whole scope is
22
+ * `@gjsify/*`, the same dialect `gjsify foreach --include` takes.
23
+ */
24
+ packages: string[];
25
+ }
26
+ /** One `node_modules/<name>` → checkout package directory link. */
27
+ export interface DevLink {
28
+ name: string;
29
+ /** Absolute path of the workspace package in the checkout. */
30
+ target: string;
31
+ /** Absolute path of the link inside the consumer's `node_modules`. */
32
+ linkPath: string;
33
+ }
34
+ /** An override that has been read, validated and turned into a link plan. */
35
+ export interface ActiveDevLinks {
36
+ /** Absolute path of the override file, for every message that names it. */
37
+ overridePath: string;
38
+ /** The consumer root the override belongs to. */
39
+ consumerRoot: string;
40
+ /** The validated checkout it points at. */
41
+ checkout: string;
42
+ links: DevLink[];
43
+ /** Just the names — what the installer excludes from its fetch set. */
44
+ names: Set<string>;
45
+ }
46
+ export declare function devLinkPath(root: string): string;
47
+ /**
48
+ * Read the override at `root`, or `null` when there is none.
49
+ *
50
+ * A file that exists but does not parse is an ERROR, not a `null`: this decides
51
+ * where a build's inputs come from, and "unreadable" must never be answered with
52
+ * the registry's copy while the developer believes their checkout is in play.
53
+ */
54
+ export declare function readDevLinkOverride(root: string): DevLinkOverride | null;
55
+ export declare function writeDevLinkOverride(root: string, override: DevLinkOverride): string;
56
+ /** Remove the override file; returns its path when one was there. */
57
+ export declare function removeDevLinkOverride(root: string): string | null;
58
+ /**
59
+ * Which directory's override governs an install started in `startDir`.
60
+ *
61
+ * Same walk `projectInstallNative` does for the install itself: a member of a
62
+ * workspace installs the workspace, so the override that applies is the
63
+ * workspace root's — otherwise `gjsify install` inside a member would silently
64
+ * ignore a link the root declares.
65
+ */
66
+ export declare function devLinkRoot(startDir: string): string;
67
+ /**
68
+ * Turn `override.checkout` into the workspace list behind it, or refuse.
69
+ *
70
+ * A DEAD LINK IS AN ERROR WITH A PATH, never a quiet fall back to the registry:
71
+ * the developer asked for their checkout, and installing the published copy
72
+ * instead produces a build that looks like the one they wanted and is not. Both
73
+ * refusals print the measured path, because "it points somewhere wrong" is only
74
+ * actionable with the somewhere in it.
75
+ */
76
+ export declare function resolveCheckoutWorkspaces(checkout: string, overridePath: string): Workspace[];
77
+ /**
78
+ * The dependency names the consumer would otherwise take from the registry.
79
+ *
80
+ * Two readers, one answer: what its manifests DECLARE (so a fresh clone can be
81
+ * linked before it has ever installed) and what its `node_modules` already HOLDS
82
+ * (so transitive `@gjsify/*` packages — the ones nobody declares and every build
83
+ * loads — are covered too).
84
+ */
85
+ export declare function consumerKnownNames(consumerRoot: string): Set<string>;
86
+ /**
87
+ * The links an override asks for: the checkout's workspace packages, narrowed by
88
+ * the name globs, INTERSECTED with what the consumer actually depends on.
89
+ *
90
+ * The intersection is the point. Linking every workspace package of a 200-member
91
+ * monorepo into a consumer that uses eight would put 200 directories it never
92
+ * declared into its tree, and leave `gjsify unlink` with no honest answer about
93
+ * what to restore. Pure, so every selection rule is decidable without a
94
+ * filesystem.
95
+ */
96
+ export declare function planDevLinks(opts: {
97
+ consumerRoot: string;
98
+ workspaces: readonly Workspace[];
99
+ patterns: readonly string[];
100
+ knownNames: ReadonlySet<string>;
101
+ }): DevLink[];
102
+ /**
103
+ * Read + validate the override at `consumerRoot` and plan its links, or `null`
104
+ * when no override is there. Throws for every other shape — see
105
+ * {@link resolveCheckoutWorkspaces}.
106
+ */
107
+ export declare function prepareDevLinks(consumerRoot: string): ActiveDevLinks | null;
108
+ /**
109
+ * (Re)create every planned link. Idempotent — a link already pointing at the
110
+ * right target is left alone, so a re-run costs no unlink/relink race against a
111
+ * process reading through it.
112
+ *
113
+ * Returns the links it actually wrote.
114
+ */
115
+ export declare function applyDevLinks(links: readonly DevLink[]): DevLink[];
116
+ /**
117
+ * Remove the links an override produced.
118
+ *
119
+ * ONLY a symlink whose target is inside the checkout is removed. A real
120
+ * directory at that path is the registry copy — some other tool's, or an install
121
+ * that ran with the override already gone — and deleting it would turn `unlink`
122
+ * into a destructive command for a tree it did not create. Same asymmetry
123
+ * `install-extraneous.ts` reasons from: a wrong deletion loses work, a wrong
124
+ * refusal costs one message.
125
+ */
126
+ export declare function removeDevLinks(links: readonly DevLink[]): DevLink[];
127
+ /**
128
+ * Every `node_modules/<name>` symlink that resolves INSIDE one of `checkouts`.
129
+ *
130
+ * The PLAN is not a census. `removeDevLinks` only ever visits the links the
131
+ * CURRENT override asks for, so a link the previous override made and the new one
132
+ * dropped is invisible to it — measured: link everything, relink with
133
+ * `--packages 'is-odd'`, and `node_modules/is-number` stays pointing into the
134
+ * checkout while nothing in the plan names it. From there `assertNodeModulesDest`
135
+ * aborted EVERY later `gjsify install`, and `gjsify unlink` could not help because
136
+ * it removed the override (the escape route) before hitting the same abort. Only a
137
+ * hand-written `rm` got the project back.
138
+ *
139
+ * So the ground truth for "what is linked" is the tree, not the file. Reads one
140
+ * level of `node_modules` plus one level into each `@scope/`, which is where
141
+ * top-level links live; `.bin` and real directories are skipped by construction.
142
+ */
143
+ export declare function scanDevLinks(consumerRoot: string, checkouts: readonly string[]): DevLink[];
144
+ /**
145
+ * Remove every dev link into `checkouts` that `keep` does not name.
146
+ *
147
+ * What makes re-linking with a narrower `--packages` a RE-selection rather than an
148
+ * addition, and what makes `unlink` leave the tree as it found it. Pass an empty
149
+ * `keep` to retire all of them.
150
+ */
151
+ export declare function retireDevLinks(consumerRoot: string, checkouts: readonly string[], keep: ReadonlySet<string>): DevLink[];
152
+ /** A linked package that cannot be imported, and why not. */
153
+ export interface UnbuiltDevLink {
154
+ link: DevLink;
155
+ reason: string;
156
+ }
157
+ /**
158
+ * Which of these links point at something that is not built.
159
+ *
160
+ * Candidates are the string entry points a manifest can carry — `main`, `module`
161
+ * and the string leaves under `exports["."]`. ANY of them existing is enough: a
162
+ * package legitimately declares entries it does not ship on every platform, and a
163
+ * refusal that fires on a healthy tree is a refusal people learn to route around.
164
+ * An entry-LESS manifest is fine, because nothing was promised that could be
165
+ * missing.
166
+ *
167
+ * An UNREADABLE manifest is NOT fine, and that is a flip from the first draft,
168
+ * which answered "built" for it. "I could not tell" is not "yes" in a check whose
169
+ * whole job is to stop a silently unusable tree.
170
+ */
171
+ export declare function unbuiltDevLinks(links: readonly DevLink[]): UnbuiltDevLink[];
172
+ /** The refusal text for {@link unbuiltDevLinks}, naming every path and the build. */
173
+ export declare function formatUnbuiltDevLinks(unbuilt: readonly UnbuiltDevLink[]): string;
174
+ /**
175
+ * FAIL CLOSED on a link to something unbuilt.
176
+ *
177
+ * `dist/`/`lib/` are git-ignored in a gjsify checkout, so "the entry point is not
178
+ * there right now" is the ordinary consequence of a branch switch, not an exotic
179
+ * state. Measured: with the entry file deleted, `gjsify install` re-linked and
180
+ * printed only "development link ACTIVE"; the consumer then died on
181
+ * MODULE_NOT_FOUND with a message naming the consumer, not the checkout.
182
+ */
183
+ export declare function assertDevLinksBuilt(links: readonly DevLink[]): void;
184
+ /**
185
+ * Make `.gjsify-link.json` invisible to git WITHOUT editing a tracked file.
186
+ *
187
+ * `.git/info/exclude` is git's own per-clone ignore list: it is not committed and
188
+ * not shared, which is exactly the lifetime of the thing it hides. Writing to
189
+ * `.gitignore` instead would put a developer's local override into everyone
190
+ * else's diff — the manifest edit this whole feature exists to avoid, one file
191
+ * over.
192
+ *
193
+ * Returns what it did, so the caller can say so. A consumer that is not a git
194
+ * repository is not an error: nothing can commit the file there either. A
195
+ * consumer whose `.git` POINTER cannot be followed IS worth a word, though —
196
+ * there the file stays visible to `git status` and one `git add -A` commits it.
197
+ */
198
+ export declare function ensureLocallyIgnored(consumerRoot: string): 'added' | 'already-ignored' | 'no-git' | 'unreadable-git';
199
+ /**
200
+ * Undo {@link ensureLocallyIgnored} — the other half of `unlink`.
201
+ *
202
+ * Measured: the `.git/info/exclude` block outlived `gjsify unlink` (idempotently,
203
+ * so it never grew — it just never left). A command that promises to undo itself
204
+ * and leaves a line behind is not an undo, and the leftover is a rule about a file
205
+ * that no longer exists, which the next reader has to research to delete.
206
+ *
207
+ * ONLY the two lines this module wrote, matched as a pair: a `.gitignore` entry or
208
+ * a hand-written exclude line is someone else's decision, and removing it would be
209
+ * the same overreach `removeDevLinks` refuses.
210
+ */
211
+ export declare function removeLocalIgnore(consumerRoot: string): 'removed' | 'absent' | 'no-git' | 'unreadable-git';
212
+ /**
213
+ * `--immutable` must never accept an active override — FAIL CLOSED.
214
+ *
215
+ * CI, a Flatpak build and a release all run `--immutable`, and all three exist to
216
+ * build exactly what is committed. An override is a developer's local tree, so
217
+ * consuming one here would produce an artifact whose inputs are not in any
218
+ * commit and whose build reported nothing unusual. Refusing names the file,
219
+ * because the fix is to delete it and the message is the only place its path
220
+ * appears.
221
+ */
222
+ export declare function assertNoDevLinkUnderImmutable(startDir: string): void;