cyclops-infobook-html 5.1.0 → 5.2.1

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 (52) hide show
  1. package/README.md +12 -12
  2. package/bin/compress-icons.js +42 -7
  3. package/bin/convert-pom-to-modpack.d.ts +2 -0
  4. package/bin/convert-pom-to-modpack.js +96 -0
  5. package/bin/generate-icons.js +50 -15
  6. package/bin/generate-infobook-html.js +46 -10
  7. package/bin/generate-mod-metadata.js +52 -14
  8. package/index.d.ts +25 -25
  9. package/lib/icon/IconsCompressor.js +43 -12
  10. package/lib/icon/IconsGenerator.d.ts +42 -20
  11. package/lib/icon/IconsGenerator.js +156 -70
  12. package/lib/infobook/FileWriter.d.ts +3 -3
  13. package/lib/infobook/FileWriter.js +5 -5
  14. package/lib/infobook/IFileWriter.d.ts +2 -2
  15. package/lib/infobook/IInfoAppendix.d.ts +3 -3
  16. package/lib/infobook/IInfoBook.d.ts +2 -4
  17. package/lib/infobook/IInfoSection.d.ts +1 -1
  18. package/lib/infobook/IInfobookPlugin.d.ts +5 -5
  19. package/lib/infobook/InfoBookInitializer.d.ts +7 -9
  20. package/lib/infobook/InfoBookInitializer.js +1 -1
  21. package/lib/infobook/appendix/IInfoBookAppendixHandler.d.ts +2 -2
  22. package/lib/infobook/appendix/InfoBookAppendixAd.d.ts +3 -3
  23. package/lib/infobook/appendix/InfoBookAppendixAd.js +7 -2
  24. package/lib/infobook/appendix/InfoBookAppendixHandlerAbstractRecipe.d.ts +12 -14
  25. package/lib/infobook/appendix/InfoBookAppendixHandlerAbstractRecipe.js +41 -8
  26. package/lib/infobook/appendix/InfoBookAppendixHandlerAdvancementRewards.d.ts +3 -3
  27. package/lib/infobook/appendix/InfoBookAppendixHandlerAdvancementRewards.js +5 -4
  28. package/lib/infobook/appendix/InfoBookAppendixHandlerCraftingRecipe.d.ts +6 -5
  29. package/lib/infobook/appendix/InfoBookAppendixHandlerCraftingRecipe.js +4 -3
  30. package/lib/infobook/appendix/InfoBookAppendixHandlerImage.d.ts +3 -3
  31. package/lib/infobook/appendix/InfoBookAppendixHandlerImage.js +4 -4
  32. package/lib/infobook/appendix/InfoBookAppendixHandlerKeybinding.d.ts +3 -3
  33. package/lib/infobook/appendix/InfoBookAppendixHandlerKeybinding.js +3 -2
  34. package/lib/infobook/appendix/InfoBookAppendixHandlerSmeltingRecipe.d.ts +6 -5
  35. package/lib/infobook/appendix/InfoBookAppendixHandlerSmeltingRecipe.js +3 -2
  36. package/lib/infobook/appendix/InfoBookAppendixHandlerTextfield.d.ts +3 -3
  37. package/lib/infobook/appendix/InfoBookAppendixHandlerTextfield.js +4 -5
  38. package/lib/infobook/appendix/InfoBookAppendixTagIndex.d.ts +4 -4
  39. package/lib/infobook/appendix/InfoBookAppendixTagIndex.js +2 -1
  40. package/lib/modloader/ModLoader.d.ts +13 -2
  41. package/lib/modloader/ModLoader.js +164 -69
  42. package/lib/modloader/PomConverter.d.ts +28 -0
  43. package/lib/modloader/PomConverter.js +194 -0
  44. package/lib/parse/XmlInfoBookParser.d.ts +5 -7
  45. package/lib/parse/XmlInfoBookParser.js +42 -9
  46. package/lib/resource/ResourceHandler.d.ts +7 -13
  47. package/lib/resource/ResourceHandler.js +16 -11
  48. package/lib/resource/ResourceLoader.d.ts +4 -5
  49. package/lib/resource/ResourceLoader.js +35 -33
  50. package/lib/serialize/HtmlInfoBookSerializer.d.ts +10 -16
  51. package/lib/serialize/HtmlInfoBookSerializer.js +83 -80
  52. package/package.json +33 -27
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
36
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
37
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -8,19 +41,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
41
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
42
  });
10
43
  };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
11
47
  Object.defineProperty(exports, "__esModule", { value: true });
12
48
  exports.ModLoader = void 0;
