@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.
Files changed (131) hide show
  1. package/.smartconfig.json +65 -0
  2. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  3. package/dist_ts/00_commitinfo_data.js +9 -0
  4. package/dist_ts/alpine/apkindex.d.ts +39 -0
  5. package/dist_ts/alpine/apkindex.js +211 -0
  6. package/dist_ts/alpine/pkginfo.d.ts +45 -0
  7. package/dist_ts/alpine/pkginfo.js +159 -0
  8. package/dist_ts/alpine/reader.d.ts +20 -0
  9. package/dist_ts/alpine/reader.js +89 -0
  10. package/dist_ts/arch/builder.d.ts +41 -0
  11. package/dist_ts/arch/builder.js +234 -0
  12. package/dist_ts/arch/reader.d.ts +16 -0
  13. package/dist_ts/arch/reader.js +271 -0
  14. package/dist_ts/arch/signer.d.ts +21 -0
  15. package/dist_ts/arch/signer.js +66 -0
  16. package/dist_ts/arch/vercmp.d.ts +12 -0
  17. package/dist_ts/arch/vercmp.js +170 -0
  18. package/dist_ts/debian/control.d.ts +22 -0
  19. package/dist_ts/debian/control.js +137 -0
  20. package/dist_ts/debian/gostring.d.ts +26 -0
  21. package/dist_ts/debian/gostring.js +145 -0
  22. package/dist_ts/debian/indexbuilder.d.ts +47 -0
  23. package/dist_ts/debian/indexbuilder.js +299 -0
  24. package/dist_ts/debian/mailaddress.d.ts +12 -0
  25. package/dist_ts/debian/mailaddress.js +736 -0
  26. package/dist_ts/debian/reader.d.ts +30 -0
  27. package/dist_ts/debian/reader.js +110 -0
  28. package/dist_ts/index.d.ts +23 -0
  29. package/dist_ts/index.js +18 -0
  30. package/dist_ts/plugins.d.ts +8 -0
  31. package/dist_ts/plugins.js +11 -0
  32. package/dist_ts/readers/ar.d.ts +31 -0
  33. package/dist_ts/readers/ar.js +87 -0
  34. package/dist_ts/readers/bytes.d.ts +58 -0
  35. package/dist_ts/readers/bytes.js +147 -0
  36. package/dist_ts/readers/bytesource.d.ts +40 -0
  37. package/dist_ts/readers/bytesource.js +154 -0
  38. package/dist_ts/readers/decompress.d.ts +43 -0
  39. package/dist_ts/readers/decompress.js +269 -0
  40. package/dist_ts/readers/gzipmember.d.ts +32 -0
  41. package/dist_ts/readers/gzipmember.js +176 -0
  42. package/dist_ts/readers/tar.d.ts +85 -0
  43. package/dist_ts/readers/tar.js +731 -0
  44. package/dist_ts/readers/text.d.ts +16 -0
  45. package/dist_ts/readers/text.js +96 -0
  46. package/dist_ts/rpm/builder.d.ts +28 -0
  47. package/dist_ts/rpm/builder.js +208 -0
  48. package/dist_ts/rpm/header.d.ts +138 -0
  49. package/dist_ts/rpm/header.js +546 -0
  50. package/dist_ts/rpm/reader.d.ts +20 -0
  51. package/dist_ts/rpm/reader.js +306 -0
  52. package/dist_ts/rpm/repoconfig.d.ts +15 -0
  53. package/dist_ts/rpm/repoconfig.js +38 -0
  54. package/dist_ts/rpm/repodata.d.ts +44 -0
  55. package/dist_ts/rpm/repodata.js +148 -0
  56. package/dist_ts/rpm/sign.d.ts +30 -0
  57. package/dist_ts/rpm/sign.js +183 -0
  58. package/dist_ts/rpm/updates.d.ts +38 -0
  59. package/dist_ts/rpm/updates.js +265 -0
  60. package/dist_ts/rpm/xml.d.ts +18 -0
  61. package/dist_ts/rpm/xml.js +90 -0
  62. package/dist_ts/shared/architecture.d.ts +8 -0
  63. package/dist_ts/shared/architecture.js +17 -0
  64. package/dist_ts/shared/errors.d.ts +38 -0
  65. package/dist_ts/shared/errors.js +22 -0
  66. package/dist_ts/shared/gopath.d.ts +13 -0
  67. package/dist_ts/shared/gopath.js +53 -0
  68. package/dist_ts/shared/gostrings.d.ts +14 -0
  69. package/dist_ts/shared/gostrings.js +29 -0
  70. package/dist_ts/shared/limits.d.ts +47 -0
  71. package/dist_ts/shared/limits.js +57 -0
  72. package/dist_ts/shared/types.d.ts +213 -0
  73. package/dist_ts/shared/types.js +13 -0
  74. package/dist_ts/shared/url.d.ts +18 -0
  75. package/dist_ts/shared/url.js +371 -0
  76. package/dist_ts/shared/validation.d.ts +21 -0
  77. package/dist_ts/shared/validation.js +58 -0
  78. package/dist_ts/signing/keys.d.ts +36 -0
  79. package/dist_ts/signing/keys.js +89 -0
  80. package/dist_ts/signing/openpgpframing.d.ts +33 -0
  81. package/dist_ts/signing/openpgpframing.js +93 -0
  82. package/dist_ts/signing/signers.d.ts +63 -0
  83. package/dist_ts/signing/signers.js +248 -0
  84. package/dist_ts/writers/gotar.d.ts +43 -0
  85. package/dist_ts/writers/gotar.js +218 -0
  86. package/license.md +19 -0
  87. package/package.json +56 -3
  88. package/readme.md +176 -0
  89. package/ts/00_commitinfo_data.ts +8 -0
  90. package/ts/alpine/apkindex.ts +241 -0
  91. package/ts/alpine/pkginfo.ts +183 -0
  92. package/ts/alpine/reader.ts +96 -0
  93. package/ts/arch/builder.ts +283 -0
  94. package/ts/arch/reader.ts +307 -0
  95. package/ts/arch/signer.ts +70 -0
  96. package/ts/arch/vercmp.ts +188 -0
  97. package/ts/debian/control.ts +150 -0
  98. package/ts/debian/gostring.ts +150 -0
  99. package/ts/debian/indexbuilder.ts +355 -0
  100. package/ts/debian/mailaddress.ts +754 -0
  101. package/ts/debian/reader.ts +126 -0
  102. package/ts/index.ts +46 -0
  103. package/ts/plugins.ts +13 -0
  104. package/ts/readers/ar.ts +102 -0
  105. package/ts/readers/bytes.ts +182 -0
  106. package/ts/readers/bytesource.ts +164 -0
  107. package/ts/readers/decompress.ts +332 -0
  108. package/ts/readers/gzipmember.ts +209 -0
  109. package/ts/readers/tar.ts +835 -0
  110. package/ts/readers/text.ts +91 -0
  111. package/ts/rpm/builder.ts +253 -0
  112. package/ts/rpm/header.ts +628 -0
  113. package/ts/rpm/reader.ts +317 -0
  114. package/ts/rpm/repoconfig.ts +45 -0
  115. package/ts/rpm/repodata.ts +219 -0
  116. package/ts/rpm/sign.ts +200 -0
  117. package/ts/rpm/updates.ts +314 -0
  118. package/ts/rpm/xml.ts +101 -0
  119. package/ts/shared/architecture.ts +16 -0
  120. package/ts/shared/errors.ts +78 -0
  121. package/ts/shared/gopath.ts +54 -0
  122. package/ts/shared/gostrings.ts +31 -0
  123. package/ts/shared/limits.ts +93 -0
  124. package/ts/shared/types.ts +251 -0
  125. package/ts/shared/url.ts +387 -0
  126. package/ts/shared/validation.ts +71 -0
  127. package/ts/signing/keys.ts +110 -0
  128. package/ts/signing/openpgpframing.ts +115 -0
  129. package/ts/signing/signers.ts +315 -0
  130. package/ts/writers/gotar.ts +256 -0
  131. package/README.md +0 -3
