@gjsify/cli 0.36.0 → 0.38.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 (122) hide show
  1. package/dist/cli.gjs.mjs +169 -169
  2. package/lib/actions/build.d.ts +7 -0
  3. package/lib/actions/build.js +18 -3
  4. package/lib/bundler-pick.js +61 -87
  5. package/lib/cli-fail.d.ts +4 -7
  6. package/lib/cli-fail.js +29 -55
  7. package/lib/commands/affected.js +161 -286
  8. package/lib/commands/install.d.ts +63 -66
  9. package/lib/commands/install.js +372 -536
  10. package/lib/config.d.ts +14 -0
  11. package/lib/config.js +18 -1
  12. package/lib/templates/install.mjs.tmpl +164 -75
  13. package/lib/types/cli-build-options.d.ts +33 -67
  14. package/lib/types/command.d.ts +9 -12
  15. package/lib/types/config-data.d.ts +137 -221
  16. package/lib/utils/auth-npmrc.d.ts +5 -10
  17. package/lib/utils/auth-npmrc.js +11 -21
  18. package/lib/utils/bin-shim.d.ts +40 -25
  19. package/lib/utils/bin-shim.js +51 -26
  20. package/lib/utils/clear-targets.d.ts +8 -11
  21. package/lib/utils/clear-targets.js +22 -29
  22. package/lib/utils/copy-targets.d.ts +13 -19
  23. package/lib/utils/copy-targets.js +31 -44
  24. package/lib/utils/dep-aggregation.d.ts +7 -15
  25. package/lib/utils/dep-aggregation.js +9 -22
  26. package/lib/utils/detect-native-packages.d.ts +89 -122
  27. package/lib/utils/detect-native-packages.js +119 -169
  28. package/lib/utils/dir-link.d.ts +29 -48
  29. package/lib/utils/dir-link.js +33 -56
  30. package/lib/utils/discover-showcases.d.ts +6 -7
  31. package/lib/utils/discover-showcases.js +11 -15
  32. package/lib/utils/dlx-cache.d.ts +29 -38
  33. package/lib/utils/dlx-cache.js +53 -63
  34. package/lib/utils/force-exit.d.ts +0 -3
  35. package/lib/utils/force-exit.js +13 -24
  36. package/lib/utils/gi-search-path.d.ts +16 -24
  37. package/lib/utils/gi-search-path.js +54 -82
  38. package/lib/utils/gjs-bundle-guard.d.ts +5 -13
  39. package/lib/utils/gjs-bundle-guard.js +35 -57
  40. package/lib/utils/gjs-source-escape.d.ts +2 -2
  41. package/lib/utils/gjs-source-escape.js +21 -39
  42. package/lib/utils/gjsify-shim.d.ts +17 -20
  43. package/lib/utils/gjsify-shim.js +80 -120
  44. package/lib/utils/html-entry.d.ts +8 -12
  45. package/lib/utils/html-entry.js +19 -28
  46. package/lib/utils/install-backend-native.d.ts +123 -161
  47. package/lib/utils/install-backend-native.js +536 -737
  48. package/lib/utils/install-backend.d.ts +42 -57
  49. package/lib/utils/install-backend.js +16 -21
  50. package/lib/utils/install-cache-fs.d.ts +10 -15
  51. package/lib/utils/install-cache-fs.js +18 -26
  52. package/lib/utils/install-lock.d.ts +5 -6
  53. package/lib/utils/install-lock.js +46 -65
  54. package/lib/utils/install-packument-cache.d.ts +27 -33
  55. package/lib/utils/install-packument-cache.js +44 -57
  56. package/lib/utils/install-progress.d.ts +2 -9
  57. package/lib/utils/install-progress.js +14 -39
  58. package/lib/utils/install-tarball-cache.d.ts +14 -20
  59. package/lib/utils/install-tarball-cache.js +41 -59
  60. package/lib/utils/library-output.d.ts +9 -22
  61. package/lib/utils/library-output.js +19 -37
  62. package/lib/utils/load-npmrc.d.ts +5 -6
  63. package/lib/utils/load-npmrc.js +18 -27
  64. package/lib/utils/node-bundle-guard.d.ts +4 -8
  65. package/lib/utils/node-bundle-guard.js +21 -35
  66. package/lib/utils/node-script.js +40 -53
  67. package/lib/utils/node-version.d.ts +5 -14
  68. package/lib/utils/node-version.js +15 -24
  69. package/lib/utils/normalize-bundler-options.d.ts +7 -11
  70. package/lib/utils/normalize-bundler-options.js +24 -42
  71. package/lib/utils/npm-oidc.d.ts +23 -37
  72. package/lib/utils/npm-oidc.js +28 -53
  73. package/lib/utils/npm-otp-cache.d.ts +4 -4
  74. package/lib/utils/npm-otp-cache.js +20 -25
  75. package/lib/utils/npm-otp.d.ts +20 -30
  76. package/lib/utils/npm-otp.js +32 -51
  77. package/lib/utils/onboard-probe.d.ts +7 -13
  78. package/lib/utils/onboard-probe.js +16 -27
  79. package/lib/utils/parse-spec.d.ts +3 -3
  80. package/lib/utils/parse-spec.js +4 -5
  81. package/lib/utils/pkg-json-edit.d.ts +7 -12
  82. package/lib/utils/pkg-json-edit.js +13 -26
  83. package/lib/utils/pkg-json.d.ts +11 -18
  84. package/lib/utils/pkg-json.js +19 -28
  85. package/lib/utils/prefixed-output.d.ts +3 -4
  86. package/lib/utils/prefixed-output.js +13 -19
  87. package/lib/utils/prompt.d.ts +3 -4
  88. package/lib/utils/prompt.js +14 -18
  89. package/lib/utils/publish-diagnose.d.ts +11 -16
  90. package/lib/utils/publish-diagnose.js +15 -28
  91. package/lib/utils/publish-headers.d.ts +7 -11
  92. package/lib/utils/publish-headers.js +36 -65
  93. package/lib/utils/publishable-packages.d.ts +4 -6
  94. package/lib/utils/publishable-packages.js +14 -18
  95. package/lib/utils/resolve-gjs-entry.d.ts +5 -8
  96. package/lib/utils/resolve-gjs-entry.js +10 -27
  97. package/lib/utils/resolve-npm-package.d.ts +6 -11
  98. package/lib/utils/resolve-npm-package.js +29 -63
  99. package/lib/utils/resolve-plugin-by-name.d.ts +11 -19
  100. package/lib/utils/resolve-plugin-by-name.js +9 -14
  101. package/lib/utils/run-gjs.d.ts +16 -21
  102. package/lib/utils/run-gjs.js +31 -56
  103. package/lib/utils/run-lifecycle-script.d.ts +5 -9
  104. package/lib/utils/run-lifecycle-script.js +23 -42
  105. package/lib/utils/run-node.d.ts +21 -33
  106. package/lib/utils/run-node.js +43 -64
  107. package/lib/utils/runtimes.d.ts +25 -35
  108. package/lib/utils/runtimes.js +34 -46
  109. package/lib/utils/spawn.d.ts +32 -48
  110. package/lib/utils/spawn.js +51 -84
  111. package/lib/utils/stdin.d.ts +2 -2
  112. package/lib/utils/stdin.js +10 -25
  113. package/lib/utils/suggest.d.ts +6 -7
  114. package/lib/utils/suggest.js +8 -10
  115. package/lib/utils/system-gi.d.ts +36 -27
  116. package/lib/utils/system-gi.js +85 -114
  117. package/lib/utils/trust-registry.d.ts +4 -8
  118. package/lib/utils/trust-registry.js +7 -15
  119. package/lib/utils/win32-command.d.ts +8 -14
  120. package/lib/utils/win32-command.js +29 -43
  121. package/lib/utils/workspace-root.js +7 -12
  122. package/package.json +18 -18
