@gjsify/cli 0.44.0 → 0.46.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 (81) hide show
  1. package/dist/cli.gjs.mjs +202 -167
  2. package/lib/commands/gettext.d.ts +2 -1
  3. package/lib/commands/gettext.js +105 -47
  4. package/lib/commands/onboard.d.ts +7 -0
  5. package/lib/commands/onboard.js +349 -96
  6. package/lib/commands/ship.d.ts +3 -0
  7. package/lib/commands/ship.js +850 -58
  8. package/lib/commands/trust.d.ts +6 -0
  9. package/lib/commands/trust.js +1 -1
  10. package/lib/commands/upgrade.d.ts +23 -0
  11. package/lib/commands/upgrade.js +95 -12
  12. package/lib/templates/install.mjs.tmpl +2 -2
  13. package/lib/types/config-data.d.ts +127 -1
  14. package/lib/utils/gi-search-path.js +11 -3
  15. package/lib/utils/msgfmt-merge.d.ts +35 -0
  16. package/lib/utils/msgfmt-merge.js +102 -0
  17. package/lib/utils/npm-otp.d.ts +27 -2
  18. package/lib/utils/npm-otp.js +82 -7
  19. package/lib/utils/onboard-discovery.d.ts +68 -0
  20. package/lib/utils/onboard-discovery.js +178 -0
  21. package/lib/utils/onboard-probe.d.ts +118 -1
  22. package/lib/utils/onboard-probe.js +153 -4
  23. package/lib/utils/prompt-output.d.ts +12 -0
  24. package/lib/utils/prompt-output.js +54 -0
  25. package/lib/utils/prompt.js +22 -2
  26. package/lib/utils/publishable-packages.d.ts +1 -1
  27. package/lib/utils/publishable-packages.js +1 -0
  28. package/lib/utils/resolve-npm-package.d.ts +56 -0
  29. package/lib/utils/resolve-npm-package.js +69 -0
  30. package/lib/utils/ship/app-runtime.d.ts +214 -0
  31. package/lib/utils/ship/app-runtime.js +429 -0
  32. package/lib/utils/ship/depends.d.ts +50 -0
  33. package/lib/utils/ship/depends.js +117 -3
  34. package/lib/utils/ship/discover.js +103 -1
  35. package/lib/utils/ship/dmg.d.ts +71 -0
  36. package/lib/utils/ship/dmg.js +181 -0
  37. package/lib/utils/ship/flatpak.js +1 -1
  38. package/lib/utils/ship/formats.d.ts +108 -7
  39. package/lib/utils/ship/formats.js +778 -15
  40. package/lib/utils/ship/launcher.d.ts +13 -3
  41. package/lib/utils/ship/launcher.js +419 -17
  42. package/lib/utils/ship/layout.d.ts +330 -0
  43. package/lib/utils/ship/layout.js +504 -0
  44. package/lib/utils/ship/localize-metadata.d.ts +22 -0
  45. package/lib/utils/ship/localize-metadata.js +126 -0
  46. package/lib/utils/ship/mime.js +1 -9
  47. package/lib/utils/ship/msi.d.ts +126 -0
  48. package/lib/utils/ship/msi.js +434 -0
  49. package/lib/utils/ship/node-runtime.d.ts +71 -0
  50. package/lib/utils/ship/node-runtime.js +122 -0
  51. package/lib/utils/ship/payload.d.ts +156 -5
  52. package/lib/utils/ship/payload.js +599 -14
  53. package/lib/utils/ship/pe-launcher.d.ts +29 -0
  54. package/lib/utils/ship/pe-launcher.js +663 -0
  55. package/lib/utils/ship/plan.d.ts +21 -0
  56. package/lib/utils/ship/plan.js +55 -7
  57. package/lib/utils/ship/plist.d.ts +98 -0
  58. package/lib/utils/ship/plist.js +256 -0
  59. package/lib/utils/ship/rpm.js +16 -3
  60. package/lib/utils/ship/schemas.d.ts +61 -0
  61. package/lib/utils/ship/schemas.js +118 -0
  62. package/lib/utils/ship/scripts.js +7 -3
  63. package/lib/utils/ship/settings.d.ts +91 -2
  64. package/lib/utils/ship/settings.js +93 -1
  65. package/lib/utils/ship/share-dirs.d.ts +53 -0
  66. package/lib/utils/ship/share-dirs.js +76 -0
  67. package/lib/utils/ship/signing.d.ts +311 -0
  68. package/lib/utils/ship/signing.js +506 -0
  69. package/lib/utils/ship/stage-manifest.d.ts +40 -12
  70. package/lib/utils/ship/stage-manifest.js +99 -13
  71. package/lib/utils/ship/stage-writer.d.ts +48 -0
  72. package/lib/utils/ship/stage-writer.js +91 -2
  73. package/lib/utils/ship/types.d.ts +191 -14
  74. package/lib/utils/ship/xml.d.ts +7 -0
  75. package/lib/utils/ship/xml.js +25 -0
  76. package/lib/utils/ship/zip.d.ts +69 -0
  77. package/lib/utils/ship/zip.js +245 -0
  78. package/lib/utils/system-gi.d.ts +20 -5
  79. package/lib/utils/system-gi.js +36 -13
  80. package/lib/utils/trust-registry.js +5 -0
  81. package/package.json +19 -19