@@ -0,0 +1,218 @@
1
+ import { concatBytes } from '../readers/bytes.js';
2
+ import { LinuxRepoError } from '../shared/errors.js';
3
+ import { goPathJoin } from '../shared/gopath.js';
4
+ const BLOCK = 512;
5
+ const NAME_SIZE = 100;
6
+ const PREFIX_SIZE = 155;
7
+ /** Go `maxSpecialFileSize`: the largest PAX record block Go writes or reads. */
8
+ const MAX_SPECIAL_FILE_SIZE = 1 << 20;
9
+ /** Go `fitsInOctal(8, x)` for the mode field: below 8^7. */
10
+ const MODE_LIMIT = 8 ** 7;
11
+ /** Go `fitsInOctal(12, x)` for the size field: below 8^11 (8 GiB). */
12
+ const SIZE_LIMIT = 8 ** 11;
13
+ const encoder = new TextEncoder();
14
+ /**
15
+ * The subset of Go 1.27 `archive/tar` `Writer` that Gitea 28.2.0 uses for its
16
+ * index archives (Arch `services/packages/arch/repository.go:381-399`, Alpine
17
+ * `services/packages/alpine/repository.go:331-351`): regular files from
18
+ * `tar.Header{Name, Mode, Size}` with every other field zero, so the zero
19
+ * `ModTime` is written as 0, uid and gid are 0 and user and group names are
20
+ * empty.
21
+ *
22
+ * Format choice follows Go's `allowedFormats` and `WriteHeader`: USTAR when the
23
+ * name is ASCII and at most 100 bytes, USTAR with a prefix split when the name
24
+ * splits, else PAX with one `path` record (refused when the records exceed
25
+ * Go's 1 MiB `maxSpecialFileSize`, as Go returns `ErrFieldTooLong`).
26
+ *
27
+ * Refused with `invalid-input` where Go would not write a regular file in USTAR
28
+ * or PAX-`path` form:
29
+ * - a name with NUL (Go fails with an invalid PAX record) or ending with `/`
30
+ * (Go refuses it for a regular file; Gitea's untyped header would become a
31
+ * directory entry instead);
32
+ * - a mode outside 7 octal digits (Go falls back to the GNU format);
33
+ * - a size of 8 GiB or more (Go adds a PAX `size` record; the codec does not);
34
+ * - a string that is not well-formed UTF-16 (Go strings never carry one).
35
+ */
36
+ export function writeGoTar(filesArg, optionsArg = { endOfArchive: true }) {
37
+ const parts = [];
38
+ for (const file of filesArg) {
39
+ parts.push(goTarHeader(file.name, file.mode, file.data.length), file.data, new Uint8Array(padding(file.data.length)));
40
+ }
41
+ if (optionsArg.endOfArchive) {
42
+ parts.push(new Uint8Array(BLOCK * 2));
43
+ }
44
+ return concatBytes(parts);
45
+ }
46
+ /**
47
+ * The bytes Go `Writer.WriteHeader` writes for a regular file: the main
48
+ * header block, preceded by a PAX `x` header and its padded records when the
49
+ * name needs one.
50
+ */
51
+ export function goTarHeader(nameArg, modeArg, sizeArg) {
52
+ if (!nameArg.isWellFormed()) {
53
+ throw new LinuxRepoError('invalid-input', 'a tar entry name is not well-formed UTF-16', { field: 'name' });
54
+ }
55
+ if (nameArg.includes('\0')) {
56
+ throw new LinuxRepoError('invalid-input', 'a tar entry name contains NUL', { field: 'name' });
57
+ }
58
+ if (nameArg.endsWith('/')) {
59
+ throw new LinuxRepoError('invalid-input', 'a regular tar entry name ends with /', { field: 'name' });
60
+ }
61
+ if (!Number.isSafeInteger(modeArg) || modeArg < 0 || modeArg >= MODE_LIMIT) {
62
+ throw new LinuxRepoError('invalid-input', 'a tar entry mode does not fit the USTAR mode field', {
63
+ field: 'mode',
64
+ });
65
+ }
66
+ if (!Number.isSafeInteger(sizeArg) || sizeArg < 0 || sizeArg >= SIZE_LIMIT) {
67
+ throw new LinuxRepoError('invalid-input', 'a tar entry size does not fit the USTAR size field', {
68
+ field: 'size',
69
+ });
70
+ }
71
+ const nameBytes = encoder.encode(nameArg);
72
+ if (isAscii(nameBytes) && nameBytes.length <= NAME_SIZE) {
73
+ return header({ name: nameBytes, prefix: null, size: sizeArg, mode: modeArg, typeflag: '0' });
74
+ }
75
+ const split = splitUstarPath(nameBytes);
76
+ if (split !== null) {
77
+ return header({ name: split.name, prefix: split.prefix, size: sizeArg, mode: modeArg, typeflag: '0' });
78
+ }
79
+ return paxHeader(nameArg, modeArg, sizeArg);
80
+ }
81
+ /** Go `splitUSTARPath` (`archive/tar/writer.go:454`). */
82
+ function splitUstarPath(nameArg) {
83
+ let length = nameArg.length;
84
+ if (length <= NAME_SIZE || !isAscii(nameArg)) {
85
+ return null;
86
+ }
87
+ else if (length > PREFIX_SIZE + 1) {
88
+ length = PREFIX_SIZE + 1;
89
+ }
90
+ else if (nameArg[length - 1] === 0x2f) {
91
+ length--;
92
+ }
93
+ const i = nameArg.subarray(0, length).lastIndexOf(0x2f);
94
+ const nameLength = nameArg.length - i - 1;
95
+ const prefixLength = i;
96
+ if (i <= 0 || nameLength > NAME_SIZE || nameLength === 0 || prefixLength > PREFIX_SIZE) {
97
+ return null;
98
+ }
99
+ return { prefix: nameArg.subarray(0, i), name: nameArg.subarray(i + 1) };
100
+ }
101
+ /**
102
+ * Go `writePAXHeader` (`archive/tar/writer.go:131`) for one `path` record:
103
+ * the `x` header written by `writeRawFile`, the records, their padding, then
104
+ * the main header with the ASCII-folded name.
105
+ */
106
+ function paxHeader(nameArg, modeArg, sizeArg) {
107
+ const records = paxRecord('path', nameArg);
108
+ if (records.length > MAX_SPECIAL_FILE_SIZE) {
109
+ throw new LinuxRepoError('invalid-input', 'a tar entry name is longer than Go archive/tar writes', {
110
+ field: 'name',
111
+ });
112
+ }
113
+ // path.Split, then path.Join(dir, "PaxHeaders.0", file); writeRawFile folds
114
+ // it to ASCII, cuts it to 100 bytes and trims trailing slashes
115
+ const slash = nameArg.lastIndexOf('/');
116
+ const dir = nameArg.slice(0, slash + 1);
117
+ const file = nameArg.slice(slash + 1);
118
+ let paxName = encoder.encode(toAscii(goPathJoin([dir, 'PaxHeaders.0', file])));
119
+ if (paxName.length > NAME_SIZE) {
120
+ paxName = paxName.subarray(0, NAME_SIZE);
121
+ }
122
+ let end = paxName.length;
123
+ while (end > 0 && paxName[end - 1] === 0x2f) {
124
+ end--;
125
+ }
126
+ return concatBytes([
127
+ header({ name: paxName.subarray(0, end), prefix: null, size: records.length, mode: 0, typeflag: 'x', raw: true }),
128
+ records,
129
+ new Uint8Array(padding(records.length)),
130
+ header({ name: encoder.encode(toAscii(nameArg)), prefix: null, size: sizeArg, mode: modeArg, typeflag: '0' }),
131
+ ]);
132
+ }
133
+ /** Go `formatPAXRecord` (`archive/tar/strconv.go:291`). */
134
+ function paxRecord(keyArg, valueArg) {
135
+ const payload = encoder.encode(` ${keyArg}=${valueArg}\n`);
136
+ let size = payload.length;
137
+ size += String(size).length;
138
+ let record = concatBytes([encoder.encode(String(size)), payload]);
139
+ if (record.length !== size) {
140
+ size = record.length;
141
+ record = concatBytes([encoder.encode(String(size)), payload]);
142
+ }
143
+ return record;
144
+ }
145
+ /** Go `templateV7Plus` plus `setFormat` (`archive/tar/writer.go:323`, `format.go:205`). */
146
+ function header(specArg) {
147
+ const block = new Uint8Array(BLOCK);
148
+ formatString(block, 0, NAME_SIZE, specArg.name);
149
+ formatOctal(block, 100, 8, specArg.mode);
150
+ formatOctal(block, 108, 8, 0); // uid
151
+ formatOctal(block, 116, 8, 0); // gid
152
+ formatOctal(block, 124, 12, specArg.size);
153
+ formatOctal(block, 136, 12, 0); // mtime: the zero time.Time is written as 0
154
+ block[156] = specArg.typeflag.charCodeAt(0);
155
+ // linkname, uname and gname stay empty
156
+ if (!specArg.raw) {
157
+ formatOctal(block, 329, 8, 0); // devmajor
158
+ formatOctal(block, 337, 8, 0); // devminor
159
+ }
160
+ if (specArg.prefix !== null) {
161
+ formatString(block, 345, PREFIX_SIZE, specArg.prefix);
162
+ }
163
+ block.set(encoder.encode('ustar\0'), 257);
164
+ block.set(encoder.encode('00'), 263);
165
+ let sum = 0;
166
+ for (let i = 0; i < BLOCK; i++) {
167
+ sum += i >= 148 && i < 156 ? 0x20 : block[i];
168
+ }
169
+ formatOctal(block, 148, 7, sum);
170
+ block[155] = 0x20;
171
+ return block;
172
+ }
173
+ /** Go `formatter.formatString` (`archive/tar/strconv.go:63`): copy, NUL-terminate when shorter, cut when longer. */
174
+ function formatString(blockArg, offsetArg, lengthArg, valueArg) {
175
+ const field = blockArg.subarray(offsetArg, offsetArg + lengthArg);
176
+ field.set(valueArg.subarray(0, lengthArg));
177
+ if (valueArg.length < lengthArg) {
178
+ field[valueArg.length] = 0;
179
+ }
180
+ if (valueArg.length > lengthArg && field[lengthArg - 1] === 0x2f) {
181
+ let n = lengthArg - 1;
182
+ while (n > 0 && valueArg[n - 1] === 0x2f) {
183
+ n--;
184
+ }
185
+ field[n] = 0;
186
+ }
187
+ }
188
+ /**
189
+ * Go `formatter.formatOctal` (`archive/tar/strconv.go:176`): zero-padded octal
190
+ * leaving room for a NUL. Callers check the range first; Go would write 0.
191
+ */
192
+ function formatOctal(blockArg, offsetArg, lengthArg, valueArg) {
193
+ const digits = valueArg.toString(8).padStart(lengthArg - 1, '0');
194
+ formatString(blockArg, offsetArg, lengthArg, encoder.encode(digits));
195
+ }
196
+ /** Go `toASCII` (`archive/tar/strconv.go:32`): drops every non-ASCII rune (NUL is refused earlier). */
197
+ function toAscii(valueArg) {
198
+ let result = '';
199
+ for (const char of valueArg) {
200
+ if (char.codePointAt(0) < 0x80) {
201
+ result += char;
202
+ }
203
+ }
204
+ return result;
205
+ }
206
+ /** Go `isASCII` (`archive/tar/strconv.go:21`) for a name without NUL. */
207
+ function isAscii(bytesArg) {
208
+ for (let i = 0; i < bytesArg.length; i++) {
209
+ if (bytesArg[i] >= 0x80) {
210
+ return false;
211
+ }
212
+ }
213
+ return true;
214
+ }
215
+ function padding(sizeArg) {
216
+ return (BLOCK - (sizeArg % BLOCK)) % BLOCK;
217
+ }
218
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ290YXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy93cml0ZXJzL2dvdGFyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNsRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDckQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBa0JqRCxNQUFNLEtBQUssR0FBRyxHQUFHLENBQUM7QUFDbEIsTUFBTSxTQUFTLEdBQUcsR0FBRyxDQUFDO0FBQ3RCLE1BQU0sV0FBVyxHQUFHLEdBQUcsQ0FBQztBQUN4QixnRkFBZ0Y7QUFDaEYsTUFBTSxxQkFBcUIsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO0FBQ3RDLDREQUE0RDtBQUM1RCxNQUFNLFVBQVUsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQzFCLHNFQUFzRTtBQUN0RSxNQUFNLFVBQVUsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO0FBQzNCLE1BQU0sT0FBTyxHQUFHLElBQUksV0FBVyxFQUFFLENBQUM7QUFFbEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXFCRztBQUNILE1BQU0sVUFBVSxVQUFVLENBQ3hCLFFBQStCLEVBQy9CLFVBQVUsR0FBdUIsRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFO0lBRXZELE1BQU0sS0FBSyxHQUFpQixFQUFFLENBQUM7SUFDL0IsS0FBSyxNQUFNLElBQUksSUFBSSxRQUFRLEVBQUUsQ0FBQztRQUM1QixLQUFLLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxJQUFJLENBQUMsSUFBSSxFQUFFLElBQUksVUFBVSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FB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
package/license.md ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2026 Task Venture Capital GmbH (hello@task.vc)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
package/package.json CHANGED
@@ -1,6 +1,59 @@
1
1
  {
2
2
  "name": "@push.rocks/smartlinuxrepo",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "description": "A pure codec for Linux package repositories (Alpine apk, Debian apt, RPM dnf, Arch pacman): read packages into records, build index file sets and sign them.",
6
+ "main": "dist_ts/index.js",
7
+ "typings": "dist_ts/index.d.ts",
8
+ "exports": {
9
+ ".": "./dist_ts/index.js"
10
+ },
11
+ "author": "Task Venture Capital GmbH",
12
+ "license": "MIT",
13
+ "engines": {
14
+ "node": "^24.20.0 || >=26.6.0"
15
+ },
16
+ "devDependencies": {
17
+ "@git.zone/tsrepo": "^2.1.0",
18
+ "@openpgp/web-stream-tools": "0.3.1",
19
+ "@types/node": "^26.6.6"
20
+ },
21
+ "dependencies": {
22
+ "@napi-rs/lzma": "1.5.3",
23
+ "openpgp": "6.3.2"
24
+ },
25
+ "files": [
26
+ "ts/**/*",
27
+ "dist_ts/**/*",
28
+ ".smartconfig.json",
29
+ "license.md",
30
+ "readme.md"
31
+ ],
32
+ "type": "module",
33
+ "sideEffects": false,
34
+ "keywords": [
35
+ "linux",
36
+ "package repository",
37
+ "alpine",
38
+ "apk",
39
+ "debian",
40
+ "apt",
41
+ "rpm",
42
+ "dnf",
43
+ "arch",
44
+ "pacman",
45
+ "openpgp",
46
+ "codec"
47
+ ],
48
+ "homepage": "https://code.foss.global/push.rocks/smartlinuxrepo",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://code.foss.global/push.rocks/smartlinuxrepo.git"
52
+ },
53
+ "scripts": {
54
+ "test": "tsrepo test test/",
55
+ "test:gate": "tsrepo test 'test/gate/*.gate.node.ts'",
56
+ "build": "tsrepo build tsfolders",
57
+ "buildDocs": "tsrepo docs"
58
+ }
6
59
  }
