@gjsify/cli 0.39.0 → 0.40.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 (63) hide show
  1. package/dist/cli.gjs.mjs +197 -180
  2. package/lib/cli-app.js +11 -25
  3. package/lib/commands/flatpak/check.js +1 -1
  4. package/lib/commands/flatpak/ci.js +1 -1
  5. package/lib/commands/flatpak/diff.js +1 -1
  6. package/lib/commands/flatpak/init.js +1 -1
  7. package/lib/commands/flatpak/scaffold.d.ts +2 -4
  8. package/lib/commands/flatpak/scaffold.js +25 -298
  9. package/lib/commands/flatpak/sync-flathub.js +1 -1
  10. package/lib/commands/foreach.js +34 -20
  11. package/lib/commands/index.d.ts +1 -0
  12. package/lib/commands/index.js +1 -0
  13. package/lib/commands/ship.d.ts +11 -0
  14. package/lib/commands/ship.js +217 -0
  15. package/lib/config.d.ts +12 -0
  16. package/lib/config.js +15 -0
  17. package/lib/types/config-data.d.ts +136 -46
  18. package/lib/utils/app-metadata.d.ts +51 -0
  19. package/lib/utils/app-metadata.js +338 -0
  20. package/lib/utils/cli-runtime-closure.d.ts +23 -0
  21. package/lib/utils/cli-runtime-closure.js +274 -0
  22. package/lib/utils/publish-headers.d.ts +13 -0
  23. package/lib/utils/publish-headers.js +30 -4
  24. package/lib/utils/ship/ar.d.ts +21 -0
  25. package/lib/utils/ship/ar.js +57 -0
  26. package/lib/utils/ship/bytes.d.ts +4 -0
  27. package/lib/utils/ship/bytes.js +23 -0
  28. package/lib/utils/ship/cpio.d.ts +22 -0
  29. package/lib/utils/ship/cpio.js +93 -0
  30. package/lib/utils/ship/deb.d.ts +24 -0
  31. package/lib/utils/ship/deb.js +118 -0
  32. package/lib/utils/ship/depends.d.ts +62 -0
  33. package/lib/utils/ship/depends.js +202 -0
  34. package/lib/utils/ship/discover.d.ts +21 -0
  35. package/lib/utils/ship/discover.js +129 -0
  36. package/lib/utils/ship/formats.d.ts +5 -0
  37. package/lib/utils/ship/formats.js +83 -0
  38. package/lib/utils/ship/gi-namespaces.d.ts +7 -0
  39. package/lib/utils/ship/gi-namespaces.js +49 -0
  40. package/lib/utils/ship/gzip.d.ts +2 -0
  41. package/lib/utils/ship/gzip.js +21 -0
  42. package/lib/utils/ship/launcher.d.ts +7 -0
  43. package/lib/utils/ship/launcher.js +37 -0
  44. package/lib/utils/ship/payload.d.ts +30 -0
  45. package/lib/utils/ship/payload.js +59 -0
  46. package/lib/utils/ship/plan.d.ts +34 -0
  47. package/lib/utils/ship/plan.js +167 -0
  48. package/lib/utils/ship/rpm-header.d.ts +29 -0
  49. package/lib/utils/ship/rpm-header.js +170 -0
  50. package/lib/utils/ship/rpm.d.ts +12 -0
  51. package/lib/utils/ship/rpm.js +377 -0
  52. package/lib/utils/ship/scripts.d.ts +31 -0
  53. package/lib/utils/ship/scripts.js +76 -0
  54. package/lib/utils/ship/settings.d.ts +60 -0
  55. package/lib/utils/ship/settings.js +184 -0
  56. package/lib/utils/ship/stage-writer.d.ts +17 -0
  57. package/lib/utils/ship/stage-writer.js +49 -0
  58. package/lib/utils/ship/types.d.ts +114 -0
  59. package/lib/utils/ship/types.js +10 -0
  60. package/lib/utils/ship/version.d.ts +17 -0
  61. package/lib/utils/ship/version.js +55 -0
  62. package/package.json +19 -18
  63. /package/lib/templates/{flatpak → app}/desktop.tmpl +0 -0