@@ -1,44 +1,37 @@
1
- // Utility to find npm packages with gjsify native prebuilds.
2
- // Packages declare: "gjsify": { "prebuilds": "<dir>" } in their package.json,
3
- // and (since the OS-axis audit) the `<os>-<arch>[-musl]` targets they promise in
4
- // "gjsify": { "platforms": [...] }. The libc half of that token is the newest
5
- // axis; which libc a package's artifacts actually NEED is a separate, measured
6
- // claim (npm's `libc` field + `gjsify.glibcRequires`, held to the binaries by the
7
- // `prebuild-libc` conformance rule) — this file only resolves DIRECTORIES.
1
+ // Find npm packages carrying gjsify native prebuilds. Packages declare
2
+ // `"gjsify": { "prebuilds": "<dir>" }` plus the `<os>-<arch>[-musl]` targets they
3
+ // promise in `"gjsify": { "platforms": [...] }`. This file only resolves
4
+ // DIRECTORIES — which libc a package's artifacts actually NEED is a separate
5
+ // measured claim (npm's `libc` field + `gjsify.glibcRequires`, held to the binaries
6
+ // by the `prebuild-libc` conformance rule).
8
7
  //
9
- // TWO passes, and the second one exists because the first cannot see an isolated
10
- // (pnpm-style) layout: `detectNativePackages(startDir)` walks up from `startDir`
11
- // and exhaustively scans every `node_modules` it finds, then resolves any package
12
- // that DECLARED a prebuild without shipping one through its per-platform
13
- // companion package (`<name>-<token>`), searching from THAT package's own
14
- // directory. See `resolvePlatformSibling`. Used by:
8
+ // TWO passes. `detectNativePackages(startDir)` walks up from `startDir` scanning
9
+ // every `node_modules`, then resolves any package that DECLARED a prebuild without
10
+ // shipping one through its per-platform companion (`<name>-<token>`), searching
11
+ // from THAT package's own directory (`resolvePlatformSibling`). The second pass
12
+ // exists because the first cannot see an isolated (pnpm-style) layout. Call sites:
15
13
  // * `gjsify run`, `gjsify info`, `gjsify install` — startDir = process.cwd()