13
- const child_process_1 = require("child_process");
14
- const fs_1 = require("fs");
15
- const fs = require("fs");
16
- const mvn_artifact_download_1 = require("mvn-artifact-download");
49
+ const node_child_process_1 = require("node:child_process");
50
+ const node_fs_1 = require("node:fs");
51
+ const fs = __importStar(require("node:fs"));
52
+ const Path = __importStar(require("node:path"));
53
+ const node_path_1 = require("node:path");
54
+ const node_util_1 = require("node:util");
55
+ const mvn_artifact_download_1 = __importDefault(require("mvn-artifact-download"));
17
56
  const ncp_1 = require("ncp");
18
- const node_fetch_1 = require("node-fetch");
19
- const path_1 = require("path");
20
- const rimraf = require("rimraf");
21
- const util_1 = require("util");
57
+ const node_fetch_1 = __importDefault(require("node-fetch"));
58
+ const rimraf_1 = __importDefault(require("rimraf"));
22
59
  const yauzl_1 = require("yauzl");
23
- const Path = require("path");
24
60
  /**
25
61
  * Takes care of installing Forge, installing mods, starting a Forge server, and fetching metadata.
26
62
  */
@@ -54,13 +90,19 @@ class ModLoader {
54
90
  if (!res.ok) {
55
91
  throw new Error(`Failed to fetch (${res.statusText}): ${forgeInstaller}`);
56
92
  }
57
- installerFile = (0, path_1.join)(this.path, 'forge-installer.jar');
58
- yield new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
59
- return fs.writeFile(installerFile, yield res.buffer(), (err) => err ? reject(err) : resolve());
93
+ installerFile = (0, node_path_1.join)(this.path, 'forge-installer.jar');
94
+ const forgeBuffer = yield res.buffer();
95
+ yield new Promise((resolve, reject) => fs.writeFile(installerFile, forgeBuffer, (err) => {
96
+ if (err) {
97
+ reject(err);
98
+ }
99
+ else {
100
+ resolve();
101
+ }
60
102
  }));
61
103
  // Install Forge
62
104
  process.stdout.write('Installing Forge...\n');
63
- yield new Promise((resolve, reject) => (0, child_process_1.exec)(`cd ${this.path} && java -jar forge-installer.jar --installServer`).on('exit', resolve));
105
+ yield new Promise(resolve => (0, node_child_process_1.exec)(`cd ${this.path} && java -jar forge-installer.jar --installServer`).on('exit', resolve));
64
106
  }
65
107
  else {
66
108
  // Download NeoForge installer
@@ -70,21 +112,27 @@ class ModLoader {
70
112
  if (!res.ok) {
71
113
  throw new Error(`Failed to fetch (${res.statusText}): ${installer}`);
72
114
  }
73
- installerFile = (0, path_1.join)(this.path, 'neoforge-installer.jar');
74
- yield new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
75
- return fs.writeFile(installerFile, yield res.buffer(), (err) => err ? reject(err) : resolve());
115
+ installerFile = (0, node_path_1.join)(this.path, 'neoforge-installer.jar');
116
+ const neoBuffer = yield res.buffer();
117
+ yield new Promise((resolve, reject) => fs.writeFile(installerFile, neoBuffer, (err) => {
118
+ if (err) {
119
+ reject(err);
120
+ }
121
+ else {
122
+ resolve();
123
+ }
76
124
  }));
77
125
  // Install Forge
78
126
  process.stdout.write('Installing NeoForge...\n');
79
- yield new Promise((resolve, reject) => (0, child_process_1.exec)(`cd ${this.path} && java -jar neoforge-installer.jar --installServer`).on('exit', resolve));
127
+ yield new Promise(resolve => (0, node_child_process_1.exec)(`cd ${this.path} && java -jar neoforge-installer.jar --installServer`).on('exit', resolve));
80
128
  }
81
129
  // Wait a bit, because otherwise some files don't exist yet (while they should...)
82
130
  process.stdout.write('Wait a bit after mod loader installation...\n');
83
- yield new Promise((resolve) => setTimeout(resolve, 10000));
131
+ yield new Promise(resolve => setTimeout(resolve, 10000));
84
132
  // Cleanup
85
133
  process.stdout.write('Cleaning up...\n');
86
134
  yield fs.promises.unlink(installerFile);
87
- yield fs.promises.unlink(installerFile + '.log');
135
+ yield fs.promises.unlink(`${installerFile}.log`);
88
136
  });
89
137
  }