@@ -0,0 +1,34 @@
1
+ import type { FormatDescriptor, ShipSettings, StagedFile } from './types.js';
2
+ /** Everything the planner needs that it cannot derive from the settings alone. */
3
+ export interface StageInputs {
4
+ /** Paths inside `settings.bundleDir`, relative and POSIX-separated. */
5
+ bundleFiles: readonly string[];
6
+ /** The rendered launcher script. */
7
+ launcher: string;
8
+ /** The rendered AppStream MetaInfo XML. */
9
+ metainfo: string;
10
+ /** The rendered desktop entry — required for `kind: 'app'`, unused for `'cli'`. */
11
+ desktopEntry?: string;
12
+ /** Contents of the project's licence file, when one was found. */
13
+ licenseText?: string;
14
+ }
15
+ /**
16
+ * Build the prefix-relative payload.
17
+ *
18
+ * The result is deduplicated by path (last write wins, so `extraFiles` can
19
+ * override anything the defaults staged) and sorted, which makes the tree —
20
+ * and therefore every artifact built from it — deterministic.
21
+ */
22
+ export declare function planStage(settings: ShipSettings, inputs: StageInputs): StagedFile[];
23
+ /**
24
+ * The per-format additions. Only the licence differs today, and it differs in
25
+ * BOTH place and shape: Debian policy wants a machine-readable `copyright` in
26
+ * `share/doc/<pkg>/`, RPM wants the licence text under `share/licenses/<pkg>/`.
27
+ */
28
+ export declare function planOverlay(settings: ShipSettings, format: FormatDescriptor, inputs: StageInputs): StagedFile[];
29
+ /**
30
+ * The hicolor subdirectory for an icon: `scalable` for SVG, otherwise the
31
+ * pixel size read from a `<n>x<n>` path component or a trailing number in the
32
+ * filename.
33
+ */
34
+ export declare function iconSizeDir(iconPath: string): string;
@@ -0,0 +1,167 @@
1
+ // Which file lands where — decided without touching the filesystem, so every
2
+ // case (including the ones that must be REFUSED) is unit-testable. Same split
3
+ // as `utils/copy-targets.ts`, and for the same reason: a stager that discovers
4
+ // its own inputs can only be tested by building a real project.
5
+ import { basename, extname, posix } from 'node:path';
6
+ /**
7
+ * Build the prefix-relative payload.
8
+ *
9
+ * The result is deduplicated by path (last write wins, so `extraFiles` can
10
+ * override anything the defaults staged) and sorted, which makes the tree —
11
+ * and therefore every artifact built from it — deterministic.
12
+ */
13
+ export function planStage(settings, inputs) {
14
+ const files = [];
15
+ const libDir = `lib/${settings.binaryName}`;
16
+ files.push({ path: `bin/${settings.binaryName}`, mode: 0o755, source: { kind: 'text', text: inputs.launcher } });
17
+ for (const rel of inputs.bundleFiles) {
18
+ files.push({
19
+ path: posix.join(libDir, rel),
20
+ mode: isExecutableAsset(rel) ? 0o755 : 0o644,
21
+ source: { kind: 'file', path: posix.join(settings.bundleDir, rel) },
22
+ });
23
+ }
24
+ files.push({
25
+ path: `share/metainfo/${settings.appId}.metainfo.xml`,
26
+ mode: 0o644,
27
+ source: { kind: 'text', text: inputs.metainfo },
28
+ });
29
+ if (settings.kind === 'app') {
30
+ if (inputs.desktopEntry === undefined) {
31
+ throw new Error('gjsify ship: internal error — a desktop entry is required for `kind: "app"`.');
32
+ }
33
+ files.push({
34
+ path: `share/applications/${settings.appId}.desktop`,
35
+ mode: 0o644,
36
+ source: { kind: 'text', text: inputs.desktopEntry },
37
+ });
38
+ for (const icon of planIcons(settings))
39
+ files.push(icon);
40
+ }
41
+ for (const schema of settings.schemaFiles) {
42
+ const name = basename(schema);
43
+ // Every installed schema shares ONE system directory
44
+ // (`/usr/share/glib-2.0/schemas`), so a generic name is a collision
45
+ // with whatever else the distro installed there — and the loser is
46
+ // decided by install order.
47
+ if (!name.startsWith(settings.appId)) {
48
+ throw new Error(`gjsify ship: the GSettings schema ${name} must be named \`${settings.appId}[.…].gschema.xml\`. ` +
49
+ 'Every installed schema shares one system directory, so the app id is what keeps it unique.');
50
+ }
51
+ files.push({
52
+ path: `share/glib-2.0/schemas/${name}`,
53
+ mode: 0o644,
54
+ source: { kind: 'file', path: schema },
55
+ });
56
+ }
57
+ for (const [dest, source] of Object.entries(settings.extraFiles)) {
58
+ files.push({ path: assertInsidePrefix(dest), mode: 0o644, source: { kind: 'file', path: source } });
59
+ }
60
+ return normalise(files);
61
+ }
62
+ /**
63
+ * The per-format additions. Only the licence differs today, and it differs in
64
+ * BOTH place and shape: Debian policy wants a machine-readable `copyright` in
65
+ * `share/doc/<pkg>/`, RPM wants the licence text under `share/licenses/<pkg>/`.
66
+ */
67
+ export function planOverlay(settings, format, inputs) {
68
+ if (inputs.licenseText === undefined)
69
+ return [];
70
+ const text = format.licenseKind === 'debian-copyright'
71
+ ? renderDebianCopyright(settings, inputs.licenseText)
72
+ : inputs.licenseText;
73
+ return [{ path: format.licenseDest(settings.binaryName), mode: 0o644, source: { kind: 'text', text } }];
74
+ }
75
+ /**
76
+ * Debian machine-readable copyright, format 1.0. The licence text is indented
77
+ * by one space and its blank lines become ` .` — a bare blank line ends the
78
+ * field, which silently truncates the licence to its first paragraph.
79
+ */
80
+ function renderDebianCopyright(settings, licenseText) {
81
+ const body = licenseText
82
+ .trimEnd()
83
+ .split('\n')
84
+ .map((line) => (line.trim() === '' ? ' .' : ` ${line}`))
85
+ .join('\n');
86
+ return [
87
+ 'Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/',
88
+ `Upstream-Name: ${settings.name}`,
89
+ ...(settings.homepage ? [`Source: ${settings.homepage}`] : []),
90
+ '',
91
+ 'Files: *',
92
+ `Copyright: ${settings.maintainer}`,
93
+ `License: ${settings.license}`,
94
+ body,
95
+ '',
96
+ ].join('\n');
97
+ }
98
+ /**
99
+ * Icons land in the hicolor theme, named after the app id — that name is what
100
+ * the desktop entry's `Icon=` and the MetaInfo `<id>` both point at, so a file
101
+ * keeping its source basename installs an icon nothing ever looks up.
102
+ */
103
+ function planIcons(settings) {
104
+ const out = [];
105
+ const seen = new Map();
106
+ for (const icon of settings.iconFiles) {
107
+ const ext = extname(icon);
108
+ const dir = iconSizeDir(icon);
109
+ const path = `share/icons/hicolor/${dir}/apps/${settings.appId}${ext}`;
110
+ const previous = seen.get(path);
111
+ if (previous !== undefined) {
112
+ throw new Error(`gjsify ship: ${icon} and ${previous} both install as ${path}. ` +
113
+ 'Icon size is read from the path (`.../128x128/...`) or the filename (`icon-128.png`); ' +
114
+ 'give them distinguishable sizes or point `gjsify.ship.icon` at a single file.');
115
+ }
116
+ seen.set(path, icon);
117
+ out.push({ path, mode: 0o644, source: { kind: 'file', path: icon } });
118
+ }
119
+ return out;
120
+ }
121
+ /**
122
+ * The hicolor subdirectory for an icon: `scalable` for SVG, otherwise the
123
+ * pixel size read from a `<n>x<n>` path component or a trailing number in the
124
+ * filename.
125
+ */
126
+ export function iconSizeDir(iconPath) {
127
+ if (extname(iconPath).toLowerCase() === '.svg')
128
+ return 'scalable';
129
+ const square = /(?:^|[\\/])(\d{1,4})x\1(?:[\\/]|$)/.exec(iconPath);
130
+ if (square)
131
+ return `${square[1]}x${square[1]}`;
132
+ const tokens = basename(iconPath, extname(iconPath)).split(/[-_.]/);
133
+ for (let i = tokens.length - 1; i >= 0; i--) {
134
+ const token = tokens[i];
135
+ if (token !== undefined && /^\d{1,4}$/.test(token))
136
+ return `${token}x${token}`;
137
+ }
138
+ throw new Error(`gjsify ship: cannot tell what size ${iconPath} is. ` +
139
+ 'Put it in a `<size>x<size>/` directory, end its name with the size (`icon-128.png`), or ship an SVG.');
140
+ }
141
+ /** A staged path must stay under the prefix — no absolute paths, no `..`. */
142
+ function assertInsidePrefix(dest) {
143
+ const normalised = posix.normalize(dest);
144
+ if (posix.isAbsolute(normalised) || normalised === '..' || normalised.startsWith('../')) {
145
+ throw new Error(`gjsify ship: \`gjsify.ship.extraFiles\` destination "${dest}" escapes the install prefix. ` +
146
+ 'Destinations are relative to the prefix, e.g. `share/applications/extra.desktop`.');
147
+ }
148
+ return normalised;
149
+ }
150
+ /**
151
+ * A native module inside the payload has to keep its executable bit.
152
+ *
153
+ * By NAME here, unlike `isArchIndependent`, which reads the file's magic: the
154
+ * planner is pure and has no bytes to look at. The two answer different
155
+ * questions — "should this be 0755" versus "is this package portable" — and
156
+ * getting this one wrong costs a mode bit, not an unusable package.
157
+ */
158
+ function isExecutableAsset(rel) {
159
+ return /\.(so|node|dylib)(\.\d+)*$/.test(rel);
160
+ }
161
+ /** Last write wins, then sort — deterministic output for a deterministic artifact. */
162
+ function normalise(files) {
163
+ const byPath = new Map();
164
+ for (const file of files)
165
+ byPath.set(file.path, file);
166
+ return [...byPath.values()].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
167
+ }
@@ -0,0 +1,29 @@
1
+ export declare const RPM_TAG_HEADERSIGNATURES = 62;
2
+ export declare const RPM_TAG_HEADERIMMUTABLE = 63;
3
+ export declare enum RpmType {
4
+ INT16 = 3,
5
+ INT32 = 4,
6
+ STRING = 6,
7
+ BIN = 7,
8
+ STRING_ARRAY = 8,
9
+ I18NSTRING = 9
10
+ }
11
+ export type RpmValue = string | string[] | number | number[] | Uint8Array;
12
+ export interface RpmEntry {
13
+ tag: number;
14
+ type: RpmType;
15
+ value: RpmValue;
16
+ }
17
+ /** Build a complete header blob, magic included. */
18
+ export declare function buildRpmHeader(entries: readonly RpmEntry[], regionTag: number): Uint8Array;
19
+ /**
20
+ * The 96-byte lead.
21
+ *
22
+ * Legacy: modern rpm validates the magic and the major version and takes
23
+ * everything else from the headers, which is why the name field is allowed to
24
+ * be truncated. It is written because the magic is how `file(1)`, and rpm's
25
+ * own first read, recognise the format at all.
26
+ */
27
+ export declare function buildRpmLead(nevr: string): Uint8Array;
28
+ /** Pad a signature header to the 8-byte boundary the main header starts on. */
29
+ export declare function padToEight(bytes: Uint8Array): Uint8Array;
@@ -0,0 +1,170 @@
1
+ // The RPM header structure — an index of typed entries over a shared store.
2
+ //
3
+ // Both headers in an RPM (the signature and the main one) are this same
4
+ // structure, which is why it is its own module: `rpm.ts` decides WHICH tags to
5
+ // write, this file decides how a header is spelled.
6
+ //
7
+ // The one genuinely surprising part is the REGION. `rpm` does not read the
8
+ // index as a flat list: the first index entry is a region trailer pointer with
9
+ // tag 62 (signatures) or 63 (immutable), whose store payload is a second,
10
+ // 16-byte index entry carrying a NEGATIVE offset — the byte distance back to
11
+ // the start of the index. Getting that offset wrong does not corrupt anything
12
+ // visibly; `rpm -qp` simply reports "region trailer" errors or silently sees a
13
+ // header with no tags in it.
14
+ const HEADER_MAGIC = Uint8Array.from([0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00]);
15
+ const INDEX_ENTRY_SIZE = 16;
16
+ /** The region trailer is one index entry, and rpm checks the count literally. */
17
+ const REGION_COUNT = 16;
18
+ export const RPM_TAG_HEADERSIGNATURES = 62;
19
+ export const RPM_TAG_HEADERIMMUTABLE = 63;
20
+ export var RpmType;
21
+ (function (RpmType) {
22
+ RpmType[RpmType["INT16"] = 3] = "INT16";
23
+ RpmType[RpmType["INT32"] = 4] = "INT32";
24
+ RpmType[RpmType["STRING"] = 6] = "STRING";
25
+ RpmType[RpmType["BIN"] = 7] = "BIN";
26
+ RpmType[RpmType["STRING_ARRAY"] = 8] = "STRING_ARRAY";
27
+ RpmType[RpmType["I18NSTRING"] = 9] = "I18NSTRING";
28
+ })(RpmType || (RpmType = {}));
29
+ /** Store alignment per type — rpm reads INT32s with an aligned load. */
30
+ const ALIGNMENT = {
31
+ [RpmType.INT16]: 2,
32
+ [RpmType.INT32]: 4,
33
+ [RpmType.STRING]: 1,
34
+ [RpmType.BIN]: 1,
35
+ [RpmType.STRING_ARRAY]: 1,
36
+ [RpmType.I18NSTRING]: 1,
37
+ };
38
+ /** Build a complete header blob, magic included. */
39
+ export function buildRpmHeader(entries, regionTag) {
40
+ // rpm expects the index sorted by tag; the store is laid out in the same
41
+ // order, which is not required but makes a hexdump readable next to a real
42
+ // package's.
43
+ const sorted = [...entries].sort((a, b) => a.tag - b.tag);
44
+ const index = [];
45
+ const chunks = [];
46
+ let storeSize = 0;
47
+ for (const entry of sorted) {
48
+ if (entry.tag < 100) {
49
+ throw new Error(`gjsify ship: rpm tag ${entry.tag} is below 100 and would be rejected by hdrchkTag.`);
50
+ }
51
+ const { data, count } = encodeValue(entry.type, entry.value, entry.tag);
52
+ const alignment = ALIGNMENT[entry.type];
53
+ const padding = (alignment - (storeSize % alignment)) % alignment;
54
+ if (padding > 0) {
55
+ chunks.push(new Uint8Array(padding));
56
+ storeSize += padding;
57
+ }
58
+ index.push({ tag: entry.tag, type: entry.type, offset: storeSize, count });
59
+ chunks.push(data);
60
+ storeSize += data.byteLength;
61
+ }
62
+ const trailerOffset = storeSize;
63
+ const indexCount = index.length + 1; // the region entry counts itself
64
+ chunks.push(indexEntry(regionTag, RpmType.BIN, -(INDEX_ENTRY_SIZE * indexCount), REGION_COUNT));
65
+ storeSize += INDEX_ENTRY_SIZE;
66
+ const header = new Uint8Array(HEADER_MAGIC.byteLength + 8 + indexCount * INDEX_ENTRY_SIZE + storeSize);
67
+ const view = new DataView(header.buffer);
68
+ header.set(HEADER_MAGIC, 0);
69
+ view.setUint32(8, indexCount);
70
+ view.setUint32(12, storeSize);
71
+ let offset = 16;
72
+ header.set(indexEntry(regionTag, RpmType.BIN, trailerOffset, REGION_COUNT), offset);
73
+ offset += INDEX_ENTRY_SIZE;
74
+ for (const item of index) {
75
+ header.set(indexEntry(item.tag, item.type, item.offset, item.count), offset);
76
+ offset += INDEX_ENTRY_SIZE;
77
+ }
78
+ for (const chunk of chunks) {
79
+ header.set(chunk, offset);
80
+ offset += chunk.byteLength;
81
+ }
82
+ return header;
83
+ }
84
+ function indexEntry(tag, type, offset, count) {
85
+ const bytes = new Uint8Array(INDEX_ENTRY_SIZE);
86
+ const view = new DataView(bytes.buffer);
87
+ view.setInt32(0, tag);
88
+ view.setInt32(4, type);
89
+ view.setInt32(8, offset);
90
+ view.setInt32(12, count);
91
+ return bytes;
92
+ }
93
+ function encodeValue(type, value, tag) {
94
+ const encoder = new TextEncoder();
95
+ switch (type) {
96
+ case RpmType.STRING: {
97
+ if (typeof value !== 'string')
98
+ throw typeMismatch(tag, 'a string');
99
+ return { data: encoder.encode(`${value}\0`), count: 1 };
100
+ }
101
+ case RpmType.STRING_ARRAY:
102
+ case RpmType.I18NSTRING: {
103
+ const list = typeof value === 'string' ? [value] : value;
104
+ if (!Array.isArray(list))
105
+ throw typeMismatch(tag, 'a string array');
106
+ // rpm rejects a zero count, so an empty array is a bug rather than
107
+ // "no value" — the caller must omit the tag instead.
108
+ if (list.length === 0)
109
+ throw new Error(`gjsify ship: rpm tag ${tag} has an empty array; omit it instead.`);
110
+ return { data: encoder.encode(list.map((item) => `${item}\0`).join('')), count: list.length };
111
+ }
112
+ case RpmType.BIN: {
113
+ if (!(value instanceof Uint8Array))
114
+ throw typeMismatch(tag, 'binary data');
115
+ return { data: value, count: value.byteLength };
116
+ }
117
+ case RpmType.INT16:
118
+ case RpmType.INT32: {
119
+ const list = typeof value === 'number' ? [value] : value;
120
+ if (!Array.isArray(list))
121
+ throw typeMismatch(tag, 'a number array');
122
+ if (list.length === 0)
123
+ throw new Error(`gjsify ship: rpm tag ${tag} has an empty array; omit it instead.`);
124
+ const width = type === RpmType.INT16 ? 2 : 4;
125
+ const data = new Uint8Array(list.length * width);
126
+ const view = new DataView(data.buffer);
127
+ list.forEach((item, i) => {
128
+ if (type === RpmType.INT16)
129
+ view.setUint16(i * width, item & 0xffff);
130
+ else
131
+ view.setUint32(i * width, item >>> 0);
132
+ });
133
+ return { data, count: list.length };
134
+ }
135
+ }
136
+ }
137
+ function typeMismatch(tag, expected) {
138
+ return new Error(`gjsify ship: internal error — rpm tag ${tag} expects ${expected}.`);
139
+ }
140
+ /**
141
+ * The 96-byte lead.
142
+ *
143
+ * Legacy: modern rpm validates the magic and the major version and takes
144
+ * everything else from the headers, which is why the name field is allowed to
145
+ * be truncated. It is written because the magic is how `file(1)`, and rpm's
146
+ * own first read, recognise the format at all.
147
+ */
148
+ export function buildRpmLead(nevr) {
149
+ const lead = new Uint8Array(96);
150
+ const view = new DataView(lead.buffer);
151
+ lead.set([0xed, 0xab, 0xee, 0xdb], 0);
152
+ lead[4] = 3; // major
153
+ lead[5] = 0; // minor
154
+ view.setInt16(6, 0); // type: binary package
155
+ view.setInt16(8, 0); // archnum — informational; the header's ARCH is authoritative
156
+ const name = new TextEncoder().encode(nevr).slice(0, 65);
157
+ lead.set(name, 10);
158
+ view.setInt16(76, 0); // osnum — same story as archnum
159
+ view.setInt16(78, 5); // signature type: header-style signatures
160
+ return lead;
161
+ }
162
+ /** Pad a signature header to the 8-byte boundary the main header starts on. */
163
+ export function padToEight(bytes) {
164
+ const padding = (8 - (bytes.byteLength % 8)) % 8;
165
+ if (padding === 0)
166
+ return bytes;
167
+ const out = new Uint8Array(bytes.byteLength + padding);
168
+ out.set(bytes, 0);
169
+ return out;
170
+ }
@@ -0,0 +1,12 @@
1
+ import type { PayloadEntry } from './payload.js';
2
+ import type { ShipSettings } from './types.js';
3
+ export interface RpmInputs {
4
+ settings: ShipSettings;
5
+ payload: readonly PayloadEntry[];
6
+ prefix: string;
7
+ depends: readonly string[];
8
+ /** RPM architecture (`x86_64`, `noarch`, …). */
9
+ archLabel: string;
10
+ mtime: number;
11
+ }
12
+ export declare function buildRpm(inputs: RpmInputs): Promise<Uint8Array>;