16
- // * `runGjsBundle()` — startDir = dirname(bundlePath), so DLX-cache layouts
17
- // (`~/.cache/gjsify/dlx/<sha>/.../node_modules/<pkg>/dist/bundle.js`) get
18
- // their full transitive prebuild set picked up automatically. The
19
- // transitive walk is what makes `gjsify showcase` / `gjsify dlx` work
20
- // for packages whose Vala typelibs live in *indirect* deps.
14
+ // * `runGjsBundle()` — startDir = dirname(bundlePath), so DLX-cache layouts pick
15
+ // up their full transitive prebuild set. That transitive walk is what makes
16
+ // `gjsify showcase` / `gjsify dlx` work for packages whose Vala typelibs live
17
+ // in *indirect* deps.
21
18
  //
22
- // PLATFORM RESOLUTION IS A PURE FUNCTION. Everything that depends on the host
23
- // OS/CPU/libc lives in `resolvePrebuildDirName()` / `buildNativeEnv()` /
24
- // `resolveHostLibc()`, all of which take `platform` / `arch` / `libc` as
25
- // parameters instead of reading `process.platform` inline. That is what makes
26
- // the darwin, win32 AND musl branches unit-testable from a glibc Linux host
27
- // (see `detect-native-packages.spec.ts`) — the host values are only read at the
28
- // outermost call site, as a default.
19
+ // PLATFORM RESOLUTION IS A PURE FUNCTION: `resolvePrebuildDirName()`,
20
+ // `buildNativeEnv()` and `resolveHostLibc()` take `platform`/`arch`/`libc` as
21
+ // parameters instead of reading `process.*` inline, which is what makes the darwin,
22
+ // win32 AND musl branches unit-testable from a glibc Linux host. The host values
23
+ // are read only at the outermost call site, as a default.
29
24
  import { readdirSync, existsSync } from 'node:fs';
30
25
  import { join, resolve } from 'node:path';
31
26
  import { readPackageJson } from './pkg-json.js';
32
- import { splitSearchPath, systemGiLibraryDirs } from './system-gi.js';
27
+ import { composeDyldFallback, systemGiLibraryDirs } from './system-gi.js';
33
28
  /**
34
- * Map a Node `process.arch` value to the uname-style spelling the Vala/meson
35
- * bridges used to stage into (`prebuilds/linux-x86_64/`, the output of
36
- * `uname -m` on the build host).
37
- *
38
- * LEGACY. Every `@gjsify/*` package now declares and stages the node spelling
39
- * (`linux-x64`); this table exists only so the CLI can still load a prebuild
40
- * out of a tarball published before the rename. See
41
- * {@link prebuildDirCandidates}.
29
+ * Node `process.arch` → the uname-style spelling the Vala/meson bridges used to stage
30
+ * into (`prebuilds/linux-x86_64/`).
31
+ *
32
+ * LEGACY: every `@gjsify/*` package now stages the node spelling (`linux-x64`), and
33
+ * this table exists only so the CLI can still load a prebuild out of a tarball
34
+ * published before the rename. See {@link prebuildDirCandidates}.
42
35
  */
