@push.rocks/smartlinuxrepo 0.0.0-stage → 1.0.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.
- package/.smartconfig.json +65 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/alpine/apkindex.d.ts +39 -0
- package/dist_ts/alpine/apkindex.js +211 -0
- package/dist_ts/alpine/pkginfo.d.ts +45 -0
- package/dist_ts/alpine/pkginfo.js +159 -0
- package/dist_ts/alpine/reader.d.ts +20 -0
- package/dist_ts/alpine/reader.js +89 -0
- package/dist_ts/arch/builder.d.ts +41 -0
- package/dist_ts/arch/builder.js +234 -0
- package/dist_ts/arch/reader.d.ts +16 -0
- package/dist_ts/arch/reader.js +271 -0
- package/dist_ts/arch/signer.d.ts +21 -0
- package/dist_ts/arch/signer.js +66 -0
- package/dist_ts/arch/vercmp.d.ts +12 -0
- package/dist_ts/arch/vercmp.js +170 -0
- package/dist_ts/debian/control.d.ts +22 -0
- package/dist_ts/debian/control.js +137 -0
- package/dist_ts/debian/gostring.d.ts +26 -0
- package/dist_ts/debian/gostring.js +145 -0
- package/dist_ts/debian/indexbuilder.d.ts +47 -0
- package/dist_ts/debian/indexbuilder.js +299 -0
- package/dist_ts/debian/mailaddress.d.ts +12 -0
- package/dist_ts/debian/mailaddress.js +736 -0
- package/dist_ts/debian/reader.d.ts +30 -0
- package/dist_ts/debian/reader.js +110 -0
- package/dist_ts/index.d.ts +23 -0
- package/dist_ts/index.js +18 -0
- package/dist_ts/plugins.d.ts +8 -0
- package/dist_ts/plugins.js +11 -0
- package/dist_ts/readers/ar.d.ts +31 -0
- package/dist_ts/readers/ar.js +87 -0
- package/dist_ts/readers/bytes.d.ts +58 -0
- package/dist_ts/readers/bytes.js +147 -0
- package/dist_ts/readers/bytesource.d.ts +40 -0
- package/dist_ts/readers/bytesource.js +154 -0
- package/dist_ts/readers/decompress.d.ts +43 -0
- package/dist_ts/readers/decompress.js +269 -0
- package/dist_ts/readers/gzipmember.d.ts +32 -0
- package/dist_ts/readers/gzipmember.js +176 -0
- package/dist_ts/readers/tar.d.ts +85 -0
- package/dist_ts/readers/tar.js +731 -0
- package/dist_ts/readers/text.d.ts +16 -0
- package/dist_ts/readers/text.js +96 -0
- package/dist_ts/rpm/builder.d.ts +28 -0
- package/dist_ts/rpm/builder.js +208 -0
- package/dist_ts/rpm/header.d.ts +138 -0
- package/dist_ts/rpm/header.js +546 -0
- package/dist_ts/rpm/reader.d.ts +20 -0
- package/dist_ts/rpm/reader.js +306 -0
- package/dist_ts/rpm/repoconfig.d.ts +15 -0
- package/dist_ts/rpm/repoconfig.js +38 -0
- package/dist_ts/rpm/repodata.d.ts +44 -0
- package/dist_ts/rpm/repodata.js +148 -0
- package/dist_ts/rpm/sign.d.ts +30 -0
- package/dist_ts/rpm/sign.js +183 -0
- package/dist_ts/rpm/updates.d.ts +38 -0
- package/dist_ts/rpm/updates.js +265 -0
- package/dist_ts/rpm/xml.d.ts +18 -0
- package/dist_ts/rpm/xml.js +90 -0
- package/dist_ts/shared/architecture.d.ts +8 -0
- package/dist_ts/shared/architecture.js +17 -0
- package/dist_ts/shared/errors.d.ts +38 -0
- package/dist_ts/shared/errors.js +22 -0
- package/dist_ts/shared/gopath.d.ts +13 -0
- package/dist_ts/shared/gopath.js +53 -0
- package/dist_ts/shared/gostrings.d.ts +14 -0
- package/dist_ts/shared/gostrings.js +29 -0
- package/dist_ts/shared/limits.d.ts +47 -0
- package/dist_ts/shared/limits.js +57 -0
- package/dist_ts/shared/types.d.ts +213 -0
- package/dist_ts/shared/types.js +13 -0
- package/dist_ts/shared/url.d.ts +18 -0
- package/dist_ts/shared/url.js +371 -0
- package/dist_ts/shared/validation.d.ts +21 -0
- package/dist_ts/shared/validation.js +58 -0
- package/dist_ts/signing/keys.d.ts +36 -0
- package/dist_ts/signing/keys.js +89 -0
- package/dist_ts/signing/openpgpframing.d.ts +33 -0
- package/dist_ts/signing/openpgpframing.js +93 -0
- package/dist_ts/signing/signers.d.ts +63 -0
- package/dist_ts/signing/signers.js +248 -0
- package/dist_ts/writers/gotar.d.ts +43 -0
- package/dist_ts/writers/gotar.js +218 -0
- package/license.md +19 -0
- package/package.json +56 -3
- package/readme.md +176 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/alpine/apkindex.ts +241 -0
- package/ts/alpine/pkginfo.ts +183 -0
- package/ts/alpine/reader.ts +96 -0
- package/ts/arch/builder.ts +283 -0
- package/ts/arch/reader.ts +307 -0
- package/ts/arch/signer.ts +70 -0
- package/ts/arch/vercmp.ts +188 -0
- package/ts/debian/control.ts +150 -0
- package/ts/debian/gostring.ts +150 -0
- package/ts/debian/indexbuilder.ts +355 -0
- package/ts/debian/mailaddress.ts +754 -0
- package/ts/debian/reader.ts +126 -0
- package/ts/index.ts +46 -0
- package/ts/plugins.ts +13 -0
- package/ts/readers/ar.ts +102 -0
- package/ts/readers/bytes.ts +182 -0
- package/ts/readers/bytesource.ts +164 -0
- package/ts/readers/decompress.ts +332 -0
- package/ts/readers/gzipmember.ts +209 -0
- package/ts/readers/tar.ts +835 -0
- package/ts/readers/text.ts +91 -0
- package/ts/rpm/builder.ts +253 -0
- package/ts/rpm/header.ts +628 -0
- package/ts/rpm/reader.ts +317 -0
- package/ts/rpm/repoconfig.ts +45 -0
- package/ts/rpm/repodata.ts +219 -0
- package/ts/rpm/sign.ts +200 -0
- package/ts/rpm/updates.ts +314 -0
- package/ts/rpm/xml.ts +101 -0
- package/ts/shared/architecture.ts +16 -0
- package/ts/shared/errors.ts +78 -0
- package/ts/shared/gopath.ts +54 -0
- package/ts/shared/gostrings.ts +31 -0
- package/ts/shared/limits.ts +93 -0
- package/ts/shared/types.ts +251 -0
- package/ts/shared/url.ts +387 -0
- package/ts/shared/validation.ts +71 -0
- package/ts/signing/keys.ts +110 -0
- package/ts/signing/openpgpframing.ts +115 -0
- package/ts/signing/signers.ts +315 -0
- package/ts/writers/gotar.ts +256 -0
- package/README.md +0 -3
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decodes bytes as Go maps them to text: valid UTF-8 unchanged (a leading BOM
|
|
3
|
+
* is kept), and every byte of an invalid sequence replaced by one U+FFFD, as
|
|
4
|
+
* Go's `utf8.DecodeRune` reports invalid input with width 1. The WHATWG
|
|
5
|
+
* decoder differs for truncated multi-byte sequences (one U+FFFD for the
|
|
6
|
+
* maximal subpart).
|
|
7
|
+
*
|
|
8
|
+
* Gitea 28.2.0 stores package metadata with `encoding/json/v2`, which refuses
|
|
9
|
+
* invalid UTF-8 (`modules/json/jsonv2.go:22-29`,
|
|
10
|
+
* `services/packages/packages.go:166`), so 28.2.0 rejects such a package.
|
|
11
|
+
* Packages stored under older Gitea are still served, so the codec
|
|
12
|
+
* deliberately accepts more than 28.2.0 to keep them importable.
|
|
13
|
+
*/
|
|
14
|
+
export declare function decodeGoUtf8(bytesArg: Uint8Array): string;
|
|
15
|
+
/** ASCII/Latin-1 view of header bytes (magic values, numeric fields). */
|
|
16
|
+
export declare function decodeLatin1(bytesArg: Uint8Array): string;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const strictUtf8 = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
|
|
2
|
+
/**
|
|
3
|
+
* Decodes bytes as Go maps them to text: valid UTF-8 unchanged (a leading BOM
|
|
4
|
+
* is kept), and every byte of an invalid sequence replaced by one U+FFFD, as
|
|
5
|
+
* Go's `utf8.DecodeRune` reports invalid input with width 1. The WHATWG
|
|
6
|
+
* decoder differs for truncated multi-byte sequences (one U+FFFD for the
|
|
7
|
+
* maximal subpart).
|
|
8
|
+
*
|
|
9
|
+
* Gitea 28.2.0 stores package metadata with `encoding/json/v2`, which refuses
|
|
10
|
+
* invalid UTF-8 (`modules/json/jsonv2.go:22-29`,
|
|
11
|
+
* `services/packages/packages.go:166`), so 28.2.0 rejects such a package.
|
|
12
|
+
* Packages stored under older Gitea are still served, so the codec
|
|
13
|
+
* deliberately accepts more than 28.2.0 to keep them importable.
|
|
14
|
+
*/
|
|
15
|
+
export function decodeGoUtf8(bytesArg) {
|
|
16
|
+
try {
|
|
17
|
+
return strictUtf8.decode(bytesArg);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return decodeGoUtf8Slow(bytesArg);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function decodeGoUtf8Slow(bytesArg) {
|
|
24
|
+
const parts = [];
|
|
25
|
+
let i = 0;
|
|
26
|
+
while (i < bytesArg.length) {
|
|
27
|
+
const b0 = bytesArg[i];
|
|
28
|
+
if (b0 < 0x80) {
|
|
29
|
+
parts.push(String.fromCharCode(b0));
|
|
30
|
+
i += 1;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
let need;
|
|
34
|
+
let codePoint;
|
|
35
|
+
let low = 0x80;
|
|
36
|
+
let high = 0xbf;
|
|
37
|
+
if (b0 >= 0xc2 && b0 <= 0xdf) {
|
|
38
|
+
need = 1;
|
|
39
|
+
codePoint = b0 & 0x1f;
|
|
40
|
+
}
|
|
41
|
+
else if (b0 >= 0xe0 && b0 <= 0xef) {
|
|
42
|
+
need = 2;
|
|
43
|
+
codePoint = b0 & 0x0f;
|
|
44
|
+
if (b0 === 0xe0) {
|
|
45
|
+
low = 0xa0;
|
|
46
|
+
}
|
|
47
|
+
else if (b0 === 0xed) {
|
|
48
|
+
high = 0x9f;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (b0 >= 0xf0 && b0 <= 0xf4) {
|
|
52
|
+
need = 3;
|
|
53
|
+
codePoint = b0 & 0x07;
|
|
54
|
+
if (b0 === 0xf0) {
|
|
55
|
+
low = 0x90;
|
|
56
|
+
}
|
|
57
|
+
else if (b0 === 0xf4) {
|
|
58
|
+
high = 0x8f;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
parts.push('\uFFFD');
|
|
63
|
+
i += 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
let valid = i + need < bytesArg.length;
|
|
67
|
+
for (let k = 1; valid && k <= need; k++) {
|
|
68
|
+
const b = bytesArg[i + k];
|
|
69
|
+
const min = k === 1 ? low : 0x80;
|
|
70
|
+
const max = k === 1 ? high : 0xbf;
|
|
71
|
+
if (b < min || b > max) {
|
|
72
|
+
valid = false;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
codePoint = (codePoint << 6) | (b & 0x3f);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!valid) {
|
|
79
|
+
parts.push('\uFFFD');
|
|
80
|
+
i += 1;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
parts.push(String.fromCodePoint(codePoint));
|
|
84
|
+
i += need + 1;
|
|
85
|
+
}
|
|
86
|
+
return parts.join('');
|
|
87
|
+
}
|
|
88
|
+
/** ASCII/Latin-1 view of header bytes (magic values, numeric fields). */
|
|
89
|
+
export function decodeLatin1(bytesArg) {
|
|
90
|
+
let text = '';
|
|
91
|
+
for (let i = 0; i < bytesArg.length; i++) {
|
|
92
|
+
text += String.fromCharCode(bytesArg[i]);
|
|
93
|
+
}
|
|
94
|
+
return text;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3JlYWRlcnMvdGV4dC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLFVBQVUsR0FBRyxJQUFJLFdBQVcsQ0FBQyxPQUFPLEVBQUUsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0FBRTlFOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILE1BQU0sVUFBVSxZQUFZLENBQUMsUUFBb0I7SUFDL0MsSUFBSSxDQUFDO1FBQ0gsT0FBTyxVQUFVLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3JDLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3BDLENBQUM7QUFDSCxDQUFDO0FBRUQsU0FBUyxnQkFBZ0IsQ0FBQyxRQUFvQjtJQUM1QyxNQUFNLEtBQUssR0FBYSxFQUFFLENBQUM7SUFDM0IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ1YsT0FBTyxDQUFDLEdBQUcsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQzNCLE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN2QixJQUFJLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQztZQUNkLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ3BDLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDUCxTQUFTO1FBQ1gsQ0FBQztRQUNELElBQUksSUFBWSxDQUFDO1FBQ2pCLElBQUksU0FBaUIsQ0FBQztRQUN0QixJQUFJLEdBQUcsR0FBRyxJQUFJLENBQUM7UUFDZixJQUFJLElBQUksR0FBRyxJQUFJLENBQUM7UUFDaEIsSUFBSSxFQUFFLElBQUksSUFBSSxJQUFJLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQztZQUM3QixJ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
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ILinuxIndexFile, IRpmPackageRecord, IRpmUpdate } from '../shared/types.js';
|
|
2
|
+
import type { IOpenPgpSigner } from '../signing/signers.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds the repodata of one RPM repository group, as Gitea 28.2.0's
|
|
5
|
+
* `BuildSpecificRepositoryFiles` does (`services/packages/rpm/repository.go:136-309`):
|
|
6
|
+
* `primary`, `filelists` and `other` (gzip), `updateinfo` only when there are
|
|
7
|
+
* updates, then `repomd.xml` and its armored detached signature
|
|
8
|
+
* `repomd.xml.asc`. Paths are relative to the RPM root: `<group>/repodata/…`,
|
|
9
|
+
* or `repodata/…` for the empty group. `repodataNaming` has no default:
|
|
10
|
+
* `'plain'` is Gitea's `repodata/<type>.xml.gz`, `'content-addressed'` is
|
|
11
|
+
* createrepo_c's default `repodata/<sha256 of the .gz>-<type>.xml.gz`
|
|
12
|
+
* (`--unique-md-filenames`, `src/cmd_parser.c:37`, `src/repomd.c:576-636`).
|
|
13
|
+
*
|
|
14
|
+
* Packages are sorted by name, version, architecture and file name; updates
|
|
15
|
+
* are combined as `combineRpmUpdates` describes. Every `repomd.xml` timestamp
|
|
16
|
+
* is `timestamp` in whole seconds. An empty entry set returns `[]` (Gitea
|
|
17
|
+
* deletes the repodata of a group without packages). An entry whose
|
|
18
|
+
* architecture fails `isIndexableArchitecture`, or whose name or version is not
|
|
19
|
+
* a path segment, is `invalid-input`, as are two entries with the same file.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildRpmIndex(input: {
|
|
22
|
+
group: string;
|
|
23
|
+
entries: IRpmPackageRecord[];
|
|
24
|
+
updates: IRpmUpdate[];
|
|
25
|
+
repodataNaming: 'plain' | 'content-addressed';
|
|
26
|
+
signer: IOpenPgpSigner;
|
|
27
|
+
timestamp: Date;
|
|
28
|
+
}): Promise<ILinuxIndexFile[]>;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { isIndexableArchitecture } from '../shared/architecture.js';
|
|
3
|
+
import { LinuxRepoError } from '../shared/errors.js';
|
|
4
|
+
import { assertTimestamp, isPathSegment } from '../shared/validation.js';
|
|
5
|
+
import { createDetachedArmoredSignature } from '../signing/openpgpframing.js';
|
|
6
|
+
import { renderFilelists, renderOther, renderPrimary, renderRepomd, rpmPackageLocation } from './repodata.js';
|
|
7
|
+
import { assertRpmUpdates, combineRpmUpdates, renderUpdateInfo } from './updates.js';
|
|
8
|
+
import { compareCodePoints } from './xml.js';
|
|
9
|
+
/**
|
|
10
|
+
* Builds the repodata of one RPM repository group, as Gitea 28.2.0's
|
|
11
|
+
* `BuildSpecificRepositoryFiles` does (`services/packages/rpm/repository.go:136-309`):
|
|
12
|
+
* `primary`, `filelists` and `other` (gzip), `updateinfo` only when there are
|
|
13
|
+
* updates, then `repomd.xml` and its armored detached signature
|
|
14
|
+
* `repomd.xml.asc`. Paths are relative to the RPM root: `<group>/repodata/…`,
|
|
15
|
+
* or `repodata/…` for the empty group. `repodataNaming` has no default:
|
|
16
|
+
* `'plain'` is Gitea's `repodata/<type>.xml.gz`, `'content-addressed'` is
|
|
17
|
+
* createrepo_c's default `repodata/<sha256 of the .gz>-<type>.xml.gz`
|
|
18
|
+
* (`--unique-md-filenames`, `src/cmd_parser.c:37`, `src/repomd.c:576-636`).
|
|
19
|
+
*
|
|
20
|
+
* Packages are sorted by name, version, architecture and file name; updates
|
|
21
|
+
* are combined as `combineRpmUpdates` describes. Every `repomd.xml` timestamp
|
|
22
|
+
* is `timestamp` in whole seconds. An empty entry set returns `[]` (Gitea
|
|
23
|
+
* deletes the repodata of a group without packages). An entry whose
|
|
24
|
+
* architecture fails `isIndexableArchitecture`, or whose name or version is not
|
|
25
|
+
* a path segment, is `invalid-input`, as are two entries with the same file.
|
|
26
|
+
*/
|
|
27
|
+
export async function buildRpmIndex(input) {
|
|
28
|
+
if (input === null || typeof input !== 'object') {
|
|
29
|
+
throw new LinuxRepoError('invalid-input', 'input must be an object', { field: 'input' });
|
|
30
|
+
}
|
|
31
|
+
const prefix = groupPrefix(input.group);
|
|
32
|
+
if (input.repodataNaming !== 'plain' && input.repodataNaming !== 'content-addressed') {
|
|
33
|
+
throw new LinuxRepoError('invalid-input', "repodataNaming must be 'plain' or 'content-addressed'", {
|
|
34
|
+
field: 'repodataNaming',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (input.signer === null ||
|
|
38
|
+
typeof input.signer !== 'object' ||
|
|
39
|
+
typeof input.signer.signBinary !== 'function' ||
|
|
40
|
+
typeof input.signer.signText !== 'function') {
|
|
41
|
+
throw new LinuxRepoError('invalid-input', 'signer must implement IOpenPgpSigner', { field: 'signer' });
|
|
42
|
+
}
|
|
43
|
+
assertTimestamp(input.timestamp);
|
|
44
|
+
assertRpmRecords(input.entries);
|
|
45
|
+
assertRpmUpdates(input.updates, 'updates');
|
|
46
|
+
if (input.entries.length === 0) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const entries = [...input.entries].sort((a, b) => compareCodePoints(a.name, b.name) ||
|
|
50
|
+
compareCodePoints(a.version, b.version) ||
|
|
51
|
+
compareCodePoints(a.architecture, b.architecture) ||
|
|
52
|
+
compareCodePoints(a.fileName, b.fileName));
|
|
53
|
+
const seconds = Math.floor(input.timestamp.getTime() / 1000);
|
|
54
|
+
const documents = [
|
|
55
|
+
['primary', renderPrimary(entries)],
|
|
56
|
+
['filelists', renderFilelists(entries)],
|
|
57
|
+
['other', renderOther(entries)],
|
|
58
|
+
];
|
|
59
|
+
const updates = combineRpmUpdates(input.updates);
|
|
60
|
+
if (updates.length > 0) {
|
|
61
|
+
documents.push(['updateinfo', renderUpdateInfo(updates)]);
|
|
62
|
+
}
|
|
63
|
+
const files = [];
|
|
64
|
+
const data = [];
|
|
65
|
+
for (const [type, xml] of documents) {
|
|
66
|
+
const open = new TextEncoder().encode(xml);
|
|
67
|
+
const gzipped = gzip(open);
|
|
68
|
+
const checksum = sha256(gzipped);
|
|
69
|
+
const name = input.repodataNaming === 'plain' ? `${type}.xml.gz` : `${checksum}-${type}.xml.gz`;
|
|
70
|
+
files.push(indexFile(`${prefix}repodata/${name}`, gzipped, checksum, 'application/gzip', 'content'));
|
|
71
|
+
data.push({
|
|
72
|
+
type,
|
|
73
|
+
checksum,
|
|
74
|
+
openChecksum: sha256(open),
|
|
75
|
+
href: `repodata/${name}`,
|
|
76
|
+
timestamp: seconds,
|
|
77
|
+
size: gzipped.length,
|
|
78
|
+
openSize: open.length,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const repomd = new TextEncoder().encode(renderRepomd(data));
|
|
82
|
+
const signature = new TextEncoder().encode(await createDetachedArmoredSignature({ data: repomd, signer: input.signer, timestamp: input.timestamp }));
|
|
83
|
+
files.push(indexFile(`${prefix}repodata/repomd.xml`, repomd, sha256(repomd), 'application/xml', 'pointer'));
|
|
84
|
+
files.push(indexFile(`${prefix}repodata/repomd.xml.asc`, signature, sha256(signature), 'application/pgp-signature', 'pointer'));
|
|
85
|
+
return files;
|
|
86
|
+
}
|
|
87
|
+
/** `''` for the root group, else `<group>/`; a group is `/`-separated path segments. */
|
|
88
|
+
function groupPrefix(groupArg) {
|
|
89
|
+
if (typeof groupArg !== 'string') {
|
|
90
|
+
throw new LinuxRepoError('invalid-input', 'group must be a string', { field: 'group' });
|
|
91
|
+
}
|
|
92
|
+
if (groupArg === '') {
|
|
93
|
+
return '';
|
|
94
|
+
}
|
|
95
|
+
for (const segment of groupArg.split('/')) {
|
|
96
|
+
if (!isPathSegment(segment)) {
|
|
97
|
+
throw new LinuxRepoError('invalid-input', 'group must be "/"-separated non-empty segments other than "." and ".."', {
|
|
98
|
+
field: 'group',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return `${groupArg}/`;
|
|
103
|
+
}
|
|
104
|
+
function indexFile(pathArg, bytesArg, sha256Arg, mediaTypeArg, stageArg) {
|
|
105
|
+
return { path: pathArg, bytes: bytesArg, sha256: sha256Arg, mediaType: mediaTypeArg, stage: stageArg, aliases: [] };
|
|
106
|
+
}
|
|
107
|
+
function gzip(bytesArg) {
|
|
108
|
+
const compressed = plugins.zlib.gzipSync(bytesArg);
|
|
109
|
+
return new Uint8Array(compressed.buffer, compressed.byteOffset, compressed.byteLength);
|
|
110
|
+
}
|
|
111
|
+
function sha256(bytesArg) {
|
|
112
|
+
return plugins.crypto.createHash('sha256').update(bytesArg).digest('hex');
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Checks the record fields the renderers read and the layout rules: an
|
|
116
|
+
* indexable architecture, a name and version that are path segments, and no
|
|
117
|
+
* two entries with the same package location.
|
|
118
|
+
*/
|
|
119
|
+
function assertRpmRecords(entriesArg) {
|
|
120
|
+
if (!Array.isArray(entriesArg)) {
|
|
121
|
+
throw new LinuxRepoError('invalid-input', 'entries must be an array of RPM package records', { field: 'entries' });
|
|
122
|
+
}
|
|
123
|
+
const locations = new Set();
|
|
124
|
+
entriesArg.forEach((entry, i) => {
|
|
125
|
+
const at = `entries[${i}]`;
|
|
126
|
+
const record = entry;
|
|
127
|
+
const fail = (whatArg) => {
|
|
128
|
+
throw new LinuxRepoError('invalid-input', `${at} ${whatArg}`, { field: at });
|
|
129
|
+
};
|
|
130
|
+
if (record === null || typeof record !== 'object' || record.format !== 'rpm') {
|
|
131
|
+
fail('must be an RPM package record');
|
|
132
|
+
}
|
|
133
|
+
for (const key of ['name', 'version', 'architecture', 'fileName']) {
|
|
134
|
+
if (typeof record[key] !== 'string') {
|
|
135
|
+
fail(`.${key} must be a string`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!isIndexableArchitecture(record.architecture)) {
|
|
139
|
+
fail(`(${record.fileName}) has an architecture that cannot name an index path: ${JSON.stringify(record.architecture)}`);
|
|
140
|
+
}
|
|
141
|
+
if (!isPathSegment(record.name) || !isPathSegment(record.version)) {
|
|
142
|
+
fail(`(${record.fileName}) has a name or version that cannot name a package path`);
|
|
143
|
+
}
|
|
144
|
+
const digests = record.digests;
|
|
145
|
+
if (digests === null || typeof digests !== 'object' || typeof digests.sha256 !== 'string' || !isCount(digests.size)) {
|
|
146
|
+
fail('.digests must hold sha256 and size');
|
|
147
|
+
}
|
|
148
|
+
const metadata = record.metadata;
|
|
149
|
+
if (metadata === null || typeof metadata !== 'object') {
|
|
150
|
+
fail('.metadata must be an object');
|
|
151
|
+
}
|
|
152
|
+
for (const key of [
|
|
153
|
+
'epoch',
|
|
154
|
+
'upstreamVersion',
|
|
155
|
+
'release',
|
|
156
|
+
'summary',
|
|
157
|
+
'description',
|
|
158
|
+
'license',
|
|
159
|
+
'projectUrl',
|
|
160
|
+
'vendor',
|
|
161
|
+
'group',
|
|
162
|
+
'packager',
|
|
163
|
+
'sourceRpm',
|
|
164
|
+
'buildHost',
|
|
165
|
+
]) {
|
|
166
|
+
if (typeof metadata[key] !== 'string') {
|
|
167
|
+
fail(`.metadata.${key} must be a string`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (const key of ['buildTime', 'fileTime', 'installedSize', 'archiveSize']) {
|
|
171
|
+
if (!isCount(metadata[key])) {
|
|
172
|
+
fail(`.metadata.${key} must be a non-negative safe integer`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
for (const key of ['provides', 'requires', 'conflicts', 'obsoletes']) {
|
|
176
|
+
const list = metadata[key];
|
|
177
|
+
if (!Array.isArray(list) ||
|
|
178
|
+
!list.every((dependency) => dependency !== null &&
|
|
179
|
+
typeof dependency === 'object' &&
|
|
180
|
+
['name', 'flags', 'epoch', 'version', 'release'].every((field) => typeof dependency[field] === 'string'))) {
|
|
181
|
+
fail(`.metadata.${key} must be a list of dependencies`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const files = metadata.files;
|
|
185
|
+
if (!Array.isArray(files) ||
|
|
186
|
+
!files.every((file) => file !== null && typeof file === 'object' && typeof file.path === 'string' && typeof file.type === 'string' && typeof file.isExecutable === 'boolean')) {
|
|
187
|
+
fail('.metadata.files must be a list of files');
|
|
188
|
+
}
|
|
189
|
+
const changelogs = metadata.changelogs;
|
|
190
|
+
if (!Array.isArray(changelogs) ||
|
|
191
|
+
!changelogs.every((changelog) => changelog !== null &&
|
|
192
|
+
typeof changelog === 'object' &&
|
|
193
|
+
typeof changelog.author === 'string' &&
|
|
194
|
+
typeof changelog.text === 'string' &&
|
|
195
|
+
isCount(changelog.date))) {
|
|
196
|
+
fail('.metadata.changelogs must be a list of changelog entries');
|
|
197
|
+
}
|
|
198
|
+
const location = rpmPackageLocation(record);
|
|
199
|
+
if (locations.has(location)) {
|
|
200
|
+
fail(`duplicates the file ${record.fileName}`);
|
|
201
|
+
}
|
|
202
|
+
locations.add(location);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
function isCount(valueArg) {
|
|
206
|
+
return typeof valueArg === 'number' && Number.isSafeInteger(valueArg) && valueArg >= 0;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVpbGRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3JwbS9idWlsZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUVyRCxPQUFPLEVBQUUsZUFBZSxFQUFFLGFBQWEsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3pFLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRTlFLE9BQU8sRUFBRSxlQUFlLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxZQUFZLEVBQUUsa0JBQWtCLEVBQW9CLE1BQU0sZUFBZSxDQUFDO0FBQ2hJLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxpQkFBaUIsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFFN0M7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxhQUFhLENBQUMsS0FPbkM7SUFDQyxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDaEQsTUFBTSxJQUFJLGNBQWMsQ0FBQyxlQUFlLEVBQUUseUJBQXlCLEVBQUUsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUMzRixDQUFDO0lBQ0QsTUFBTSxNQUFNLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN4QyxJQUFJLEtBQUssQ0FBQyxjQUFjLEtBQUssT0FBTyxJQUFJLEtBQUssQ0FBQyxjQUFjLEtBQUssbUJBQW1CLEVBQUUsQ0FBQztRQUNyRixNQUFNLElBQUksY0FBYyxDQUFDLGVBQWUsRUFBRSx1REFBdUQsRUFBRTtZQUNqRyxLQUFLLEVBQUUsZ0JBQWdCO1NBQ3hCLENBQUMsQ0FBQztJQUNMLENBQUM7SUFDRCxJQUNFLEtBQUssQ0FBQyxNQUFNLEtBQUssSUFBSTtRQUNyQixPQUFPLEtBQUssQ0FBQyxNQUFNLEtBQUssUUFBUTtRQUNoQyxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMsVUFBVSxLQUFLLFVBQVU7UUFDN0MsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLFFBQVEsS0FBSyxVQUFVLEVBQzNDLENBQUM7UUFDRCxNQUFNLElBQUksY0FBYyxDQUFDLGVBQWUsRUFBRSxzQ0FBc0MsRUFBRSxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO0lBQ3pHLENBQUM7SUFDRCxlQUFlLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ2pDLGdCQUFnQixDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNoQyxnQkFBZ0IsQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQzNDLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDL0IsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDO0lBRUQsTUFBTSxPQUFPLEdBQUcsQ0FBQyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQ3JDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQ1AsaUJBQWlCLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDO1FBQ2pDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN2QyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsWUFBWSxFQUFFLENBQUMsQ0FBQyxZQUFZLENBQUM7UUFDakQsaUJBQWlCLENBQUMsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLENBQzVDLENBQUM7SUFDRixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUM7SUFDN0QsTUFBTSxTQUFTLEdBQTRCO1FBQ3pDLENBQUMsU0FBUyxFQUFFLGFBQWEsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNuQyxDQUFDLFdBQVcsRUFBRSxlQUFlLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDdkMsQ0FBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0tBQ2hDLENBQUM7SUFDRixNQUFNLE9BQU8sR0FBRyxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDakQsSUFBSSxPQUFPLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3ZCLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxZQUFZLEVBQUUsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzVELENBQUM7SUFFRCxNQUFNLEtBQUssR0FBc0IsRUFBRSxDQUFDO0lBQ3BDLE1BQU0sSUFBSSxHQUFrQixFQUFFLENBQUM7SUFDL0IsS0FBSyxNQUFNLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJLFNBQVMsRUFBRSxDQUFDO1FBQ3BDLE1BQU0sSUFBSSxHQUFHLElBQUksV0FBVyxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMzQixNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDakMsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLGNBQWMsS0FBSyxPQUFPLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsUUFBUSxJQUFJLElBQUksU0FBUyxDQUFDO1FBQ2hHLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsTUFBTSxZQUFZLElBQUksRUFBRSxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsa0JBQWtCLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQztRQUNyRyxJQUFJLENBQUMsSUFBSSxDQUFDO1lBQ1IsSUFBSTtZQUNKLFFBQVE7WUFDUixZQUFZLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQztZQUMxQixJQUFJLEVBQUUsWUFBWSxJQUFJLEVBQUU7WUFDeEIsU0FBUyxFQUFFLE9BQU87WUFDbEIsSUFBSSxFQUFFLE9BQU8sQ0FBQyxNQUFNO1lBQ3BCLFFBQVEsRUFBRSxJQUFJLENBQUMsTUFBTTtTQUN0QixDQUFDLENBQUM7SUFDTCxDQUFDO0lBQ0QsTUFBTSxNQUFNLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDNUQsTUFBTSxTQUFTLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQyxNQUFNLENBQ3hDLE1BQU0sOEJBQThCLENBQUMsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLFNBQVMsRUFBRSxLQUFLLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FDekcsQ0FBQztJQUNGLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsTUFBTSxxQkFBcUIsRUFBRSxNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLGlCQUFpQixFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUM7SUFDNUcsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxNQUFNLHlCQUF5QixFQUFFLFNBQVMsRUFBRSxNQUFNLENBQUMsU0FBUyxDQUFDLEVBQUUsMkJBQTJCLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQztJQUNoSSxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRCx3RkFBd0Y7QUFDeEYsU0FBUyxXQUFXLENBQUMsUUFBaUI7SUFDcEMsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUNqQyxNQUFNLElBQUksY0FBYyxDQUFDLGVBQWUsRUFBRSx3QkFBd0IsRUFBRSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO0lBQzFGLENBQUM7SUFDRCxJQUFJLFFBQVEsS0FBSyxFQUFFLEVBQUUsQ0FBQztRQUNwQixPQUFPLEVBQUUsQ0FBQztJQUNaLENBQUM7SUFDRCxLQUFLLE1BQU0sT0FBTyxJQUFJLFFBQVEsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUMxQyxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDNUIsTUFBTSxJQUFJLGNBQWMsQ0FBQyxlQUFlLEVBQUUsd0VBQXdFLEVBQUU7Z0JBQ2xILEtBQUssRUFBRSxPQUFPO2FBQ2YsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLEdBQUcsUUFBUSxHQUFHLENBQUM7QUFDeEIsQ0FBQztBQUVELFNBQVMsU0FBUyxDQUNoQixPQUFlLEVBQ2YsUUFBb0IsRUFDcEIsU0FBaUIsRUFDakIsWUFBb0IsRUFDcEIsUUFBa0M7SUFFbEMsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFLENBQUM7QUFDdEgsQ0FBQztBQUVELFNBQVMsSUFBSSxDQUFDLFFBQW9CO0lBQ2hDLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ25ELE9BQU8sSUFBSSxVQUFVLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsVUFBVSxFQUFFLFVBQVUsQ0FBQyxVQUFVLENBQUMsQ0FBQztBQUN6RixDQUFDO0FBRUQsU0FBUyxNQUFNLENBQUMsUUFBb0I7SUFDbEMsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQzVFLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBUyxnQkFBZ0IsQ0FBQyxVQUFtQjtJQUMzQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1FBQy9CLE1BQU0sSUFBSSxjQUFjLENBQUMsZUFBZSxFQUFFLGlEQUFpRCxFQUFFLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7SUFDckgsQ0FBQztJQUNELE1BQU0sU0FBUyxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDcEMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDLEtBQWMsRUFBRSxDQUFDLEVBQUUsRUFBRTtRQUN2QyxNQUFNLEVBQUUsR0FBRyxXQUFXLENBQUMsR0FBRyxDQUFDO1FBQzNCLE1BQU0sTUFBTSxHQUFHLEtBQTBCLENBQUM7UUFDMUMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxPQUFlLEVBQVMsRUFBRTtZQUN0QyxNQUFNLElBQUksY0FBYyxDQUFDLGVBQWUsRUFBRSxHQUFHLEVBQUUsSUFBSSxPQUFPLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQy9FLENBQUMsQ0FBQztRQUNGLElBQUksTUFBTSxLQUFLLElBQUksSUFBSSxPQUFPLE1BQU0sS0FBSyxRQUFRLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxLQUFLLEVBQUUsQ0FBQztZQUM3RSxJQUFJLENBQUMsK0JBQStCLENBQUMsQ0FBQztRQUN4QyxDQUFDO1FBQ0QsS0FBSyxNQUFNLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxTQUFTLEVBQUUsY0FBYyxFQUFFLFVBQVUsQ0FBVSxFQUFFLENBQUM7WUFDM0UsSUFBSSxPQUFPLE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDcEMsSUFBSSxDQUFDLElBQUksR0FBRyxtQkFBbUIsQ0FBQyxDQUFDO1lBQ25DLENBQUM7UUFDSCxDQUFDO1FBQ0QsSUFBSSxDQUFDLHVCQUF1QixDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDO1lBQ2xELElBQUksQ0FBQyxJQUFJLE1BQU0sQ0FBQyxRQUFRLHlEQUF5RCxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDMUgsQ0FBQztRQUNELElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQ2xFLElBQUksQ0FBQyxJQUFJLE1BQU0sQ0FBQyxRQUFRLHlEQUF5RCxDQUFDLENBQUM7UUFDckYsQ0FBQztRQUNELE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUM7UUFDL0IsSUFBSSxPQUFPLEtBQUssSUFBSSxJQUFJLE9BQU8sT0FBTyxLQUFLLFFBQVEsSUFBSSxPQUFPLE9BQU8sQ0FBQyxNQUFNLEtBQUssUUFBUSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ3BILElBQUksQ0FBQyxvQ0FBb0MsQ0FBQyxDQUFDO1FBQzdDLENBQUM7UUFDRCxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDO1FBQ2pDLElBQUksUUFBUSxLQUFLLElBQUksSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUN0RCxJQUFJLENBQUMsNkJBQTZCLENBQUMsQ0FBQztRQUN0QyxDQUFDO1FBQ0QsS0FBSyxNQUFNLEdBQUcsSUFBSTtZQUNoQixPQUFPO1lBQ1AsaUJBQWlCO1lBQ2pCLFNBQVM7WUFDVCxTQUFTO1lBQ1QsYUFBYTtZQUNiLFNBQVM7WUFDVCxZQUFZO1lBQ1osUUFBUTtZQUNSLE9BQU87WUFDUCxVQUFVO1lBQ1YsV0FBVztZQUNYLFdBQVc7U0FDSCxFQUFFLENBQUM7WUFDWCxJQUFJLE9BQU8sUUFBUSxDQUFDLEdBQUcsQ0FBQyxLQUFLLFFBQVEsRUFBRSxDQUFDO2dCQUN0QyxJQUFJLENBQUMsYUFBYSxHQUFHLG1CQUFtQixDQUFDLENBQUM7WUFDNUMsQ0FBQztRQUNILENBQUM7UUFDRCxLQUFLLE1BQU0sR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLFVBQVUsRUFBRSxlQUFlLEVBQUUsYUFBYSxDQUFVLEVBQUUsQ0FBQztZQUNyRixJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQzVCLElBQUksQ0FBQyxhQUFhLEdBQUcsc0NBQXNDLENBQUMsQ0FBQztZQUMvRCxDQUFDO1FBQ0gsQ0FBQztRQUNELEtBQUssTUFBTSxHQUFHLElBQUksQ0FBQyxVQUFVLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxXQUFXLENBQVUsRUFBRSxDQUFDO1lBQzlFLE1BQU0sSUFBSSxHQUFZLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNwQyxJQUNFLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7Z0JBQ3BCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FDVCxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQ2IsVUFBVSxLQUFLLElBQUk7b0JBQ25CLE9BQU8sVUFBVSxLQUFLLFFBQVE7b0JBQzlCLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLFNBQVMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsT0FBTyxVQUFVLENBQUMsS0FBSyxDQUFDLEtBQUssUUFBUSxDQUFDLENBQzNHLEVBQ0QsQ0FBQztnQkFDRCxJQUFJLENBQUMsYUFBYSxHQUFHLGlDQUFpQyxDQUFDLENBQUM7WUFDMUQsQ0FBQztRQUNILENBQUM7UUFDRCxNQUFNLEtBQUssR0FBWSxRQUFRLENBQUMsS0FBSyxDQUFDO1FBQ3RDLElBQ0UsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQztZQUNyQixDQUFDLEtBQUssQ0FBQyxLQUFLLENBQ1YsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUNQLElBQUksS0FBSyxJQUFJLElBQUksT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLE9BQU8sSUFBSSxDQUFDLElBQUksS0FBSyxRQUFRLElBQUksT0FBTyxJQUFJLENBQUMsSUFBSSxLQUFLLFFBQVEsSUFBSSxPQUFPLElBQUksQ0FBQyxZQUFZLEtBQUssU0FBUyxDQUN4SixFQUNELENBQUM7WUFDRCxJQUFJLENBQUMseUNBQXlDLENBQUMsQ0FBQztRQUNsRCxDQUFDO1FBQ0QsTUFBTSxVQUFVLEdBQVksUUFBUSxDQUFDLFVBQVUsQ0FBQztRQUNoRCxJQUNFLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUM7WUFDMUIsQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUNmLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FDWixTQUFTLEtBQUssSUFBSTtnQkFDbEIsT0FBTyxTQUFTLEtBQUssUUFBUTtnQkFDN0IsT0FBTyxTQUFTLENBQUMsTUFBTSxLQUFLLFFBQVE7Z0JBQ3BDLE9BQU8sU0FBUyxDQUFDLElBQUksS0FBSyxRQUFRO2dCQUNsQyxPQUFPLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUMxQixFQUNELENBQUM7WUFDRCxJQUFJLENBQUMsMERBQTBELENBQUMsQ0FBQztRQUNuRSxDQUFDO1FBQ0QsTUFBTSxRQUFRLEdBQUcsa0JBQWtCLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDNUMsSUFBSSxTQUFTLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDNUIsSUFBSSxDQUFDLHVCQUF1QixNQUFNLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNqRCxDQUFDO1FBQ0QsU0FBUyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMxQixDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxTQUFTLE9BQU8sQ0FBQyxRQUFpQjtJQUNoQyxPQUFPLE9BQU8sUUFBUSxLQUFLLFFBQVEsSUFBSSxNQUFNLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxJQUFJLFFBQVEsSUFBSSxDQUFDLENBQUM7QUFDekYsQ0FBQyJ9
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { type IByteReader } from '../readers/bytes.js';
|
|
2
|
+
import type { ILinuxPackageLimits } from '../shared/limits.js';
|
|
3
|
+
/**
|
|
4
|
+
* RPM lead and header structures, read the way go-rpmutils v0.4.0 (Gitea
|
|
5
|
+
* 28.2.0's pin, `go.mod:85`) reads them (`header.go:75-146`,
|
|
6
|
+
* `rpmutils.go:97-136`), so that a package Gitea accepts is accepted and one
|
|
7
|
+
* Gitea refuses (or panics on) is refused:
|
|
8
|
+
* - an index entry of a fixed-size type covers `offset .. offset + size*count`;
|
|
9
|
+
* any other type is a run of `count` NUL-terminated strings; an entry that
|
|
10
|
+
* does not fit the data is `malformed-header`;
|
|
11
|
+
* - a later entry with the same tag replaces an earlier one;
|
|
12
|
+
* - the main header's SHA-256 (signature tag 273), else SHA-1 (269), is
|
|
13
|
+
* checked as lowercase hex (`digest-mismatch`).
|
|
14
|
+
*/
|
|
15
|
+
export declare const RPM_LEAD_BYTES = 96;
|
|
16
|
+
/** Header data types. */
|
|
17
|
+
export declare const RPM_TYPE: {
|
|
18
|
+
readonly null: 0;
|
|
19
|
+
readonly char: 1;
|
|
20
|
+
readonly int8: 2;
|
|
21
|
+
readonly int16: 3;
|
|
22
|
+
readonly int32: 4;
|
|
23
|
+
readonly int64: 5;
|
|
24
|
+
readonly string: 6;
|
|
25
|
+
readonly bin: 7;
|
|
26
|
+
readonly stringArray: 8;
|
|
27
|
+
readonly i18nString: 9;
|
|
28
|
+
};
|
|
29
|
+
/** Signature header tags. */
|
|
30
|
+
export declare const RPM_SIGNATURE_TAG: {
|
|
31
|
+
readonly headerSignatures: 62;
|
|
32
|
+
readonly dsa: 267;
|
|
33
|
+
readonly rsa: 268;
|
|
34
|
+
readonly sha1: 269;
|
|
35
|
+
readonly longArchiveSize: 271;
|
|
36
|
+
readonly sha256: 273;
|
|
37
|
+
readonly size: 1000;
|
|
38
|
+
readonly pgp: 1002;
|
|
39
|
+
readonly md5: 1004;
|
|
40
|
+
readonly gpg: 1005;
|
|
41
|
+
readonly payloadSize: 1007;
|
|
42
|
+
readonly reservedSpace: 1008;
|
|
43
|
+
};
|
|
44
|
+
/** One index entry with the byte range of its contents in the header data. */
|
|
45
|
+
export interface IRpmHeaderEntry {
|
|
46
|
+
tag: number;
|
|
47
|
+
type: number;
|
|
48
|
+
count: number;
|
|
49
|
+
start: number;
|
|
50
|
+
end: number;
|
|
51
|
+
}
|
|
52
|
+
/** The value of an integer tag as go-rpmutils `getInts` reads it. */
|
|
53
|
+
export interface IRpmIntegers {
|
|
54
|
+
/** Element width in bytes: 1, 2, 4 or 8. */
|
|
55
|
+
width: 1 | 2 | 4 | 8;
|
|
56
|
+
values: bigint[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A parsed header: its exact bytes, as read, and its index entries. The index
|
|
60
|
+
* stays in the header bytes; only the end of each entry's contents is kept
|
|
61
|
+
* beside it (4 bytes per entry), and finding the end of a string entry may
|
|
62
|
+
* build a NUL directory of about 3% of the data (`NulIndex`), so a header
|
|
63
|
+
* costs little more than its own bytes: reading a 64 MiB header peaks at
|
|
64
|
+
* about 1.33x its bytes with 4.19M entries and 1.12x with data of all NULs
|
|
65
|
+
* (Node 24.20.0). `IRpmHeaderEntry` objects are made only for the entries
|
|
66
|
+
* asked for.
|
|
67
|
+
*/
|
|
68
|
+
export declare class RpmHeader {
|
|
69
|
+
/** The header bytes as read: intro, index and data (with the padding of a signature header). */
|
|
70
|
+
readonly bytes: Uint8Array;
|
|
71
|
+
readonly data: Uint8Array;
|
|
72
|
+
/** Number of index entries. */
|
|
73
|
+
readonly entryCount: number;
|
|
74
|
+
private readonly view;
|
|
75
|
+
private readonly ends;
|
|
76
|
+
private readonly lookup;
|
|
77
|
+
private readonly maxEntries;
|
|
78
|
+
constructor(bytesArg: Uint8Array, endsArg: Uint32Array, dataStartArg: number, maxEntriesArg: number);
|
|
79
|
+
/** The tag of index entry `indexArg`. */
|
|
80
|
+
tagAt(indexArg: number): number;
|
|
81
|
+
/** Index entry `indexArg`. */
|
|
82
|
+
entryAt(indexArg: number): IRpmHeaderEntry;
|
|
83
|
+
/** The effective entry for a tag (the last one in the index), or `null`. */
|
|
84
|
+
entry(tagArg: number): IRpmHeaderEntry | null;
|
|
85
|
+
/** The index positions of every effective entry (the last one per tag), in ascending tag order. */
|
|
86
|
+
effectiveIndexes(): Uint32Array;
|
|
87
|
+
contents(entryArg: IRpmHeaderEntry): Uint8Array;
|
|
88
|
+
/**
|
|
89
|
+
* go-rpmutils `GetStrings` on the raw bytes: `null` when the tag is absent
|
|
90
|
+
* or not a string type. A negative count (a Go slice panic) is
|
|
91
|
+
* `malformed-header`; more values than `maxEntries` is `limit-exceeded`.
|
|
92
|
+
*/
|
|
93
|
+
strings(tagArg: number): Uint8Array[] | null;
|
|
94
|
+
/** go-rpmutils `getInts`: `null` when the tag is absent or not an integer type. */
|
|
95
|
+
integers(tagArg: number): IRpmIntegers | null;
|
|
96
|
+
/** go-rpmutils `GetUint32s`: `null` when absent, not an integer type, or 64-bit. */
|
|
97
|
+
uint32s(tagArg: number): number[] | null;
|
|
98
|
+
/** go-rpmutils `GetUint64s`: `null` when absent or not an integer type. */
|
|
99
|
+
uint64s(tagArg: number): bigint[] | null;
|
|
100
|
+
/** go-rpmutils `GetBytes`: `null` when absent or not binary. */
|
|
101
|
+
binary(tagArg: number): Uint8Array | null;
|
|
102
|
+
private assertCount;
|
|
103
|
+
}
|
|
104
|
+
/** The lead, both headers and their offsets, as read from the start of a package. */
|
|
105
|
+
export interface IRpmPackageHeaders {
|
|
106
|
+
lead: Uint8Array;
|
|
107
|
+
/** Lead bytes 6-7 equal 1 (go-rpmutils `rpmutils.go:131`). */
|
|
108
|
+
isSource: boolean;
|
|
109
|
+
signature: RpmHeader;
|
|
110
|
+
main: RpmHeader;
|
|
111
|
+
/** Offset of the main header: 96 plus the signature header with its padding. */
|
|
112
|
+
mainStart: number;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Reads the lead, the signature header and the main header from `readerArg`
|
|
116
|
+
* and checks the main header digest. The signature header plus the main
|
|
117
|
+
* header count against `maxMetadataBytes`. The reader is left at the payload.
|
|
118
|
+
*/
|
|
119
|
+
export declare function readRpmHeaders(readerArg: IByteReader, limitsArg: Readonly<ILinuxPackageLimits>): Promise<IRpmPackageHeaders>;
|
|
120
|
+
/** An entry written by `writeSignatureHeader` that does not come from the source header. */
|
|
121
|
+
export interface IRpmNewEntry {
|
|
122
|
+
tag: number;
|
|
123
|
+
type: number;
|
|
124
|
+
count: number;
|
|
125
|
+
contents: Uint8Array;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Serializes a signature header as go-rpmutils `WriteTo(…, 62)` does
|
|
129
|
+
* (`writeheader.go:66-123`): entries below tag 64 are dropped, the rest are
|
|
130
|
+
* written in ascending tag order with INT16/32/64 data aligned, the region
|
|
131
|
+
* tag 62 comes first in the index with its 16-byte trailer as the last data
|
|
132
|
+
* item, and the whole header is padded to 8 bytes.
|
|
133
|
+
*
|
|
134
|
+
* The entries are the effective entries of `sourceArg` whose tags are not in
|
|
135
|
+
* `removedArg`, copied without an object per entry, and `addedArg`, whose
|
|
136
|
+
* tags must be in `removedArg`.
|
|
137
|
+
*/
|
|
138
|
+
export declare function writeSignatureHeader(sourceArg: RpmHeader, removedArg: ReadonlySet<number>, addedArg: IRpmNewEntry[]): Uint8Array;
|