@@ -0,0 +1,330 @@
1
+ import type { HostOs, StagedFile } from './types.js';
2
+ /**
3
+ * The positional `gjsify ship <os>` takes, in ADR 0024 § A2's spelling.
4
+ *
5
+ * NOT the same vocabulary as {@link HostOs}, and the difference is deliberate
6
+ * rather than sloppy: § A2 writes `gjsify ship windows --stage`, while the
7
+ * repo-wide target spelling is `${process.platform}-${process.arch}` and
8
+ * therefore `win32-x64` (root AGENTS.md § Runtime & platform model). Both are
9
+ * accepted on the command line and exactly one is ever RECORDED — the stage
10
+ * manifest's `target.os` is a `HostOs`, so `--expect-target win32-x64` keeps
11
+ * meaning what it meant.
12
+ */
13
+ export type LayoutName = 'linux' | 'darwin' | 'windows';
14
+ /** The layout-affecting half of `ShipSettings` — a display name and a binary name, nothing else. */
15
+ export interface LayoutIdentity {
16
+ /** Package name: the launcher's own filename. */
17
+ binaryName: string;
18
+ /** Human-readable display name — the `<App>.app` directory a user sees in Finder. */
19
+ name: string;
20
+ }
21
+ /**
22
+ * What a layout needs to render the metadata it OWNS.
23
+ *
24
+ * `LayoutIdentity` plus the three fields an `Info.plist` names the app by. It
25
+ * EXTENDS the identity rather than sitting beside it so `placeStage` keeps taking
26
+ * one argument and `ShipSettings` satisfies it structurally — no call site has to
27
+ * assemble a second object, and adding a field here is a compile error at exactly
28
+ * the rows that would have to answer for it.
29
+ *
30
+ * Everything here comes from the consumer's own `gjsify.ship` block. Nothing in
31
+ * this file is derived from gjsify's tree and nothing has a gjsify-specific
32
+ * default, which is the same line {@link LayoutDirs} draws for directories.
33
+ */
34
+ export interface LayoutMetadataInput extends LayoutIdentity {
35
+ /** Reverse-DNS application id — `CFBundleIdentifier`, and what the data tree is keyed on. */
36
+ appId: string;
37
+ /** Upstream version — `CFBundleShortVersionString`. */
38
+ version: string;
39
+ /** Packaging revision within one upstream version — the second half of `CFBundleVersion`. */
40
+ release: string;
41
+ /**
42
+ * `process.arch` the payload is LABELLED for — what `--arch` names.
43
+ *
44
+ * Here because a layout can own a PROGRAM and not only a manifest, and a
45
+ * program has an architecture: the windows row emits an x86-64 GUI launcher,
46
+ * and staging it into a tree labelled `arm64` would make
47
+ * `assertPayloadMatchesArch` refuse a stage over a file the author never
48
+ * named. `Layout.arches` already says the windows layout has no other
49
+ * architecture; this is the same fact where the file is written.
50
+ */
51
+ arch: string;
52
+ }
53
+ /** The four places a payload file can belong, as stage-relative directories. */
54
+ export interface LayoutDirs {
55
+ /** Where the launcher lives. */
56
+ launcher: string;
57
+ /** Where the app's own bundle tree lives. */
58
+ bundle: string;
59
+ /** Where the typelibs and shared libraries the app CARRIES live. */
60
+ native: string;
61
+ /** Where the XDG-shaped data tree lives (icons, schemas, metainfo, locale). */
62
+ data: string;
63
+ /**
64
+ * Where a prefix-relative path that sorts into none of the above goes.
65
+ *
66
+ * Reachable only through `gjsify.ship.extraFiles`, whose destinations are
67
+ * documented as prefix-relative and may name anything. On macOS that has to
68
+ * land inside `Contents/`, or the file sits beside it where nothing in the
69
+ * bundle can address it and `codesign` later refuses the bundle.
70
+ */
71
+ other: string;
72
+ }
73
+ /**
74
+ * One operating system's layout.
75
+ *
76
+ * Data, not code: adding a fourth OS is a row here plus a launcher form, the same
77
+ * way adding a format is a row in `FORMATS`. The one function is {@link place},
78
+ * and it is shared by all three.
79
+ */
80
+ export interface Layout {
81
+ /** The positional that selects this layout. */
82
+ name: LayoutName;
83
+ /** The `process.platform` token recorded in the stage manifest's `target.os`. */
84
+ os: HostOs;
85
+ /**
86
+ * The runtime ADR 0024 § 4 DERIVES for a shipped artifact on this OS.
87
+ *
88
+ * Recorded, not obeyed: it is what #1354 M0's platform packages implement,
89
+ * and it is NOT what the staged launcher execs today. Linux takes GJS from
90
+ * the distribution and the emitted `Depends: gjs` backs it; both other OSes
91
+ * take Node, because Node + `@gjsify/node-gi` + `@gjsify/gtk-runtime-<os>-<arch>`
92
+ * is the only combination either OS's CI proves.
93
+ *
94
+ * Keeping it as DATA rather than as a launcher decision is the whole of what
95
+ * M1 may honestly claim. A layout with no packer produces no installable
96
+ * artifact, so writing `exec node` into a stage assembled from a `--app gjs`
97
+ * bundle would put a runtime that cannot read the payload in front of it —
98
+ * the exact defect `resolveShipApp`'s refusal exists to prevent, produced by the
99
+ * code meant to prevent it. Measured before this was data: `gjsify ship
100
+ * darwin --stage` on a project with no `gjsify.app` key exited 0 and staged
101
+ * `exec node "$contents/Resources/lib/gjs.js"` in front of a bundle whose
102
+ * first line is `import Gtk from 'gi://Gtk?version=4.0'`.
103
+ */
104
+ shippedRuntime: 'gjs' | 'node';
105
+ /**
106
+ * Why the staged launcher cannot name {@link shippedRuntime} yet, or absent
107
+ * when it already does.
108
+ *
109
+ * PRINTED at stage time rather than left as a comment: a tree whose launcher
110
+ * execs an interpreter the target OS may not have is a fact its author has to
111
+ * know, and it is the same fact that retires this field when M0 lands.
112
+ */
113
+ runtimeGap?: string;
114
+ /**
115
+ * Why a face in the payload's `share/fonts` may not be REACHED on this OS, or
116
+ * absent where nothing stands in its way.
117
+ *
118
+ * Optional, mirroring {@link runtimeGap} rather than {@link metadata}'s
119
+ * required-and-empty shape, because it asks the same kind of question: is there
120
+ * something true about this OS that the staged tree cannot show? Linux answers
121
+ * by having none.
122
+ *
123
+ * THE GAP IS NOT THE PATH (ADR 0038). The faces are STAGED identically on all
124
+ * three layouts and `planStage` emits one plan for them; what differs is what
125
+ * READS the directory. On Linux fontconfig does, expanding
126
+ * `<dir prefix="xdg">fonts</dir>` over the `XDG_DATA_DIRS` every launcher
127
+ * already exports — measured in eight independent fontconfigs — so that row has
128
+ * nothing to report. On the other two the reader is a property of
129
+ * `@gjsify/gtk-runtime-<target>` rather than of this payload, and it is not
130
+ * fontconfig in either: CoreText on macOS, DirectWrite on Windows, the latter
131
+ * measured on hardware (`LAYOUTS.windows.fontGap` carries the numbers).
132
+ * `gjsify ship` prints this only when the payload actually carries a face, so a
133
+ * project shipping none is told nothing.
134
+ */
135
+ fontGap?: string;
136
+ /** Suffix the launcher's filename carries, `''` where the OS needs none. */
137
+ launcherExt: string;
138
+ /**
139
+ * The directory inside the STAGE that this layout hangs under, or `''` when
140
+ * the stage root IS the layout root.
141
+ *
142
+ * The asymmetry is the OS's, not ours. A `<App>.app` is the artifact — it is
143
+ * dragged to `/Applications` as one unit, so the stage has to carry the
144
+ * directory itself. A Windows program directory is NOT: the installer chooses
145
+ * `C:\Program Files\<Publisher>\<App>` and lays the stage's contents into it,
146
+ * so a wrapper directory here would become a doubled path there.
147
+ */
148
+ root: (identity: LayoutIdentity) => string;
149
+ /** The four destinations, resolved against one app's names. */
150
+ dirs: (identity: LayoutIdentity) => LayoutDirs;
151
+ /**
152
+ * Files this layout OWNS, already in stage-relative form — `[]` where it owns none.
153
+ *
154
+ * The seam {@link place} structurally cannot provide, and that is why it is a
155
+ * second function rather than another prefix rule. `Contents/Info.plist` has no
156
+ * prefix-relative counterpart to be mapped FROM: `planStage` emits one plan in
157
+ * the Linux/XDG shape, everything unmatched falls to `dirs.other`, and
158
+ * `assertInsidePrefix` forbids a plan entry from escaping upward — so no
159
+ * `gjsify.ship.extraFiles` value and no planner rule can put a file at the
160
+ * bundle root. The layout has to add it.
161
+ *
162
+ * REQUIRED on every row, not optional. An optional field is one a fourth OS can
163
+ * forget; a required one that returns `[]` is a row that has ANSWERED "this
164
+ * layout owns nothing", which is a different statement and the one worth being
165
+ * able to read. Linux and Windows both answer that today — a program directory
166
+ * and a `/usr` prefix have no manifest of their own — so the whole difference
167
+ * between "no metadata yet" and "no metadata ever" stays visible.
168
+ *
169
+ * The result is placed by `placeStage`, which applies the SAME uniqueness check
170
+ * it applies to mapped files: an `extraFiles` destination that lands on
171
+ * `Contents/Info.plist` is refused rather than silently replacing it.
172
+ *
173
+ * IT TAKES THE PAYLOAD, in the prefix-relative shape, because a manifest is a
174
+ * statement ABOUT a tree and one key already has to be (ADR 0038):
175
+ * `ATSApplicationFontsPath` names the directory the bundle's faces are in, and
176
+ * emitting it over a bundle that carries none would point macOS at a path that
177
+ * is not there. That is also the seam `CFBundleDocumentTypes` will need, which
178
+ * is ADR 0024 stages 4 and 5 — so the parameter is the payload rather than a
179
+ * `hasFonts` flag: the next key to need it will need a different question
180
+ * answered about the same tree.
181
+ */
182
+ metadata: (input: LayoutMetadataInput, payload: readonly StagedFile[]) => StagedFile[];
183
+ /**
184
+ * The `process.arch` values this layout can be assembled for, or `null` when
185
+ * it imposes no limit of its own.
186
+ *
187
+ * REQUIRED on every row, like {@link metadata}, and for the same reason: `null`
188
+ * is an ANSWER ("this layout constrains nothing"), while an optional field is
189
+ * one a fourth OS forgets and then silently inherits Linux's answer. Linux is
190
+ * the `null` row — a `.deb` exists for every architecture Debian has, and
191
+ * `DEBIAN_ARCH`/`RPM_ARCH` in `formats.ts` already refuse the ones this project
192
+ * has no name for.
193
+ *
194
+ * WHY IT IS ON THE LAYOUT AND NOT ONLY ON THE FORMAT, since
195
+ * {@link assertLayoutSupportsArch} runs in `packOne` — the same phase
196
+ * `FormatDescriptor.archName` does, and this field buys neither an earlier
197
+ * refusal nor a different one. It buys the two things a table lookup cannot:
198
+ *
199
+ * * a REASON. `archName` answers "no Windows architecture is known for
200
+ * `process.arch` \"arm64\"", which reads as a gap in our table. The truth is
201
+ * that gvsbuild publishes no arm64 GTK, the blocker is upstream, and #1117
202
+ * is where it is tracked — and the difference between "unsupported" and
203
+ * "here is what would have to change" is the whole value of the message.
204
+ * * a STAGE-TIME warning at the flag that caused it (`commands/ship.ts`), for
205
+ * a phase that deliberately does not refuse — `tests/e2e/ship-layout`
206
+ * assembles all three layouts from ONE payload on purpose, and that
207
+ * payload's native file has an architecture.
208
+ *
209
+ * The constraint is also a fact about the layout's RUNTIME story — which GTK
210
+ * closure and which interpreter exist for it — rather than about any one
211
+ * container, which is why the format rows read it back instead of restating it.
212
+ */
213
+ arches: LayoutArches | null;
214
+ }
215
+ /** The architectures one layout is assemblable for, and why the others are not. */
216
+ export interface LayoutArches {
217
+ /** `process.arch` spellings — the same vocabulary `--arch` takes. */
218
+ only: readonly string[];
219
+ /** What a reader must know to act on the refusal: who blocks it, and where it is tracked. */
220
+ why: string;
221
+ }
222
+ export declare const LAYOUTS: Record<LayoutName, Layout>;
223
+ /** Every layout name, derived from the table so a new row cannot be missed. */
224
+ export declare const LAYOUT_NAMES: LayoutName[];
225
+ /** Stage-relative path of the launcher this layout puts in front of the app. */
226
+ export declare function launcherPath(layout: Layout, identity: LayoutIdentity): string;
227
+ /**
228
+ * Stage-relative path of the windows layout's GUI launcher.
229
+ *
230
+ * IT MUST BE `<launcherPath minus `.cmd`>.exe` AND THE STUB DEPENDS ON IT:
231
+ * `buildGuiLauncher` finds its `.cmd` by overwriting the last three characters of
232
+ * its own module filename, which is a three-character store instead of a path
233
+ * search only because the two names differ in exactly those bytes. Deriving both
234
+ * from `LAYOUTS.windows.launcherExt` here is what keeps that true — renaming the
235
+ * batch launcher to `.bat` would otherwise leave a stub looking for a file that
236
+ * is not there, at exit 0, with a window that never appears.
237
+ */
238
+ export declare function windowsGuiLauncherPath(identity: LayoutIdentity): string;
239
+ /**
240
+ * What the windows GUI launcher calls the file it writes under `%TEMP%`.
241
+ *
242
+ * Named after the binary rather than the display name: `%TEMP%` is shared by
243
+ * every application on the machine, and `binaryName` is the one identifier this
244
+ * project already refuses to let collide.
245
+ */
246
+ export declare function windowsLaunchLogLeaf(identity: LayoutIdentity): string;
247
+ /**
248
+ * The layout for one `process.platform` token, refusing every alias.
249
+ *
250
+ * STRICT, and that is the difference from {@link resolveLayout}, which is for a
251
+ * word a human typed. This one is for the stage manifest's `target.os` — a
252
+ * cross-host WIRE FORMAT with exactly one legal spelling per OS. Accepting
253
+ * `"windows"` there and silently comparing it as `win32` would mean
254
+ * `--expect-target win32-x64` no longer compares against the file's content:
255
+ * two manifests with different bytes in that field would both match, and the
256
+ * flag's whole job is to catch a job that downloaded the wrong artifact.
257
+ */
258
+ export declare function layoutForOs(os: string): Layout;
259
+ /**
260
+ * Resolve the positional to a layout.
261
+ *
262
+ * Accepts BOTH vocabularies (`windows` and `win32`) for the reason
263
+ * {@link LayoutName} records: the ADR writes one and `--expect-target` prints the
264
+ * other, and refusing whichever the user has in front of them is a papercut with
265
+ * no upside. The canonical answer is a single `Layout`, so nothing downstream has
266
+ * two spellings to reconcile. NOT for the stage manifest — see {@link layoutForOs}.
267
+ */
268
+ export declare function resolveLayout(raw: string): Layout;
269
+ /**
270
+ * The layout for the host this process is running on.
271
+ *
272
+ * What `gjsify ship` picks when the positional is absent, so a bare `gjsify ship`
273
+ * on Linux keeps assembling the Linux layout and packing `.deb` + `.rpm` exactly
274
+ * as before. A host whose platform has no layout is refused BY NAME rather than
275
+ * silently treated as Linux — a payload laid out for `/usr` and labelled for a
276
+ * FreeBSD would install and not run.
277
+ */
278
+ export declare function hostLayout(platform?: string): Layout;
279
+ /**
280
+ * Refuse an architecture this layout cannot be assembled for, naming the blocker.
281
+ *
282
+ * AT STAGE TIME, which is the whole point — see {@link Layout.arches}. The
283
+ * message carries the row's own `why` rather than a generic "unsupported", because
284
+ * the two refusals a reader can act on are different jobs: `x64` is what a Windows
285
+ * artifact is built for TODAY, and `win32-arm64` is blocked in a repository we do
286
+ * not own. Telling an author "unknown architecture" for the second one would send
287
+ * them to look for a flag.
288
+ *
289
+ * `process.arch` spelling on both sides, so the value quoted back is the one the
290
+ * user typed after `--arch`.
291
+ */
292
+ export declare function assertLayoutSupportsArch(layout: Layout, arch: string): void;
293
+ /**
294
+ * Map one PREFIX-RELATIVE planned path to where this layout keeps it.
295
+ *
296
+ * The plan is written once, in the Linux/XDG shape, and this is the only place
297
+ * that knows any other. Order matters: `lib/<name>/gi/` is tested before
298
+ * `lib/<name>/`, because on macOS those two go to different directories.
299
+ */
300
+ export declare function place(layout: Layout, identity: LayoutIdentity, rel: string): string;
301
+ /**
302
+ * Apply a layout to a whole planned payload, and add what the layout itself owns.
303
+ *
304
+ * THREE SOURCES, one destination namespace. The mapped files come from
305
+ * `planStage`'s prefix-relative plan; {@link Layout.metadata} adds the files that
306
+ * have no prefix-relative counterpart at all (`Contents/Info.plist`); `carried`
307
+ * adds the runtime the artifact ships inside itself (#1354 M2b — the interpreter,
308
+ * the relocated GTK closure, the node-gi addon). All three go through the same
309
+ * uniqueness check, which is what makes the other two safe: an `extraFiles`
310
+ * destination that maps onto a metadata path is refused by name instead of
311
+ * silently replacing the file that makes the bundle a bundle.
312
+ *
313
+ * `carried` is already stage-relative and deliberately NOT mapped. A relocated
314
+ * closure's every internal relation is relative — `@loader_path/<leaf>` install
315
+ * names, `@loader_path/../../..` in `loaders.cache`, `@loader_path/gtk/lib` on the
316
+ * addon — so the tree survives the copy only if it is copied as a tree. Routing it
317
+ * through the prefix-relative plan would put it through
318
+ * `gjsify.ship.bundledTypelibs`'s flattening (`plan.ts`: `posix.join(libDir, 'gi',
319
+ * basename(file))`), which destroys all three at once.
320
+ *
321
+ * The uniqueness check is not decoration for the mapped half either. `planStage`
322
+ * deduplicates on the PREFIX-RELATIVE path — that is how `gjsify.ship.extraFiles`
323
+ * is documented to override a default — but the MAP can bring two distinct
324
+ * prefix-relative paths together: on Windows the launcher becomes
325
+ * `<binaryName>.cmd` at the program root, which an `extraFiles` destination can
326
+ * name directly. Without this the second one silently wins and which one that is
327
+ * depends on plan order — the "installs cleanly, does nothing" class this command
328
+ * is built against.
329
+ */
330
+ export declare function placeStage(layout: Layout, identity: LayoutMetadataInput, files: readonly StagedFile[], carried?: readonly StagedFile[]): StagedFile[];