43
36
  const NODE_ARCH_TO_LEGACY_UNAME = {
44
37
  x64: 'x86_64',
@@ -47,16 +40,14 @@ const NODE_ARCH_TO_LEGACY_UNAME = {
47
40
  ia32: 'i686',
48
41
  };
49
42
  /**
50
- * Arch spellings that name the same target, folded onto the NODE spelling.
51
- * Mirrors `ARCH_ALIASES` in `packages/infra/manifest-conformance/lib/platforms.mjs`
52
- * — the table `scripts/audit-runtimes.mjs` canonicalises
53
- * `package.json#gjsify.platforms` through for the platform-support matrix and the
54
- * prebuild rules. The two MUST agree or a package could pass the audit while the
55
- * CLI fails to find its artifact.
56
- *
57
- * The direction matters: `${process.platform}-${process.arch}` is what a
58
- * running process can compute about itself, so folding *onto* it means the
59
- * host identity never has to be translated in the hot path.
43
+ * Arch spellings that name the same target, folded onto the NODE spelling. MUST agree
44
+ * with `ARCH_ALIASES` in `packages/infra/manifest-conformance/lib/platforms.mjs` (what
45
+ * `scripts/audit-runtimes.mjs` canonicalises `gjsify.platforms` through), or a package
46
+ * passes the audit while the CLI fails to find its artifact.
47
+ *
48
+ * The direction matters: `${process.platform}-${process.arch}` is what a running
49
+ * process can compute about itself, so folding *onto* it means the host identity is
50
+ * never translated in the hot path.
60
51
  */
61
52
  const ARCH_ALIASES = {
62
53
  x86_64: 'x64',
@@ -70,35 +61,27 @@ const ARCH_ALIASES = {
70
61
  */
71
62
  const MUSL_SUFFIX = '-musl';
72
63
  /**
73
- * Split a prebuild target into its three axes.
74
- *
75
- * TOKEN GRAMMAR: `<os>-<arch>[-musl]`.
76
- *
77
- * An UNSUFFIXED token means "the default build". Note what it does NOT mean: it
78
- * is not a synonym for glibc. For an artifact that links `libc.so.6` the default
79
- * build IS glibc; for one that links only GLib/GObject/GIO (plus GnuTLS resp.
80
- * GStreamer) it is libc-AGNOSTIC and loads against whatever libc the host's GLib
81
- * was built for. The directory name cannot express that difference and does not
82
- * need to: the package's own `libc` field answers it at install time, and the
83
- * `prebuild-libc` conformance rule verifies that field against the binaries
84
- * (`readElfNeeded` — the DT_NEEDED list is where the answer actually lives).
85
- *
86
- * DELIBERATELY NOT CHOSEN: renaming every existing `linux-<arch>` directory to
87
- * `linux-<arch>-gnu`, which is what a symmetric grammar would demand. It would
88
- * rename ~60 COMMITTED directories, and — per `status/open-todos.md`'s
89
- * "Nine fixtures re-implement the prebuild-target name instead of importing it"
90
- * — nine e2e fixtures COMPOSE the token themselves instead of importing it, so
91
- * the rename would have to be swept by hand in two shapes (the literal path and
92
- * the computed one), which is exactly how the last vocabulary change missed
93
- * `tests/e2e/self-host/run.mjs`. The asymmetric grammar buys the same
94
- * expressiveness for zero renames, and the cost — that "unsuffixed" is not
95
- * self-describing — is paid by a field that is machine-checked anyway.
96
- *
97
- * A `-musl` suffix is only meaningful when the OS half is `linux`: musl targets
98
- * no other kernel, and npm's own `libc` field is documented as Linux-only. A
99
- * `darwin-arm64-musl` token is therefore not a target with an odd name, it is a
100
- * malformed one, and this returns `libc: null` for it so callers report the
101
- * token rather than half-honouring it.
64
+ * Split a prebuild target into its three axes. TOKEN GRAMMAR: `<os>-<arch>[-musl]`.
65
+ *
66
+ * An UNSUFFIXED token means "the default build", NOT a synonym for glibc. For an
67
+ * artifact linking `libc.so.6` the default build IS glibc; for one linking only
68
+ * GLib/GObject/GIO it is libc-AGNOSTIC and loads against whatever libc the host's GLib
69
+ * was built for. The directory name cannot express that and does not need to — the
70
+ * package's own `libc` field answers it at install time, verified against the binaries'
71
+ * DT_NEEDED list by the `prebuild-libc` conformance rule.
72
+ *
73
+ * DELIBERATELY NOT CHOSEN: the symmetric grammar, renaming every `linux-<arch>` to
74
+ * `linux-<arch>-gnu`. That is ~60 committed directories plus nine e2e fixtures that
75
+ * COMPOSE the token instead of importing it (open-todos: "Nine fixtures re-implement
76
+ * the prebuild-target name instead of importing it"), so the sweep would have to be
77
+ * done by hand in two shapes — the literal path and the computed one — which is
78
+ * exactly how the last vocabulary change missed `tests/e2e/self-host/run.mjs`. The
79
+ * asymmetric grammar is equally expressive for zero renames.
80
+ *
81
+ * A `-musl` suffix is only meaningful when the OS half is `linux`: musl targets no
82
+ * other kernel and npm's `libc` field is documented Linux-only. `darwin-arm64-musl` is
83
+ * therefore malformed rather than oddly named, and this returns `libc: null` for it so
84
+ * callers report the token instead of half-honouring it.
102
85
  */
103
86
  export function parsePlatformToken(token) {
104
87
  const isMusl = token.endsWith(MUSL_SUFFIX);
@@ -109,17 +92,15 @@ export function parsePlatformToken(token) {
109
92
  return { os, arch, libc: isMusl && os === 'linux' ? 'musl' : null };
110
93
  }
111
94
  /**
112
- * Canonical `<os>-<arch>[-musl]` form — the node spelling — so `linux-x86_64`
113
- * and `linux-x64` compare equal. Only the *arch* half is normalised: the OS half
114
- * is always a `process.platform` token (`linux`/`darwin`/`win32`), which has no
115
- * competing spelling, and the libc half has exactly one spelling by
116
- * construction.
117
- *
118
- * A `-musl` suffix on a non-Linux token is preserved verbatim rather than
119
- * dropped. Silently canonicalising `darwin-arm64-musl` to `darwin-arm64` would
120
- * make a malformed declaration compare EQUAL to a valid one, so the audit that
121
- * rejects it would never see it — the same class of bug as folding the legacy
122
- * uname spelling on a WRITE path.
95
+ * Canonical `<os>-<arch>[-musl]` (node spelling) so `linux-x86_64` and `linux-x64`
96
+ * compare equal. Only the *arch* half is normalised — the OS half is always a
97
+ * `process.platform` token with no competing spelling, and the libc half has exactly
98
+ * one spelling by construction.
99
+ *
100
+ * A `-musl` suffix on a non-Linux token is preserved rather than dropped: silently
101
+ * canonicalising `darwin-arm64-musl` to `darwin-arm64` would make a malformed
102
+ * declaration compare EQUAL to a valid one, so the audit that rejects it would never
103
+ * see it.
123
104
  */
124
105
  export function canonicalPlatformToken(token) {
125
106
  const { os, arch, libc } = parsePlatformToken(token);
@@ -128,37 +109,30 @@ export function canonicalPlatformToken(token) {
128
109
  const canonical = `${os}-${ARCH_ALIASES[arch] ?? arch}`;
129
110
  if (libc === 'musl')
130
111
  return `${canonical}${MUSL_SUFFIX}`;
131
- // Not a Linux musl token, so `-musl` (if present at all) is not a libc
132
- // suffix this grammar recognises — keep the token intact.
112
+ // Not a Linux musl token, so any `-musl` here is not a libc suffix this grammar
113
+ // recognises — keep the token intact.
133
114
  return token.endsWith(MUSL_SUFFIX) ? `${canonical}${MUSL_SUFFIX}` : canonical;
134
115
  }
135
116
  /**
136
- * The target tokens that describe THIS host, most-specific first — the WRITE-side
137
- * vocabulary.
138
- *
139
- * This is the single definition of the musl preference order, and three callers
140
- * share it so it cannot drift: the directory probe ({@link prebuildDirCandidates}),
141
- * the per-platform sibling package probe ({@link platformPackageName}), and
142
- * `scripts/stage-prebuild.mjs`, which must stage a musl build into
143
- * `linux-x64-musl/` and never into `linux-x64/`.
144
- *
145
- * On a musl host the suffixed token comes FIRST and the unsuffixed one stays as a
146
- * fallback: an unsuffixed directory is the default build, which for the
147
- * libc-agnostic bridges (they record no `libc.so.6` at all) genuinely does load
148
- * on musl. On glibc the `-musl` token is not offered at all — a musl artifact
149
- * cannot load against glibc, so probing for it could only ever produce a
150
- * false positive.
151
- *
152
- * @param platform `process.platform` value.
153
- * @param arch `process.arch` value.
154
- * @param libc host C library; `undefined`/`null` behaves as glibc (see
155
- * {@link HostTarget.libc} for why that is the safe default).
117
+ * The target tokens describing THIS host, most-specific first — the WRITE-side
118
+ * vocabulary, and the SINGLE definition of the musl preference order. Three callers
119
+ * share it so it cannot drift: the directory probe ({@link prebuildDirCandidates}), the
120
+ * companion-package probe ({@link platformPackageName}), and `scripts/stage-prebuild.mjs`,
121
+ * which must stage a musl build into `linux-x64-musl/` and never into `linux-x64/`.
122
+ *
123
+ * On a musl host the suffixed token comes FIRST with the unsuffixed one as fallback,
124
+ * because an unsuffixed directory is the default build and the libc-agnostic bridges
125
+ * (no `libc.so.6` recorded at all) genuinely do load on musl. On glibc the `-musl`
126
+ * token is not offered at all — a musl artifact cannot load against glibc, so probing
127
+ * for it could only produce a false positive.
128
+ *
129
+ * @param libc `undefined`/`null` behaves as glibc — see {@link HostTarget.libc}.
156
130
  */
157
131
  export function hostPlatformTokens(platform, arch, libc) {
158
132
  const canonical = `${platform}-${arch}`;
159
- // The suffix is Linux-only by construction, so a `libc: 'musl'` handed in
160
- // for darwin/win32 is ignored rather than trusted — the caller's host facts
161
- // do not get to invent a target the grammar does not have.
133
+ // The suffix is Linux-only by construction, so a `libc: 'musl'` handed in for
134
+ // darwin/win32 is ignored rather than trusted: the caller's host facts do not get
135
+ // to invent a target the grammar does not have.
162
136
  if (libc === 'musl' && platform === 'linux')
163
137
  return [`${canonical}${MUSL_SUFFIX}`, canonical];
164
138
  return [canonical];
@@ -166,44 +140,34 @@ export function hostPlatformTokens(platform, arch, libc) {
166
140
  /**
167
141
  * The prebuild directory names to probe for a host, most-specific first.
168
142
  *
169
- * The canonical name is `${process.platform}-${process.arch}` — `linux-x64`,
170
- * `linux-arm64`, `darwin-arm64`, `win32-x64`. Every package in the release
171
- * train declares it (`gjsify.platforms`), `scripts/stage-prebuild.mjs` is the
172
- * only thing that creates it, and `scripts/audit-runtimes.mjs --check` rejects
173
- * any other spelling, so the three can no longer drift.
174
- *
175
- * Two probes remain BEHIND that canonical name, and both are backward
176
- * compatibility with tarballs already on npm — not a second convention:
177
- *
178
- * 1. **The package's own declared spelling.** A tarball published before the
179
- * rename declares (and ships) `linux-x86_64`; probing its declaration
180
- * first loads it without the CLI having to guess. This is also what makes
181
- * the rename work in the OTHER direction — an older CLI resolving a newer
182
- * package — because `canonicalPlatformToken` folds both spellings onto
183
- * one form at both ends.
184
- * 2. **The legacy uname spelling.** Only reachable for a package that ships
185
- * a prebuild dir and declares NO `gjsify.platforms` at all — i.e. a
186
- * tarball predating the OS-axis audit, or a third-party package using
187
- * `gjsify.prebuilds` without the declaration. Inside this repo the audit
188
- * makes that state impossible.
189
- *
190
- * Both are pure array entries matched against an already-read directory
191
- * listing — no extra I/O — so tolerance on the READ side costs nothing, while
192
- * the single spelling is enforced everywhere a name is WRITTEN.
193
- *
194
- * The LIBC AXIS rides in front of all of that: on a musl host the
195
- * `<os>-<arch>-musl` token is preferred over `<os>-<arch>`, and on glibc the
196
- * suffixed token is NEVER offered (a musl artifact cannot load against glibc, so
197
- * a probe for it can only produce a false positive). The `libc` value is a
198
- * PARAMETER rather than a `process` read for the same reason `platform` and
199
- * `arch` are: it is the only way the musl branch gets exercised at all, since CI
200
- * and every developer machine here run glibc.
201
- *
202
- * @param platform `process.platform` value.
203
- * @param arch `process.arch` value.
143
+ * The canonical name is `${process.platform}-${process.arch}`. Every package in the
144
+ * release train declares it (`gjsify.platforms`), `scripts/stage-prebuild.mjs` is the
145
+ * only thing that creates it, and `scripts/audit-runtimes.mjs --check` rejects any
146
+ * other spelling, so the three cannot drift.
147
+ *
148
+ * Two probes sit BEHIND it, both backward compatibility with tarballs already on npm
149
+ * rather than a second convention:
150
+ *
151
+ * 1. **The package's own declared spelling.** A pre-rename tarball declares and
152
+ * ships `linux-x86_64`, so probing its declaration first loads it without
153
+ * guessing. This is also what makes the rename work in the OTHER direction (an
154
+ * older CLI, a newer package), since `canonicalPlatformToken` folds both
155
+ * spellings onto one form at both ends.
156
+ * 2. **The legacy uname spelling.** Only reachable for a package that ships a
157
+ * prebuild dir and declares NO `gjsify.platforms` — a tarball predating the
158
+ * OS-axis audit, or a third-party package. The audit makes that state impossible
159
+ * inside this repo.
160
+ *
161
+ * Both are array entries matched against an already-read directory listing, so
162
+ * tolerance on the READ side costs no I/O while the single spelling stays enforced
163
+ * everywhere a name is WRITTEN.
164
+ *
165
+ * The LIBC AXIS rides in front of all of it (see {@link hostPlatformTokens}). `libc` is
166
+ * a PARAMETER rather than a `process` read for the same reason `platform` and `arch`
167
+ * are: it is the only way the musl branch is exercised at all, since CI and every
168
+ * developer machine here run glibc.
169
+ *
204
170
  * @param declaredPlatforms `package.json#gjsify.platforms`, when present.
205
- * @param libc host C library; `undefined`/`null` = behave as before the axis
206
- * existed.
207
171
  */
208
172
  export function prebuildDirCandidates(platform, arch, declaredPlatforms, libc) {
209
173
  const hostTokens = hostPlatformTokens(platform, arch, libc);
@@ -213,10 +177,8 @@ export function prebuildDirCandidates(platform, arch, declaredPlatforms, libc) {
213
177
  if (!out.includes(name))
214
178
  out.push(name);
215
179
  };
216
- // 1. The package's own declared spelling for each host token, most-specific
217
- // token first. Keyed off the SAME host-token list as step 2, so a musl
218
- // host that finds `linux-x64-musl` in `gjsify.platforms` prefers it over
219
- // a declared `linux-x64` — the declaration probe must not be able to
180
+ // 1. The package's own declared spelling per host token, most-specific first. Keyed
181
+ // off the SAME host-token list as step 2, so the declaration probe cannot
220
182
  // reorder the libc preference.
221
183
  for (const token of hostTokens) {
222
184
  for (const declared of declaredPlatforms ?? []) {
@@ -605,12 +567,12 @@ const DYLD_FALLBACK_VAR = 'DYLD_FALLBACK_LIBRARY_PATH';
605
567
  * exactly the "nothing else found it" case. Consequence, and it is the
606
568
  * intended precedence: gjsify's own prebuilds keep priority through the
607
569
  * override variable, the system stack fills in behind it.
608
- * 2. **Our dirs go FIRST, an inherited fallback is kept BEHIND them, and
609
- * `/usr/lib` is appended when there is none.** Setting the variable
610
- * REPLACES dyld's own default fallback list, so writing only our dirs would
611
- * silently remove `/usr/lib` from the search — a host that exported the
612
- * variable itself already carries whatever tail it wants. Same composition
613
- * as node-gi's re-exec.
570
+ * 2. **Our dirs go FIRST, an inherited fallback behind them, and dyld's own
571
+ * default list behind that** — `composeDyldFallback()`, the same
572
+ * composition node-gi's re-exec uses. Setting the variable REPLACES that
573
+ * default list rather than extending it, so any composition that does not
574
+ * carry it hands the child a SMALLER search path than it would have had
575
+ * with the variable unset.
614
576
  *
615
577
  * Emitted even when `packages` is empty: the gap is in the host's loader, not in
616
578
  * a gjsify prebuild, so a plain `gjsify run script.gjs.js` that touches GTK needs
@@ -641,19 +603,7 @@ export function buildNativeEnv(packages, opts = {}) {
641
603
  // it, so a Mac with no GI stack installed is left byte-unchanged.
642
604
  const systemDirs = (opts.systemGiDirs ?? systemGiLibraryDirs)({ platform, env });
643
605
  if (systemDirs.length > 0) {
644
- const inherited = splitSearchPath(env[DYLD_FALLBACK_VAR]);
645
- // Deduplicated, and not for tidiness: `systemGiLibraryDirs()` accepts a
646
- // libdir stated outright through `GI_TYPELIB_PATH` on directory
647
- // existence alone, so a host that names `/usr/lib/girepository-1.0`
648
- // there puts `/usr/lib` in `systemDirs` — and the `/usr/lib` tail below
649
- // then repeated it. A search path that lists the same directory twice
650
- // asks the loader to stat it twice for every miss, and it makes the
651
- // value the `$ …` echo prints read as though the CLI had composed
652
- // something it did not. First occurrence wins in every loader, so
653
- // dropping later repeats is behaviour-preserving.
654
- out[DYLD_FALLBACK_VAR] = [
655
- ...new Set([...systemDirs, ...(inherited.length > 0 ? inherited : ['/usr/lib'])]),
656
- ].join(':');
606
+ out[DYLD_FALLBACK_VAR] = composeDyldFallback(systemDirs, env);
657
607
  }
658
608
  return out;
659
609
  }
@@ -1,24 +1,18 @@
1
1
  /**
2
2
  * Which kind of directory link this platform uses.
3
3
  *
4
- * Spelled with a NAME for the POSIX case rather than `undefined`, so both
5
- * branches can be requested explicitly. `dirLinkTarget` takes this as a
6
- * defaulted parameter, and a defaulted parameter cannot receive `undefined`:
7
- * passing it IS how you ask for the default. So while POSIX was spelled
8
- * `undefined`, `dirLinkTarget(link, target, undefined)` meant "use the host's
9
- * kind" — the POSIX branch was unexercisable on Windows, and the win32 branch
10
- * unexercisable anywhere else. The suite read as if it covered both and did
11
- * not, which is precisely the shape the header above blames for the `'dir'`
12
- * bug reaching a release.
4
+ * The POSIX case is spelled with a NAME rather than `undefined` so both branches
5
+ * stay explicitly requestable: `dirLinkTarget` takes this as a DEFAULTED
6
+ * parameter, and a defaulted parameter cannot receive `undefined` — passing it IS
7
+ * how you ask for the host's kind. Spelled `undefined`, each branch was
8
+ * unexercisable off its own platform while the suite read as if it covered both.
13
9
  */
14
10
  export type DirLinkKind = 'junction' | 'symlink';
15
11
  /** The link kind directory links use on this platform. */
16
12
  export declare const DIR_LINK_KIND: DirLinkKind;
17
13
  /**
18
- * The link type a directory link must pass to `fs.symlink`.
19
- *
20
- * `undefined` on POSIX is deliberate: Node's default is correct there, and
21
- * passing a type would be noise. Derived from {@link DIR_LINK_KIND} so the
14
+ * The link type a directory link must pass to `fs.symlink` — `undefined` on POSIX,
15
+ * where Node's default is correct. Derived from {@link DIR_LINK_KIND} so the
22
16
  * platform decision has exactly one source.
23
17
  */
24
18
  export declare const DIR_LINK_TYPE: 'junction' | undefined;
@@ -31,34 +25,26 @@ export declare const dirLinksAreJunctions: () => boolean;
31
25
  * on POSIX so the tree stays movable (gotcha 2).
32
26
  *
33
27
  * `linkKind` is a PARAMETER, defaulting to the host's, for the same reason
34
- * `detect-native-packages.ts` takes `platform`/`arch`: the Windows branch is
35
- * then unit-testable from a Linux host, which is the only place anyone runs the
36
- * tests. A branch nobody can exercise is how the `'dir'` bug survived to a
37
- * release — and see {@link DirLinkKind} for why the POSIX side had to be named
38
- * rather than spelled `undefined` before that promise actually held.
28
+ * `detect-native-packages.ts` takes `platform`/`arch`: the Windows branch is then
29
+ * unit-testable from a Linux host, the only place anyone runs the tests.
39
30
  *
40
- * BOTH paths must be in the SAME canonical space — either both canonical or
41
- * both spelled through the same symlinks. The POSIX target is relative to the
42
- * link's own directory, but the kernel resolves it from the link's REAL
43
- * directory, so mixing the two spaces yields a link that points nowhere. macOS
44
- * is where this actually happens: `os.tmpdir()` is `/var/folders/…` and `/var`
45
- * is a symlink to `/private/var`, so a `linkPath` built from `tmpdir()` and an
46
- * `absTarget` that has been through `realpathSync` disagree, and the relative
47
- * path between them walks out of the real tree. `realpathSync` whichever side
48
- * is not already canonical before calling this.
31
+ * BOTH paths must be in the SAME canonical space. The POSIX target is relative to
32
+ * the link's own directory, but the kernel resolves it from the link's REAL
33
+ * directory, so mixing the two yields a link pointing nowhere. macOS is where this
34
+ * bites: `os.tmpdir()` is `/var/folders/…` and `/var` is a symlink to
35
+ * `/private/var`, so a `linkPath` from `tmpdir()` and an `absTarget` that went
36
+ * through `realpathSync` disagree and the relative path between them walks out of
37
+ * the real tree. `realpathSync` whichever side is not already canonical.
49
38
  *
50
- * @param linkPath where the link itself will live
51
39
  * @param absTarget the directory it should point at, as an ABSOLUTE path
52
40
  * @param linkKind override the host's link kind (tests only)
53
41
  */
54
42
  export declare function dirLinkTarget(linkPath: string, absTarget: string, linkKind?: DirLinkKind): string;
55
43
  /**
56
44
  * Link `linkPath` → `absTarget`, choosing symlink-vs-junction and the target
57
- * spelling for the host.
58
- *
59
- * `absTarget` must be absolute; passing a relative path is the mistake gotcha 1
60
- * describes, and it would only misbehave on Windows, so it is rejected here on
61
- * every platform rather than left to surface on the one host nobody tests on.
45
+ * spelling for the host. A relative `absTarget` is gotcha 1 and would only misbehave
46
+ * on Windows, so it is rejected on every platform rather than left to surface on the
47
+ * one host nobody tests on.
62
48
  */
63
49
  export declare function linkDirSync(linkPath: string, absTarget: string): void;
64
50
  /** Promise-returning twin of {@link linkDirSync}. */
@@ -66,21 +52,16 @@ export declare function linkDir(linkPath: string, absTarget: string): Promise<vo
66
52
  /**
67
53
  * Recreate an EXISTING link at a new path, preserving what it points at.
68
54
  *
69
- * A different job from {@link linkDirSync}, and it cannot reuse it: the source
70
- * link's target is whatever it already is — frequently relative, deliberately so
71
- * — while a junction demands an absolute one. Copying the target verbatim is
72
- * therefore right on POSIX and wrong on Windows the moment the target is a
73
- * directory.
74
- *
75
- * So the target's KIND decides: a link to a directory becomes a junction with
76
- * the target resolved against the source link's own directory (the resolution
77
- * Node will not do for you — see gotcha 1 above); anything else stays a plain
78
- * file symlink, which Windows allows unprivileged.
79
- *
80
- * A dangling source link keeps the verbatim target: it points nowhere either
81
- * way, and guessing a kind for it would turn a faithful copy into a fabrication.
55
+ * Cannot reuse {@link linkDirSync}: the source link's target is whatever it
56
+ * already is — frequently relative, deliberately so — while a junction demands an
57
+ * absolute one, so copying verbatim is right on POSIX and wrong on Windows the
58
+ * moment the target is a directory. The target's KIND therefore decides: a link to
59
+ * a directory becomes a junction with the target resolved against the source
60
+ * link's own directory (gotcha 1); anything else stays a plain file symlink, which
61
+ * Windows allows unprivileged. A dangling source link keeps the verbatim target —
62
+ * guessing a kind for it would turn a faithful copy into a fabrication.
82
63
  *
83
- * Primitive fs ops only — this runs under the GJS facade as well as Node, which
84
- * is why there is no `fs.cp`/`opendir` here.
64
+ * Primitive fs ops only: this runs under the GJS facade as well as Node, hence no
65
+ * `fs.cp`/`opendir`.
85
66
  */
86
67
  export declare function replicateLinkSync(srcLink: string, dstLink: string): void;