package/readme.md ADDED
@@ -0,0 +1,176 @@
1
+ # @push.rocks/smartlinuxrepo
2
+
3
+ A pure codec for Linux package repositories: Alpine (apk), Debian (apt), RPM (dnf) and Arch (pacman). It reads package files into JSON records, builds repository index file sets and signs them, following Gitea 28.2.0. It does no storage and no network I/O; the caller owns both.
4
+
5
+ ## Issue Reporting and Security
6
+
7
+ For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ pnpm add @push.rocks/smartlinuxrepo
13
+ ```
14
+
15
+ Node.js `^24.20.0 || >=26.6.0`: the releases whose `node:zlib` reports a truncated zstd frame (nodejs/node#64593). On an older Node, opening a zstd decoder throws a plain `Error`.
16
+
17
+ ## Status
18
+
19
+ All four formats read, index and sign: `readAlpinePackage`, `readDebianPackage`, `readRpmPackage` and `readArchPackage` read packages into records; `buildAlpineIndex`, `buildDebianIndex`, `buildRpmIndex` and `buildArchIndex` build signed index file sets; `signRpmPackage` and `signArchPackage` sign packages at upload. Each format is checked against Gitea 28.2.0's own output and installed from with apk, apt, dnf and pacman. The built-in OpenPGP signer does not stream: openpgp.js 6.3.2 holds a detached-signed document in memory (see Signing).
20
+
21
+ ## Records
22
+
23
+ `TLinuxPackageRecord` is a union of `IAlpinePackageRecord`, `IDebianPackageRecord`, `IRpmPackageRecord` and `IArchPackageRecord`, discriminated by `format`. Every record carries `name`, `version`, `architecture`, `fileName`, `digests` (size, md5, sha1, sha256, sha512 over the whole file; Alpine adds `controlSha1`) and a format-specific `metadata` object. Records are plain JSON and safe to persist; changing their shape is a major release.
24
+
25
+ ## Limits and errors
26
+
27
+ Readers take `Partial<ILinuxPackageLimits>` per call over `DEFAULT_LINUX_PACKAGE_LIMITS`:
28
+
29
+ | Limit | Default | Bounds |
30
+ | --- | --- | --- |
31
+ | `maxPackageBytes` | 8 GiB | the whole input |
32
+ | `maxMetadataBytes` | 64 MiB | one buffered metadata unit (apk or deb control member, RPM headers) |
33
+ | `maxDecompressedMetadataBytes` | 32 MiB | a decompressed control tar or `.PKGINFO` |
34
+ | `maxPayloadBytes` | 16 GiB | the decompressed Arch payload walked for the file list |
35
+ | `maxEntries` | 100,000 | archive entries, values per multi-valued field |
36
+ | `maxFileListBytes` | 16 MiB | total Arch file-name bytes |
37
+ | `maxHeaderBytes` | 4 MiB | one tar header chain or metadata line |
38
+
39
+ Every failure is a `LinuxRepoError` with a stable `code` (`limit-exceeded`, `unsupported-compression`, `malformed-archive`, `malformed-header`, `missing-field`, `invalid-field`, `unsupported-package-format`, `truncated`, `digest-mismatch`, `invalid-input`, `invalid-key`, `signing-failed`) and a `detail` naming the field or limit. Errors from your own source or signer, and failures inside node:crypto or openpgp.js during key generation, are passed through unchanged.
40
+
41
+ ## Signing
42
+
43
+ The codec never stores keys. You pass a signer on every call:
44
+
45
+ ```ts
46
+ import {
47
+ createAlpineSigner,
48
+ createOpenPgpSigner,
49
+ generateAlpineKeyPair,
50
+ generateOpenPgpKeyPair,
51
+ giteaAlpineKeyName,
52
+ } from '@push.rocks/smartlinuxrepo';
53
+
54
+ const alpineKeys = await generateAlpineKeyPair(); // RSA-4096, PKCS#1 private + PKIX public PEM
55
+ const alpineSigner = createAlpineSigner({
56
+ privateKeyPem: alpineKeys.privateKeyPem,
57
+ keyName: giteaAlpineKeyName('my-org', alpineKeys.publicKeyPem), // my-org@<sha256>.rsa.pub
58
+ });
59
+
60
+ const pgpKeys = await generateOpenPgpKeyPair({ userId: 'Example Repository Key', timestamp: new Date() });
61
+ const pgpSigner = await createOpenPgpSigner({ privateKeyArmored: pgpKeys.privateKeyArmored });
62
+ ```
63
+
64
+ `IAlpineSigner.sign` returns an RSA PKCS#1 v1.5 SHA-1 signature. `IOpenPgpSigner.signBinary` and `signText` return one raw OpenPGP v4 signature packet (SHA-256, created at the given timestamp). `signBinary` takes the document as an `AsyncIterable<Uint8Array>` and signs the concatenation of its chunks, reading it to its end exactly once, so a whole package can be signed while it streams; pass bytes you already hold as a single chunk. The built-in signer from `createOpenPgpSigner` does not stream yet: openpgp.js 6.3.2 keeps every chunk of a detached-signed stream and then concatenates them, so signing peaks at about twice the document size in memory. A custom `IOpenPgpSigner` implementation can stream. The codec does all armoring and cleartext framing, so any implementation of the interface (an HSM, a remote signer, a test stub) produces identically framed files. `createOpenPgpSigner` accepts unencrypted v4 keys only.
65
+
66
+ `isIndexableArchitecture(architecture)` tells whether an architecture can name an index path; builders refuse entries that fail it.
67
+
68
+ Every builder returns the complete file set for its input as `ILinuxIndexFile[]` (`path`, `bytes`, `sha256`, `mediaType`, `stage`, `aliases`); a previously written file that is not in the set is stale. Write every `content` file before any `pointer` file: the pointer files are the signed documents that name the content files.
69
+
70
+ ## Alpine
71
+
72
+ ```ts
73
+ import { buildAlpineIndex, readAlpinePackage } from '@push.rocks/smartlinuxrepo';
74
+
75
+ // upload() returns a fresh AsyncIterable<Uint8Array> of the package bytes; limits are optional
76
+ const record = await readAlpinePackage(upload(), limits);
77
+ const files = await buildAlpineIndex({
78
+ branch: 'v3.22',
79
+ repository: 'main',
80
+ entries: [record], // every package of this branch and repository
81
+ signer: alpineSigner,
82
+ timestamp: new Date(),
83
+ });
84
+ ```
85
+
86
+ `readAlpinePackage` reads apk v2 packages, signed or unsigned; an apk v3 (ADB) package is `unsupported-package-format`, as in Gitea. The record's `fileName` is `<name>-<version>.apk`, and `digests.controlSha1` is the SHA-1 of the whole control gzip member, which is what apk verifies (the index's `C:` value).
87
+
88
+ For every architecture the entries name, `buildAlpineIndex` returns one file, `<branch>/<repository>/<architecture>/APKINDEX.tar.gz`: a gzip member with the signature entry `.SIGN.RSA.<keyName>` (the signer's `keyName`), followed by the gzip member holding `APKINDEX`. Packages of architecture `noarch` are also listed in every other architecture's index, and every entry's `A:` is the index's architecture, so apk requests a `noarch` package under the architecture's path. Your route serves package files at `<branch>/<repository>/<architecture>/<fileName>` and, as Gitea 28.2.0 does:
89
+
90
+ - A package file or `APKINDEX.tar.gz` that does not exist under the requested architecture is looked up under `noarch`. A repository with only `noarch` packages has no other index, so apk gets the `noarch` one.
91
+ - apk finds the public key by the signature entry's name: users install it as `/etc/apk/keys/<keyName>`. `giteaAlpineKeyName` gives Gitea's name, `<owner>@<sha256 of the public key>.rsa.pub`.
92
+
93
+ ## Debian
94
+
95
+ ```ts
96
+ import { buildDebianIndex, readDebianPackage } from '@push.rocks/smartlinuxrepo';
97
+
98
+ // upload() returns a fresh AsyncIterable<Uint8Array> of the package bytes; limits are optional
99
+ const record = await readDebianPackage(upload(), limits);
100
+ const files = await buildDebianIndex({
101
+ distribution: 'bookworm',
102
+ entries: [{ component: 'main', record }], // every package of the distribution
103
+ origin: 'Example',
104
+ label: 'Example',
105
+ signer: pgpSigner,
106
+ timestamp: new Date(),
107
+ });
108
+ ```
109
+
110
+ `readDebianPackage` reads the `control.tar` member (plain, `.gz`, `.xz` or `.zst`) and hashes the rest of the file without decompressing it. The record keeps Gitea's derived values exactly, and its `fileName` is `<name>_<version>_<architecture>.deb`.
111
+
112
+ `buildDebianIndex` returns the complete set for one distribution. For every component and architecture with packages it writes `dists/<distribution>/<component>/binary-<architecture>/Packages`, `Packages.gz` and `Packages.xz` (`content` files). Packages of architecture `all` are listed only under `binary-all`, as in Gitea. Then it writes `dists/<distribution>/Release`, its armored detached signature `Release.gpg` and the cleartext-signed `InRelease` (`pointer` files). `distribution` and `component` must match `^[a-zA-Z0-9][a-zA-Z0-9.~+_-]*$`; `origin` and `label` are each one non-empty line without surrounding whitespace. Your route serves:
113
+
114
+ - every file at its `path` and at each of its `aliases`, `by-hash/MD5Sum`, `SHA1`, `SHA256` and `SHA512/<hex>` next to the `Packages` file. `Release` sets `Acquire-By-Hash: yes`, and apt 2.6 and 3.x fetch `Packages.xz` through `by-hash/SHA512/<hex>`;
115
+ - each package file at `pool/<distribution>/<component>/<fileName>`, the `Filename:` its stanza names;
116
+ - the public key (`publicKeyArmored` from `generateOpenPgpKeyPair`), which users install as `/etc/apt/keyrings/<name>.asc` and reference with `signed-by`.
117
+
118
+ ## RPM
119
+
120
+ ```ts
121
+ import { buildRpmIndex, mergeRpmUpdates, readRpmPackage, renderRpmRepoConfig, signRpmPackage } from '@push.rocks/smartlinuxrepo';
122
+
123
+ // optional upload signing, as Gitea's ?sign=true: tags 1002 (header + payload) and 268 (header)
124
+ const { prefix, resumeAt } = await signRpmPackage({ source: packageStream(), signer: pgpSigner, timestamp: new Date() });
125
+ // the signed package is `prefix` followed by the original bytes from `resumeAt`
126
+
127
+ const record = await readRpmPackage(signedPackageStream());
128
+ const files = await buildRpmIndex({
129
+ group: 'el/9', // '' for the root group
130
+ entries: [record],
131
+ updates: [], // IRpmUpdate[] of every version with a file in the group
132
+ repodataNaming: 'plain', // Gitea's repodata/<type>.xml.gz, or 'content-addressed' for <sha256>-<type>.xml.gz
133
+ signer: pgpSigner,
134
+ timestamp: new Date(),
135
+ });
136
+ ```
137
+
138
+ `buildRpmIndex` returns `primary`, `filelists`, `other` (and `updateinfo` when there are updates) as `.xml.gz` content files, then `repomd.xml` and `repomd.xml.asc`. The documents follow Gitea 28.2.0 with three corrections: the `filelists` namespace is `http://linux.duke.edu/metadata/filelists`, `primary` also lists the paths createrepo_c's `cr_is_primary` selects (`/etc/…`, `*bin/*`, `/usr/lib/sendmail`), and every document lists a source RPM's architecture as `src` (its package path keeps the ARCH tag). `readRpmPackage` refuses with `invalid-field` a NAME or `[EPOCH-]VERSION-RELEASE` that cannot name a path segment (empty, `.`, `..`, `/` or a control character), which Gitea does not check. `mergeRpmUpdates` merges posted errata as Gitea's errata route does; `renderRpmRepoConfig` writes Gitea's `.repo` text, optionally with `repo_gpgcheck`.
139
+
140
+ ## Arch
141
+
142
+ ```ts
143
+ import { buildArchIndex, readArchPackage, signArchPackage } from '@push.rocks/smartlinuxrepo';
144
+
145
+ // upload() returns a fresh AsyncIterable<Uint8Array> of the package bytes; limits are optional
146
+ const record = await readArchPackage(upload(), limits);
147
+ const packageSignature = await signArchPackage({ source: upload(), signer: pgpSigner, timestamp: new Date(), limits });
148
+ const files = await buildArchIndex({ repository: 'core', entries: [{ record, packageSignature }], signer: pgpSigner, timestamp: new Date() });
149
+ ```
150
+
151
+ Pass `signArchPackage` the limits the package was read with. Store its result with the package: it is the package's `.sig` file, and the builder writes it into the index as `%PGPSIG%`. An entry without one has no `%PGPSIG%`, and pacman fetches `<file>.sig` instead.
152
+
153
+ For every architecture that has packages, `buildArchIndex` returns two files: `<repository>/<architecture>/packages.db` (a gzip tar) and its signature `packages.db.sig`. Packages of architecture `any` also go into every other architecture's db. The files have no aliases. Your route maps request names onto them, as Gitea 28.2.0 does for `GET <repository>/<architecture>/<filename>`:
154
+
155
+ - A trailing `.sig` asks for the signature of the rest of the name.
156
+ - A name ending in `.db`, `.db.tar.gz`, `.files` or `.files.tar.gz` means `packages.db`. pacman requests `<repo>.db`, where `<repo>` is the repository's name in `pacman.conf`.
157
+ - A name that does not exist under the requested architecture is looked up under `any`. Gitea does this for every name, so a package uploaded as `any` downloads from every architecture's URL.
158
+
159
+ ## License and Legal Information
160
+
161
+ This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license.md) file within this repository.
162
+
163
+ **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
164
+
165
+ ### Trademarks
166
+
167
+ This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
168
+
169
+ ### Company Information
170
+
171
+ Task Venture Capital GmbH
172
+ Registered at District court Bremen HRB 35230 HB, Germany
173
+
174
+ For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
175
+
176
+ By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@push.rocks/smartlinuxrepo',
6
+ version: '1.0.0',
7
+ description: 'A pure codec for Linux package repositories (Alpine apk, Debian apt, RPM dnf, Arch pacman): read packages into records, build index file sets and sign them.'
8
+ }