90
138
  /**
@@ -93,14 +141,14 @@ class ModLoader {
93
141
  acceptEula() {
94
142
  return __awaiter(this, void 0, void 0, function* () {
95
143
  process.stdout.write('Accepting EULA...\n');
96
- yield fs.promises.writeFile((0, path_1.join)(this.path, 'eula.txt'), 'eula=true');
144
+ yield fs.promises.writeFile((0, node_path_1.join)(this.path, 'eula.txt'), 'eula=true');
97
145
  });
98
146
  }
99
147
  /**
100
148
  * @returns {boolean} If mods are installed.
101
149
  */
102
150
  areModsInstalled() {
103
- return fs.existsSync((0, path_1.join)(this.path, 'mods'));
151
+ return fs.existsSync((0, node_path_1.join)(this.path, 'mods'));
104
152
  }
105
153
  /**
106
154
  * Download and install mods.
@@ -108,7 +156,7 @@ class ModLoader {
108
156
  installMods() {
109
157
  return __awaiter(this, void 0, void 0, function* () {
110
158
  process.stdout.write('Downloading mods...\n');
111
- const modsDir = (0, path_1.join)(this.path, 'mods');
159
+ const modsDir = (0, node_path_1.join)(this.path, 'mods');
112
160
  if (!fs.existsSync(modsDir)) {
113
161
  yield fs.promises.mkdir(modsDir);
114
162
  }
@@ -117,15 +165,22 @@ class ModLoader {
117
165
  const fileName = `${mod.artifact}-${mod.version}.jar`;
118
166
  process.stdout.write(` - ${fileName} from CurseForge...\n`);
119
167
  const url = `https://minecraft.curseforge.com/api/maven/${mod.project}/${mod.artifact
120
- .replace(/-/g, '/')}/${fileName}`;
168
+ .replaceAll('-', '/')}/${fileName}`;
121
169
  yield this.downloadFile(url, fileName, modsDir);
122
170
  }
123
171
  else if (mod.type === 'maven') {
124
172
  process.stdout.write(` - ${mod.artifact} from ${mod.repo}...\n`);
125
- const name = yield (0, mvn_artifact_download_1.default)(mod.artifact, modsDir, mod.repo);
173
+ let name;
174
+ if (mod.headers && Object.keys(mod.headers).length > 0) {
175
+ // Authenticated download: construct URL manually and use fetch with headers
176
+ name = yield this.downloadMavenArtifact(mod.artifact, mod.repo, modsDir, mod.headers);
177
+ }
178
+ else {
179
+ name = yield (0, mvn_artifact_download_1.default)(mod.artifact, modsDir, mod.repo);
180
+ }
126
181
  // Rename file if needed
127
182
  if ('name' in mod) {
128
- fs.renameSync(name, (0, path_1.join)(modsDir, mod.name));
183
+ fs.renameSync(name, (0, node_path_1.join)(modsDir, mod.name));
129
184
  }
130
185
  }
131
186
  else if (mod.type === 'raw') {
@@ -133,25 +188,64 @@ class ModLoader {
133
188
  yield this.downloadFile(mod.url, mod.name, modsDir);
134
189
  }
135
190
  else {
136
- throw new Error('Unknown mod type ' + mod.type);
191
+ throw new Error(`Unknown mod type ${mod.type}`);
137
192
  }
138
193
  }
139
194
  });
140
195
  }
141
- downloadFile(url, fileName, modsDir) {
196
+ downloadFile(url, fileName, modsDir, headers) {
142
197
  return __awaiter(this, void 0, void 0, function* () {
143
- const response = yield (0, node_fetch_1.default)(url);
198
+ const response = yield (0, node_fetch_1.default)(url, headers ? { headers } : undefined);
144
199
  if (response.status !== 200) {
145
- throw new Error(response.statusText + ' on ' + url);
200
+ throw new Error(`${response.statusText} on ${url}`);
146
201
  }
147
202
  yield new Promise((resolve, reject) => {
148
203
  response.body
149
204
  .on('error', reject)
150
205
  .on('end', resolve)
151
- .pipe(fs.createWriteStream((0, path_1.join)(modsDir, fileName)));
206
+ .pipe(fs.createWriteStream((0, node_path_1.join)(modsDir, fileName)));
152
207
  });
153
208
  });
154
209
  }
210
+ /**
211
+ * Download a Maven artifact by constructing the repository URL manually.
212
+ * This is used for authenticated repositories where custom headers are needed.
213
+ * @param artifact The Maven artifact coordinates (groupId:artifactId:version[:classifier]).
214
+ * @param repoUrl The base URL of the Maven repository.
215
+ * @param modsDir The directory to save the downloaded file.
216
+ * @param headers Optional HTTP headers (e.g., Authorization).
217
+ * @returns The full path to the downloaded file.
218
+ */
219
+ downloadMavenArtifact(artifact, repoUrl, modsDir, headers) {
220
+ return __awaiter(this, void 0, void 0, function* () {
221
+ const parts = artifact.split(':');
222
+ if (parts.length < 3) {
223
+ throw new Error(`Invalid Maven artifact: ${artifact}. Expected format: groupId:artifactId:version[:classifier]`);
224
+ }
225
+ const [groupId, artifactId, version, classifier] = parts;
226
+ const groupPath = groupId.replaceAll('.', '/');
227
+ const suffix = classifier ? `-${classifier}` : '';
228
+ const fileName = `${artifactId}-${version}${suffix}.jar`;
229
+ const base = repoUrl.endsWith('/') ? repoUrl : `${repoUrl}/`;
230
+ const url = `${base}${groupPath}/${artifactId}/${version}/${fileName}`;
231
+ // Expand environment variable placeholders in header values (e.g. ${GITHUB_TOKEN})
232
+ const resolvedHeaders = {};
233
+ if (headers) {
234
+ for (const [key, value] of Object.entries(headers)) {
235
+ resolvedHeaders[key] = value.replaceAll(/\$\{([^}]+)\}/gu, (fullMatch, name) => {
236
+ const envValue = process.env[name];
237
+ if (envValue === undefined) {
238
+ process.stderr.write(`Warning: environment variable '${name}' is not set; the request header '${key}' may be invalid\n`);
239
+ return fullMatch;
240
+ }
241
+ return envValue;
242
+ });
243
+ }
244
+ }
245
+ yield this.downloadFile(url, fileName, modsDir, resolvedHeaders);
246
+ return (0, node_path_1.join)(modsDir, fileName);
247
+ });
248
+ }
155
249
  /**
156
250
  * Start the server and execute a command to dump all registries
157
251
  */
@@ -159,7 +253,7 @@ class ModLoader {
159
253
  return __awaiter(this, void 0, void 0, function* () {
160
254
  // Start the Forge server
161
255
  process.stdout.write('Starting server...\n');
162
- const proc = (0, child_process_1.exec)(`cd ${this.path} && ./run.sh nogui`);
256
+ const proc = (0, node_child_process_1.exec)(`cd ${this.path} && ./run.sh nogui`);
163
257
  // Ignore stdout: proc.stdout.pipe(process.stdout);
164
258
  proc.stderr.pipe(process.stderr);
165
259
  const onDone = new Promise((resolve, reject) => {
@@ -168,14 +262,14 @@ class ModLoader {
168
262
  resolve();
169
263
  }
170
264
  else {
171
- reject('Server closed with non-zero exit code');
265
+ reject(new Error('Server closed with non-zero exit code'));
172
266
  }
173
267
  });
174
268
  proc.addListener('error', reject);
175
269
  });
176
270
  // Once the loading is complete, send our command and stop the server
177
271
  proc.stdout.on('data', (line) => {
178
- if (line.indexOf('Done') >= 0 && line.indexOf('For help, type "help"') >= 0) {
272
+ if (line.includes('Done') && line.includes('For help, type "help"')) {
179
273
  process.stdout.write('Dumping registries...\n');
180
274
  this.sendCommand(proc, '/cyclopscore dumpregistries');
181
275
  this.sendCommand(proc, '/stop');
@@ -190,7 +284,7 @@ class ModLoader {
190
284
  * @param {string} command A command.
191
285
  */
192
286
  sendCommand(proc, command) {
193
- proc.stdin.write(command + '\n');
287
+ proc.stdin.write(`${command}\n`);
194
288
  }
195
289
  /**
196
290
  * Copy the resulting registry files to a target path.
@@ -199,10 +293,10 @@ class ModLoader {
199
293
  copyRegistries(target) {
200
294
  return __awaiter(this, void 0, void 0, function* () {
201
295
  process.stdout.write('Copying registries...\n');
202
- if (!fs.existsSync((0, path_1.join)(this.path, 'cyclops_registries'))) {
203
- yield fs.promises.mkdir((0, path_1.join)(this.path, 'cyclops_registries'));
296
+ if (!fs.existsSync((0, node_path_1.join)(this.path, 'cyclops_registries'))) {
297
+ yield fs.promises.mkdir((0, node_path_1.join)(this.path, 'cyclops_registries'));
204
298
  }
205
- yield (0, util_1.promisify)(ncp_1.ncp)((0, path_1.join)(this.path, 'cyclops_registries'), target);
299
+ yield (0, node_util_1.promisify)(ncp_1.ncp)((0, node_path_1.join)(this.path, 'cyclops_registries'), target);
206
300
  });
207
301
  }
208
302
  /**
@@ -211,8 +305,8 @@ class ModLoader {
211
305
  extractMinecraftAssets() {
212
306
  return __awaiter(this, void 0, void 0, function* () {
213
307
  process.stdout.write('Extracting minecraft assets...\n');
214
- if (!fs.existsSync((0, path_1.join)(this.path, 'mc_assets'))) {
215
- yield fs.promises.mkdir((0, path_1.join)(this.path, 'mc_assets'));
308
+ if (!fs.existsSync((0, node_path_1.join)(this.path, 'mc_assets'))) {
309
+ yield fs.promises.mkdir((0, node_path_1.join)(this.path, 'mc_assets'));
216
310
  }
217
311
  // Find Minecraft jar
218
312
  let jar = null;
@@ -221,14 +315,14 @@ class ModLoader {
221
315
  if (dir.indexOf('-') > 0) {
222
316
  for (const file of yield fs.promises.readdir(Path.join(subPath, dir))) {
223
317
  if (file.startsWith('server') && file.endsWith('extra.jar')) {
224
- jar = (0, path_1.join)(subPath, dir, file);
318
+ jar = (0, node_path_1.join)(subPath, dir, file);
225
319
  }
226
320
  }
227
321
  }
228
322
  }
229
323
  // Error if no jar was found
230
324
  if (!jar) {
231
- throw new Error('Could not find a valid minecraft server in ' + this.path);
325
+ throw new Error(`Could not find a valid minecraft server in ${this.path}`);
232
326
  }
233
327
  // Unzip the jar
234
328
  process.stdout.write(`Extracting Minecraft jar...\n`);
@@ -241,14 +335,14 @@ class ModLoader {
241
335
  extractModsAssets() {
242
336
  return __awaiter(this, void 0, void 0, function* () {
243
337
  process.stdout.write('Extracting mod assets...\n');
244
- if (!fs.existsSync((0, path_1.join)(this.path, 'mod_assets'))) {
245
- yield fs.promises.mkdir((0, path_1.join)(this.path, 'mod_assets'));
338
+ if (!fs.existsSync((0, node_path_1.join)(this.path, 'mod_assets'))) {
339
+ yield fs.promises.mkdir((0, node_path_1.join)(this.path, 'mod_assets'));
246
340
  }
247
341
  // Loop over all mods
248
- const modsDir = (0, path_1.join)(this.path, 'mods');
342
+ const modsDir = (0, node_path_1.join)(this.path, 'mods');
249
343
  for (const mod of yield fs.promises.readdir(modsDir)) {
250
344
  if (mod.endsWith('.jar')) {
251
- const modFile = (0, path_1.join)(modsDir, mod);
345
+ const modFile = (0, node_path_1.join)(modsDir, mod);
252
346
  process.stdout.write(` - ${mod}...\n`);
253
347
  yield this.extractModAssets(modFile);
254
348
  }
@@ -270,33 +364,34 @@ class ModLoader {
270
364
  });
271
365
  });
272
366
  zipFile.readEntry();
273
- zipFile.on('error', (e) => process.stdout.write(e));
367
+ zipFile.on('error', e => process.stdout.write(String(e)));
274
368
  zipFile.on('entry', (entry) => {
275
369
  if (entry.fileName.endsWith('/')) {
276
370
  // Directory
277
371
  zipFile.readEntry();
278
372
  }
279
- else {
373
+ else if (entry.fileName.startsWith('assets/') || entry.fileName.startsWith('data/')) {
280
374
  // File
281
- if (entry.fileName.startsWith('assets/') || entry.fileName.startsWith('data/')) {
282
- const targetFile = (0, path_1.join)(this.path, 'mod_assets', entry.fileName.substring(entry.fileName.startsWith('assets/') ? 7 : 5, entry.fileName.length));
283
- const targetDir = (0, path_1.dirname)(targetFile);
284
- this.ensureDirExists(targetDir).then(() => {
285
- zipFile.openReadStream(entry, (e, readStream) => {
286
- if (e) {
287
- throw e;
288
- }
289
- readStream.pipe((0, fs_1.createWriteStream)(targetFile));
290
- readStream.on('end', () => zipFile.readEntry());
291
- });
375
+ const prefix = entry.fileName.startsWith('assets/') ? 7 : 5;
376
+ const targetFile = (0, node_path_1.join)(this.path, 'mod_assets', entry.fileName.slice(prefix, entry.fileName.length));
377
+ const targetDir = (0, node_path_1.dirname)(targetFile);
378
+ void this.ensureDirExists(targetDir).then(() => {
379
+ zipFile.openReadStream(entry, (e, readStream) => {
380
+ if (e) {
381
+ throw e;
382
+ }
383
+ readStream.pipe((0, node_fs_1.createWriteStream)(targetFile));
384
+ readStream.on('end', () => zipFile.readEntry());
292
385
  });
293
- }
294
- else {
295
- zipFile.readEntry();
296
- }
386
+ }).catch((e) => {
387
+ throw e;
388
+ });
389
+ }
390
+ else {
391
+ zipFile.readEntry();
297
392
  }
298
393
  });
299
- yield new Promise((resolve) => zipFile.on('end', resolve));
394
+ yield new Promise(resolve => zipFile.on('end', resolve));
300
395
  });
301
396
  }
302
397
  /**
@@ -306,7 +401,7 @@ class ModLoader {
306
401
  copyModAssets(target) {
307
402
  return __awaiter(this, void 0, void 0, function* () {
308
403
  process.stdout.write('Copying mod assets...\n');
309
- yield (0, util_1.promisify)(ncp_1.ncp)((0, path_1.join)(this.path, 'mod_assets'), target);
404
+ yield (0, node_util_1.promisify)(ncp_1.ncp)((0, node_path_1.join)(this.path, 'mod_assets'), target);
310
405
  });
311
406
  }
312
407
  /**
@@ -314,7 +409,7 @@ class ModLoader {
314
409
  */
315
410
  removeServer() {
316
411
  return __awaiter(this, void 0, void 0, function* () {
317
- yield (0, util_1.promisify)(rimraf)(this.path);
412
+ yield (0, node_util_1.promisify)(rimraf_1.default)(this.path);
318
413
  });
319
414
  }
320
415
  /**
@@ -322,18 +417,18 @@ class ModLoader {
322
417
  */
323
418
  removeMods() {
324
419
  return __awaiter(this, void 0, void 0, function* () {
325
- yield (0, util_1.promisify)(rimraf)((0, path_1.join)(this.path, 'mods'));
420
+ yield (0, node_util_1.promisify)(rimraf_1.default)((0, node_path_1.join)(this.path, 'mods'));
326
421
  });
327
422
  }
328
423
  ensureDirExists(path) {
329
424
  return __awaiter(this, void 0, void 0, function* () {
330
- const segments = path.substr(this.path.length, path.length).split(path_1.sep);
425
+ const segments = path.slice(this.path.length, this.path.length + path.length).split(node_path_1.sep);
331
426
  for (let i = 1; i <= segments.length; i++) {
332
- const subPath = (0, path_1.join)(this.path, segments.slice(0, i).join(path_1.sep));
427
+ const subPath = (0, node_path_1.join)(this.path, segments.slice(0, i).join(node_path_1.sep));
333
428
  try {
334
429
  yield fs.promises.stat(subPath);
335
430
  }
336
- catch (e) {
431
+ catch (_a) {
337
432
  yield fs.promises.mkdir(subPath);
338
433
  }
339
434
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Build the URL for a Maven artifact JAR in a repository.
3
+ */
4
+ export declare function buildMavenArtifactUrl(groupId: string, artifactId: string, version: string, classifier: string | undefined, repoUrl: string): string;
5
+ /**
6
+ * Check if a Maven artifact exists in a repository by sending an HTTP HEAD request.
7
+ * Returns true if the server responds with a 2xx status, false otherwise.
8
+ */
9
+ export declare function artifactExistsInRepo(groupId: string, artifactId: string, version: string, classifier: string | undefined, repoUrl: string, headers?: Record<string, string>): Promise<boolean>;
10
+ export interface IModpackMod {
11
+ type: 'maven';
12
+ artifact: string;
13
+ repo: string;
14
+ headers?: Record<string, string>;
15
+ }
16
+ export interface IModpackJson {
17
+ minecraft: string;
18
+ neoforge?: string;
19
+ forge?: string;
20
+ mods: IModpackMod[];
21
+ }
22
+ /**
23
+ * Convert a Maven pom.xml (and optional settings.xml) into a modpack.json object.
24
+ * @param pomXml The contents of the pom.xml file.
25
+ * @param settingsXml The contents of the settings.xml file, or undefined.
26
+ * @returns The generated modpack.json object.
27
+ */
28
+ export declare function convertPomToModpack(pomXml: string, settingsXml: string | undefined): Promise<IModpackJson>;
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.buildMavenArtifactUrl = buildMavenArtifactUrl;
16
+ exports.artifactExistsInRepo = artifactExistsInRepo;
17
+ exports.convertPomToModpack = convertPomToModpack;
18
+ const node_util_1 = require("node:util");
19
+ const node_fetch_1 = __importDefault(require("node-fetch"));
20
+ const xml2js_1 = require("xml2js");
21
+ const parseStringPromise = (0, node_util_1.promisify)(xml2js_1.parseString);
22
+ /** Default Maven repository URL, used when no settings.xml is provided. */
23
+ const DEFAULT_MAVEN_REPO = 'https://repo.maven.apache.org/maven2';
24
+ /**
25
+ * Build the URL for a Maven artifact JAR in a repository.
26
+ */
27
+ function buildMavenArtifactUrl(groupId, artifactId, version, classifier, repoUrl) {
28
+ const groupPath = groupId.replaceAll('.', '/');
29
+ const suffix = classifier ? `-${classifier}` : '';
30
+ const fileName = `${artifactId}-${version}${suffix}.jar`;
31
+ const base = repoUrl.endsWith('/') ? repoUrl : `${repoUrl}/`;
32
+ return `${base}${groupPath}/${artifactId}/${version}/${fileName}`;
33
+ }
34
+ /**
35
+ * Check if a Maven artifact exists in a repository by sending an HTTP HEAD request.
36
+ * Returns true if the server responds with a 2xx status, false otherwise.
37
+ */
38
+ function artifactExistsInRepo(groupId, artifactId, version, classifier, repoUrl, headers) {
39
+ return __awaiter(this, void 0, void 0, function* () {
40
+ const url = buildMavenArtifactUrl(groupId, artifactId, version, classifier, repoUrl);
41
+ try {
42
+ const response = yield (0, node_fetch_1.default)(url, { method: 'HEAD', headers: headers !== null && headers !== void 0 ? headers : {} });
43
+ return response.ok;
44
+ }
45
+ catch (_a) {
46
+ return false;
47
+ }
48
+ });
49
+ }
50
+ /**
51
+ * Build Authorization headers for a server entry.
52
+ * Returns a Bearer header for placeholder passwords, or a Basic header for literal credentials.
53
+ * Returns an empty object if no password is configured.
54
+ */
55
+ function buildAuthHeaders(server) {
56
+ var _a;
57
+ if (!(server === null || server === void 0 ? void 0 : server.password)) {
58
+ return {};
59
+ }
60
+ const headers = {};
61
+ // If the password is an environment variable placeholder (e.g. ${GITHUB_TOKEN}),
62
+ // use Bearer authentication to avoid having to base64-encode at generation time.
63
+ if (/^\$\{[^}]+\}$/u.test(server.password)) {
64
+ headers.Authorization = `Bearer ${server.password}`;
65
+ }
66
+ else {
67
+ // Literal credentials: encode as Basic auth
68
+ const credentials = Buffer
69
+ .from(`${(_a = server.username) !== null && _a !== void 0 ? _a : 'token'}:${server.password}`)
70
+ .toString('base64');
71
+ headers.Authorization = `Basic ${credentials}`;
72
+ }
73
+ return headers;
74
+ }
75
+ /**
76
+ * Convert a Maven pom.xml (and optional settings.xml) into a modpack.json object.
77
+ * @param pomXml The contents of the pom.xml file.
78
+ * @param settingsXml The contents of the settings.xml file, or undefined.
79
+ * @returns The generated modpack.json object.
80
+ */
81
+ function convertPomToModpack(pomXml, settingsXml) {
82
+ return __awaiter(this, void 0, void 0, function* () {
83
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
84
+ const pomDoc = (yield parseStringPromise(pomXml));
85
+ const project = pomDoc.project;
86
+ // Extract Minecraft version from the project <version>
87
+ const minecraftVersion = project.version[0];
88
+ // Extract NeoForge/Forge version from <properties>
89
+ let neoforgeVersion;
90
+ let forgeVersion;
91
+ if ((_a = project.properties) === null || _a === void 0 ? void 0 : _a[0]) {
92
+ const props = project.properties[0];
93
+ if (props['neoforge.version']) {
94
+ neoforgeVersion = props['neoforge.version'][0];
95
+ }
96
+ if (props['forge.version']) {
97
+ forgeVersion = props['forge.version'][0];
98
+ }
99
+ }
100
+ // Parse settings.xml if provided
101
+ const activeProfileIds = new Set();
102
+ const repoMap = new Map();
103
+ const serverMap = new Map();
104
+ if (settingsXml) {
105
+ const settingsDoc = (yield parseStringPromise(settingsXml));
106
+ const settings = settingsDoc.settings;
107
+ // Collect active profile IDs
108
+ if ((_c = (_b = settings.activeProfiles) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.activeProfile) {
109
+ for (const id of settings.activeProfiles[0].activeProfile) {
110
+ activeProfileIds.add(id);
111
+ }
112
+ }
113
+ // Collect repositories from active profiles
114
+ if ((_e = (_d = settings.profiles) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.profile) {
115
+ for (const profile of settings.profiles[0].profile) {
116
+ const profileId = profile.id[0];
117
+ if (!activeProfileIds.has(profileId)) {
118
+ continue;
119
+ }
120
+ if ((_g = (_f = profile.repositories) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.repository) {
121
+ for (const repo of profile.repositories[0].repository) {
122
+ const repoId = repo.id[0];
123
+ const repoUrl = repo.url[0].replace(/\/$/u, '');
124
+ repoMap.set(repoId, { id: repoId, url: repoUrl });
125
+ }
126
+ }
127
+ }
128
+ }
129
+ // Collect server credentials
130
+ if ((_j = (_h = settings.servers) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.server) {
131
+ for (const server of settings.servers[0].server) {
132
+ const serverId = server.id[0];
133
+ const username = (_k = server.username) === null || _k === void 0 ? void 0 : _k[0];
134
+ const password = (_l = server.password) === null || _l === void 0 ? void 0 : _l[0];
135
+ serverMap.set(serverId, { id: serverId, username, password });
136
+ }
137
+ }
138
+ }
139
+ // Collect dependencies from pom.xml
140
+ const mods = [];
141
+ if ((_o = (_m = project.dependencies) === null || _m === void 0 ? void 0 : _m[0]) === null || _o === void 0 ? void 0 : _o.dependency) {
142
+ const repos = [...repoMap.values()];
143
+ for (const dep of project.dependencies[0].dependency) {
144
+ const groupId = dep.groupId[0];
145
+ const artifactId = dep.artifactId[0];
146
+ const version = dep.version[0];
147
+ const classifier = (_p = dep.classifier) === null || _p === void 0 ? void 0 : _p[0];
148
+ const artifact = classifier ?
149
+ `${groupId}:${artifactId}:${version}:${classifier}` :
150
+ `${groupId}:${artifactId}:${version}`;
151
+ // Determine repo: when multiple repos are configured, probe each one in order
152
+ // and use the first that has the artifact (mirrors Maven's repository resolution).
153
+ // When only one repo is configured, skip probing and use it directly.
154
+ let repo = (_q = repos[0]) !== null && _q !== void 0 ? _q : { id: 'central', url: DEFAULT_MAVEN_REPO };
155
+ if (repos.length > 1) {
156
+ for (const candidateRepo of repos) {
157
+ const candidateServer = serverMap.get(candidateRepo.id);
158
+ const probeHeaders = buildAuthHeaders(candidateServer);
159
+ const found = yield artifactExistsInRepo(groupId, artifactId, version, classifier, candidateRepo.url, probeHeaders);
160
+ if (found) {
161
+ repo = candidateRepo;
162
+ break;
163
+ }
164
+ }
165
+ }
166
+ const mod = {
167
+ type: 'maven',
168
+ artifact,
169
+ repo: repo.url,
170
+ };
171
+ // If the repo has a server entry with credentials, add auth headers
172
+ const server = serverMap.get(repo.id);
173
+ if (server === null || server === void 0 ? void 0 : server.password) {
174
+ mod.headers = buildAuthHeaders(server);
175
+ }
176
+ mods.push(mod);
177
+ }
178
+ }
179
+ // Build the modpack.json output
180
+ const modpack = {
181
+ minecraft: minecraftVersion,
182
+ mods,
183
+ };
184
+ if (neoforgeVersion) {
185
+ modpack.neoforge = neoforgeVersion;
186
+ }
187
+ else if (forgeVersion) {
188
+ modpack.forge = forgeVersion;
189
+ }
190
+ // Reorder so minecraft/neoforge/forge come before mods
191
+ return Object.assign(Object.assign(Object.assign({ minecraft: modpack.minecraft }, (modpack.neoforge ? { neoforge: modpack.neoforge } : {})), (modpack.forge ? { forge: modpack.forge } : {})), { mods: modpack.mods });
192
+ });
193
+ }
194
+ //# sourceMappingURL=PomConverter.js.map