amule-ec-client 0.5.1 → 0.6.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 (86) hide show
  1. package/README.md +6 -4
  2. package/dist/AddLinkRequest-DwUbSMXD.mjs +17 -0
  3. package/dist/AddLinkRequest-DwUbSMXD.mjs.map +1 -0
  4. package/dist/ClientQueueRequest-D9FaX5K9.mjs +17 -0
  5. package/dist/ClientQueueRequest-D9FaX5K9.mjs.map +1 -0
  6. package/dist/ClientQueueResponse-FVQBR_8b.mjs +65 -0
  7. package/dist/ClientQueueResponse-FVQBR_8b.mjs.map +1 -0
  8. package/dist/Codes-CjiW7Pdl.mjs +411 -0
  9. package/dist/Codes-CjiW7Pdl.mjs.map +1 -0
  10. package/dist/CreateCategoryRequest-Br6yfDhZ.mjs +24 -0
  11. package/dist/CreateCategoryRequest-Br6yfDhZ.mjs.map +1 -0
  12. package/dist/DeleteCategoryRequest-C2-p6sx0.mjs +17 -0
  13. package/dist/DeleteCategoryRequest-C2-p6sx0.mjs.map +1 -0
  14. package/dist/DownloadCommandRequest-CaULjfNi.mjs +31 -0
  15. package/dist/DownloadCommandRequest-CaULjfNi.mjs.map +1 -0
  16. package/dist/DownloadFileRequest-CcNqsBo7.mjs +20 -0
  17. package/dist/DownloadFileRequest-CcNqsBo7.mjs.map +1 -0
  18. package/dist/DownloadQueueRequest-bd7lFiMj.mjs +17 -0
  19. package/dist/DownloadQueueRequest-bd7lFiMj.mjs.map +1 -0
  20. package/dist/DownloadQueueResponse-DyP1rLTV.mjs +261 -0
  21. package/dist/DownloadQueueResponse-DyP1rLTV.mjs.map +1 -0
  22. package/dist/GetPreferencesRequest-C94Su9IH.mjs +18 -0
  23. package/dist/GetPreferencesRequest-C94Su9IH.mjs.map +1 -0
  24. package/dist/PreferencesDetailsResponse-BjMxbvVY.mjs +96 -0
  25. package/dist/PreferencesDetailsResponse-BjMxbvVY.mjs.map +1 -0
  26. package/dist/PreferencesResponse-Dth-n2sA.mjs +39 -0
  27. package/dist/PreferencesResponse-Dth-n2sA.mjs.map +1 -0
  28. package/dist/ReloadSharedFilesRequest-CPwAEJYG.mjs +15 -0
  29. package/dist/ReloadSharedFilesRequest-CPwAEJYG.mjs.map +1 -0
  30. package/dist/Request-C0UUuQIX.mjs +160 -0
  31. package/dist/Request-C0UUuQIX.mjs.map +1 -0
  32. package/dist/SearchProgressResponse-Bputc9Ww.mjs +17 -0
  33. package/dist/SearchProgressResponse-Bputc9Ww.mjs.map +1 -0
  34. package/dist/SearchRequest-CNqz-K1Z.mjs +57 -0
  35. package/dist/SearchRequest-CNqz-K1Z.mjs.map +1 -0
  36. package/dist/SearchResultsRequest-DKG508KF.mjs +15 -0
  37. package/dist/SearchResultsRequest-DKG508KF.mjs.map +1 -0
  38. package/dist/SearchResultsResponse-ChNrKJ6_.mjs +31 -0
  39. package/dist/SearchResultsResponse-ChNrKJ6_.mjs.map +1 -0
  40. package/dist/SearchStatusRequest-Bv6iaC9Y.mjs +15 -0
  41. package/dist/SearchStatusRequest-Bv6iaC9Y.mjs.map +1 -0
  42. package/dist/SearchStopRequest-rET-2Nsz.mjs +15 -0
  43. package/dist/SearchStopRequest-rET-2Nsz.mjs.map +1 -0
  44. package/dist/ServerConnectRequest-CcO2-Eqd.mjs +20 -0
  45. package/dist/ServerConnectRequest-CcO2-Eqd.mjs.map +1 -0
  46. package/dist/ServerDisconnectRequest-DEORM3SF.mjs +15 -0
  47. package/dist/ServerDisconnectRequest-DEORM3SF.mjs.map +1 -0
  48. package/dist/ServerListRequest-FwFQBp12.mjs +15 -0
  49. package/dist/ServerListRequest-FwFQBp12.mjs.map +1 -0
  50. package/dist/ServerListResponse-ZAKSSCPZ.mjs +52 -0
  51. package/dist/ServerListResponse-ZAKSSCPZ.mjs.map +1 -0
  52. package/dist/ServerUpdateFromUrlRequest-D4xBqJDJ.mjs +14 -0
  53. package/dist/ServerUpdateFromUrlRequest-D4xBqJDJ.mjs.map +1 -0
  54. package/dist/SetFileCategoryRequest-BwEPJVeR.mjs +17 -0
  55. package/dist/SetFileCategoryRequest-BwEPJVeR.mjs.map +1 -0
  56. package/dist/SetPreferencesRequest-D2fVIH3P.mjs +66 -0
  57. package/dist/SetPreferencesRequest-D2fVIH3P.mjs.map +1 -0
  58. package/dist/SharedFilePriorityRequest-j6ImgagV.mjs +15 -0
  59. package/dist/SharedFilePriorityRequest-j6ImgagV.mjs.map +1 -0
  60. package/dist/SharedFilesRequest-D2M3K0eB.mjs +17 -0
  61. package/dist/SharedFilesRequest-D2M3K0eB.mjs.map +1 -0
  62. package/dist/SharedFilesResponse-BiXT_oCx.mjs +41 -0
  63. package/dist/SharedFilesResponse-BiXT_oCx.mjs.map +1 -0
  64. package/dist/StatsRequest-B8v9LLHG.mjs +17 -0
  65. package/dist/StatsRequest-B8v9LLHG.mjs.map +1 -0
  66. package/dist/StatsResponse-BVUxNPbz.mjs +78 -0
  67. package/dist/StatsResponse-BVUxNPbz.mjs.map +1 -0
  68. package/dist/Tag-CZF6BRMY.mjs +262 -0
  69. package/dist/Tag-CZF6BRMY.mjs.map +1 -0
  70. package/dist/UpdateCategoryRequest-CgzH7SxP.mjs +24 -0
  71. package/dist/UpdateCategoryRequest-CgzH7SxP.mjs.map +1 -0
  72. package/dist/UpdateRequest-Bunl73Ga.mjs +17 -0
  73. package/dist/UpdateRequest-Bunl73Ga.mjs.map +1 -0
  74. package/dist/UpdateResponse-1LhyH9Zf.mjs +47 -0
  75. package/dist/UpdateResponse-1LhyH9Zf.mjs.map +1 -0
  76. package/dist/download-details-OM0FJUhZ.mjs +12 -0
  77. package/dist/download-details-OM0FJUhZ.mjs.map +1 -0
  78. package/dist/index.d.mts +1130 -0
  79. package/dist/index.mjs +1077 -0
  80. package/dist/index.mjs.map +1 -0
  81. package/dist/utils-DBnUQnZ-.mjs +26 -0
  82. package/dist/utils-DBnUQnZ-.mjs.map +1 -0
  83. package/package.json +5 -5
  84. package/dist/index.d.ts +0 -1016
  85. package/dist/index.js +0 -3130
  86. package/dist/index.js.map +0 -1
package/dist/index.mjs ADDED
@@ -0,0 +1,1077 @@
1
+ import { n as Packet, r as Flags, t as Request } from "./Request-C0UUuQIX.mjs";
2
+ import { a as EcPrefs, i as ECTagName, n as ECOpCode, o as ProtocolVersion, r as ECSearchType, t as ECDetailLevel } from "./Codes-CjiW7Pdl.mjs";
3
+ import { a as StringTag, c as UInt128Tag, d as UShortTag, f as findNumericTag, i as Ipv4Tag, l as UIntTag, n as DoubleTag, o as Tag, r as Hash16Tag, s as UByteTag, t as CustomTag, u as ULongTag } from "./Tag-CZF6BRMY.mjs";
4
+ import { n as PARTSIZE, t as ChunkStatus } from "./download-details-OM0FJUhZ.mjs";
5
+ import * as net from "net";
6
+ import * as zlib from "zlib";
7
+ import * as crypto from "crypto";
8
+ //#region src/exceptions/index.ts
9
+ var AmuleException = class extends Error {
10
+ cause;
11
+ constructor(message, cause) {
12
+ super(message);
13
+ this.cause = cause;
14
+ this.name = "AmuleException";
15
+ }
16
+ };
17
+ var InvalidECException = class extends AmuleException {
18
+ constructor(message, cause) {
19
+ super(message, cause);
20
+ this.name = "InvalidECException";
21
+ }
22
+ };
23
+ var CommunicationException = class extends AmuleException {
24
+ constructor(message, cause) {
25
+ super(message, cause);
26
+ this.name = "CommunicationException";
27
+ }
28
+ };
29
+ var ServerException = class extends AmuleException {
30
+ constructor(message, cause) {
31
+ super(message, cause);
32
+ this.name = "ServerException";
33
+ }
34
+ };
35
+ //#endregion
36
+ //#region src/ec/Encoding.ts
37
+ /**
38
+ * Reads a UTF-8 encoded number from a buffer
39
+ * Treats UTF-8 bytes as a Unicode code point and returns the numeric value
40
+ */
41
+ function readUtf8Number(buffer, offset) {
42
+ const firstByte = buffer[offset];
43
+ let length;
44
+ if ((firstByte & 128) === 0) length = 1;
45
+ else if ((firstByte & 224) === 192) length = 2;
46
+ else if ((firstByte & 240) === 224) length = 3;
47
+ else if ((firstByte & 248) === 240) length = 4;
48
+ else throw new Error(`Invalid UTF-8 first byte: 0x${firstByte.toString(16)}`);
49
+ const codePoint = buffer.subarray(offset, offset + length).toString("utf-8").codePointAt(0);
50
+ if (codePoint === void 0) throw new Error("Failed to decode UTF-8 number");
51
+ return {
52
+ value: BigInt(codePoint),
53
+ newOffset: offset + length
54
+ };
55
+ }
56
+ /**
57
+ * Encodes a number as UTF-8 code point
58
+ * Treats the number as a Unicode code point and encodes it as UTF-8
59
+ */
60
+ function encodeUtf8Number(value) {
61
+ const num = Number(value);
62
+ const str = String.fromCodePoint(num);
63
+ return Buffer.from(str, "utf-8");
64
+ }
65
+ /**
66
+ * Gets the number of bytes needed to encode a value as UTF-8 code point
67
+ */
68
+ function numberLength(value) {
69
+ const num = Number(value);
70
+ if (num < 128) return 1;
71
+ else if (num < 2048) return 2;
72
+ else if (num < 65536) return 3;
73
+ else if (num < 1114112) return 4;
74
+ else throw new Error(`Code point out of range: ${num}`);
75
+ }
76
+ //#endregion
77
+ //#region src/ec/tag/TagParser.ts
78
+ const TAG_NAME_SIZE = 2;
79
+ const TAG_TYPE_SIZE = 1;
80
+ const TAG_LENGTH_SIZE = 4;
81
+ const SUBTAG_COUNT_SIZE = 2;
82
+ var TagParser = class {
83
+ buffer;
84
+ offset;
85
+ useUtf8Numbers;
86
+ constructor(buffer, offset = 0, useUtf8Numbers = false) {
87
+ this.buffer = buffer;
88
+ this.offset = offset;
89
+ this.useUtf8Numbers = useUtf8Numbers;
90
+ }
91
+ /**
92
+ * Parse a single tag from the current position
93
+ */
94
+ parseTag() {
95
+ return this.parseTagWithMetadata().tag;
96
+ }
97
+ /**
98
+ * Internal parse that returns metadata needed for nested tag calculation
99
+ */
100
+ parseTagWithMetadata() {
101
+ const tagNameAndSubtags = this.readNumber(TAG_NAME_SIZE);
102
+ const tagName = tagNameAndSubtags >> 1 & 65535;
103
+ const hasSubtags = (tagNameAndSubtags & 1) !== 0;
104
+ const tagType = this.buffer.readUInt8(this.offset);
105
+ this.offset += TAG_TYPE_SIZE;
106
+ const tagLength = this.readNumber(TAG_LENGTH_SIZE);
107
+ let valueStartIndex = this.offset;
108
+ const subTags = [];
109
+ let theoreticalLength = 0;
110
+ let valueEndIndex;
111
+ if (!hasSubtags) valueEndIndex = valueStartIndex + tagLength - 1;
112
+ else {
113
+ const subTagCount = this.readNumber(SUBTAG_COUNT_SIZE);
114
+ valueStartIndex = this.offset;
115
+ for (let i = 0; i < subTagCount; i++) {
116
+ const result = this.parseTagWithMetadata();
117
+ subTags.push(result.tag);
118
+ theoreticalLength += result.theoreticalLength;
119
+ }
120
+ if (subTags.length > subTagCount) throw new InvalidECException(`Error parsing subtags list - Expected subtags ${subTagCount} found subtags ${subTags.length}`);
121
+ valueEndIndex = this.offset + (tagLength - theoreticalLength - 1);
122
+ theoreticalLength += SUBTAG_COUNT_SIZE;
123
+ }
124
+ const valueSize = valueEndIndex - this.offset + 1;
125
+ const tagValue = this.buffer.subarray(this.offset, this.offset + valueSize);
126
+ this.offset += valueSize;
127
+ theoreticalLength += tagValue.length;
128
+ theoreticalLength += TAG_NAME_SIZE;
129
+ theoreticalLength += TAG_TYPE_SIZE;
130
+ theoreticalLength += TAG_LENGTH_SIZE;
131
+ const tag = this.createTagFromValue(tagName, tagType, tagValue);
132
+ if (subTags.length > 0) tag.nestedTags = subTags;
133
+ return {
134
+ tag,
135
+ theoreticalLength,
136
+ endIndex: this.offset - 1
137
+ };
138
+ }
139
+ /**
140
+ * Parse multiple tags
141
+ */
142
+ parseTags(count) {
143
+ const tags = [];
144
+ for (let i = 0; i < count; i++) tags.push(this.parseTag());
145
+ return tags;
146
+ }
147
+ /**
148
+ * Read a number with the given fixed size.
149
+ * In UTF-8 mode, reads as UTF-8 codepoint. Otherwise reads as big-endian integer.
150
+ */
151
+ readNumber(fixedSize) {
152
+ if (this.useUtf8Numbers) {
153
+ const result = readUtf8Number(this.buffer, this.offset);
154
+ this.offset = result.newOffset;
155
+ return Number(result.value);
156
+ } else {
157
+ let value;
158
+ switch (fixedSize) {
159
+ case 1:
160
+ value = this.buffer.readUInt8(this.offset);
161
+ break;
162
+ case 2:
163
+ value = this.buffer.readUInt16BE(this.offset);
164
+ break;
165
+ case 4:
166
+ value = this.buffer.readUInt32BE(this.offset);
167
+ break;
168
+ default: throw new InvalidECException(`Unsupported number size: ${fixedSize}`);
169
+ }
170
+ this.offset += fixedSize;
171
+ return value;
172
+ }
173
+ }
174
+ /**
175
+ * Create a tag instance based on type and raw value bytes
176
+ */
177
+ createTagFromValue(name, type, value) {
178
+ switch (type) {
179
+ case 1: return new CustomTag(name, value);
180
+ case 2: return new UByteTag(name, value.length > 0 ? value.readUInt8(0) : 0);
181
+ case 3: return new UShortTag(name, value.length >= 2 ? value.readUInt16BE(0) : 0);
182
+ case 4: return new UIntTag(name, value.length >= 4 ? value.readUInt32BE(0) : 0);
183
+ case 5: return new ULongTag(name, value.length >= 8 ? value.readBigUInt64BE(0) : 0n);
184
+ case 10: return new UInt128Tag(name, value.length >= 16 ? this.parseUInt128(value) : 0n);
185
+ case 6: return new StringTag(name, value[value.length - 1] === 0 ? value.toString("utf-8", 0, value.length - 1) : value.toString("utf-8"));
186
+ case 7: return new DoubleTag(name, value.length >= 8 ? value.readDoubleBE(0) : 0);
187
+ case 8: {
188
+ if (value.length !== 6) throw new InvalidECException(`Ipv4Tag value must be 6 bytes long, got ${value.length}`);
189
+ const address = `${value[0]}.${value[1]}.${value[2]}.${value[3]}`;
190
+ const port = value.readUInt16BE(4);
191
+ const tag = new Ipv4Tag(name);
192
+ tag["value"] = {
193
+ address,
194
+ port
195
+ };
196
+ return tag;
197
+ }
198
+ case 9: return new Hash16Tag(name, value);
199
+ default: throw new InvalidECException(`Unknown tag type: ${type} for tag ${name}`);
200
+ }
201
+ }
202
+ parseUInt128(value) {
203
+ if (value.length < 16) throw new InvalidECException(`UInt128 value must be at least 16 bytes long, got ${value.length}`);
204
+ return value.readBigUInt64BE(0) << 64n | value.readBigUInt64BE(8);
205
+ }
206
+ /**
207
+ * Get current offset
208
+ */
209
+ getOffset() {
210
+ return this.offset;
211
+ }
212
+ };
213
+ //#endregion
214
+ //#region src/ec/packet/PacketParser.ts
215
+ /**
216
+ * Packet Parser - Deserializes EC packets from binary data
217
+ */
218
+ var PacketParser = class {
219
+ static HEADER_SIZE = 8;
220
+ /**
221
+ * Parse a packet from a buffer
222
+ */
223
+ static parse(buffer) {
224
+ if (buffer.length < this.HEADER_SIZE) throw new InvalidECException(`Buffer too small: ${buffer.length} bytes`);
225
+ const flags = Flags.fromBuffer(buffer, 0);
226
+ const dataLength = buffer.readUInt32BE(4);
227
+ if (buffer.length < this.HEADER_SIZE + dataLength) throw new InvalidECException(`Incomplete packet: expected ${this.HEADER_SIZE + dataLength} bytes, got ${buffer.length}`);
228
+ let data = buffer.subarray(this.HEADER_SIZE, this.HEADER_SIZE + dataLength);
229
+ if (flags.isZlib()) data = this.decompress(data);
230
+ return this.parsePacketData(data, flags);
231
+ }
232
+ /**
233
+ * Parse packet data (after decompression)
234
+ */
235
+ static parsePacketData(data, flags) {
236
+ let offset = 0;
237
+ const opCode = data.readUInt8(offset);
238
+ offset += 1;
239
+ const useUtf8 = flags.isUtf8Numbers();
240
+ let tagCount;
241
+ if (useUtf8) {
242
+ tagCount = data.readUInt8(offset);
243
+ offset += 1;
244
+ } else {
245
+ tagCount = data.readUInt16BE(offset);
246
+ offset += 2;
247
+ }
248
+ return new Packet(opCode, flags, new TagParser(data, offset, useUtf8).parseTags(tagCount));
249
+ }
250
+ /**
251
+ * Decompress ZLIB data
252
+ */
253
+ static decompress(data) {
254
+ try {
255
+ return zlib.inflateSync(data);
256
+ } catch (error) {
257
+ throw new InvalidECException(`Failed to decompress packet: ${error}`);
258
+ }
259
+ }
260
+ /**
261
+ * Calculate expected packet size from header
262
+ */
263
+ static getExpectedPacketSize(headerBuffer) {
264
+ if (headerBuffer.length < this.HEADER_SIZE) throw new InvalidECException("Header buffer too small");
265
+ const dataLength = headerBuffer.readUInt32BE(4);
266
+ return this.HEADER_SIZE + dataLength;
267
+ }
268
+ /**
269
+ * Check if buffer contains a complete packet
270
+ */
271
+ static hasCompletePacket(buffer) {
272
+ if (buffer.length < this.HEADER_SIZE) return false;
273
+ const expectedSize = this.getExpectedPacketSize(buffer);
274
+ return buffer.length >= expectedSize;
275
+ }
276
+ };
277
+ //#endregion
278
+ //#region src/ec/tag/TagEncoder.ts
279
+ var TagEncoder = class {
280
+ useUtf8Numbers;
281
+ constructor(useUtf8Numbers = false) {
282
+ this.useUtf8Numbers = useUtf8Numbers;
283
+ }
284
+ /**
285
+ * Calculate total size needed for a tag (Physical Buffer Size)
286
+ */
287
+ calculateTagSize(tag) {
288
+ let size = 0;
289
+ if (this.useUtf8Numbers) {
290
+ const hasSubtags = (tag.nestedTags?.length ?? 0) > 0 ? 1 : 0;
291
+ const nameAndSubtags = (tag.name & 65535) << 1 | hasSubtags;
292
+ size += numberLength(nameAndSubtags);
293
+ } else size += 2;
294
+ size += 1;
295
+ const reportedLength = this.calculateReportedLength(tag);
296
+ if (this.useUtf8Numbers) size += numberLength(reportedLength);
297
+ else size += 4;
298
+ if ((tag.nestedTags?.length ?? 0) > 0) {
299
+ const nestedCount = tag.nestedTags?.length ?? 0;
300
+ if (this.useUtf8Numbers) size += numberLength(nestedCount);
301
+ else size += 2;
302
+ }
303
+ if (tag.nestedTags) for (const nestedTag of tag.nestedTags) size += this.calculateTagSize(nestedTag);
304
+ size += this.calculateValueSize(tag);
305
+ return size;
306
+ }
307
+ /**
308
+ * Calculate the "Reported Length" of the tag content.
309
+ * This logic mimics Jamule's computeTagLength, which assumes FIXED header sizes
310
+ * (2 bytes name, 1 byte type, 4 bytes length, 2 bytes subtag count)
311
+ * even when UTF-8 encoding is used.
312
+ */
313
+ calculateReportedLength(tag) {
314
+ let length = 0;
315
+ if (tag.nestedTags) for (const nestedTag of tag.nestedTags) {
316
+ length += this.calculateReportedLength(nestedTag);
317
+ length += 7;
318
+ if ((nestedTag.nestedTags?.length ?? 0) > 0) length += 2;
319
+ }
320
+ length += this.calculateValueSize(tag);
321
+ return length;
322
+ }
323
+ /**
324
+ * Calculate size of tag value
325
+ */
326
+ calculateValueSize(tag) {
327
+ const encoded = tag.encodeValue();
328
+ switch (tag.type) {
329
+ case 1:
330
+ if (encoded.length === 0) return 0;
331
+ return encoded.length;
332
+ case 6: return encoded.length;
333
+ case 2: return 1;
334
+ case 3: return 2;
335
+ case 4: return 4;
336
+ case 5: return 8;
337
+ case 10: return 16;
338
+ case 7: return 8;
339
+ case 8: return 6;
340
+ case 9: return 16;
341
+ default: return 0;
342
+ }
343
+ }
344
+ /**
345
+ * Encode a tag to a buffer
346
+ */
347
+ encodeTag(tag, buffer, offset) {
348
+ let currentOffset = offset;
349
+ const hasSubtags = (tag.nestedTags?.length ?? 0) > 0 ? 1 : 0;
350
+ const nameAndSubtags = (tag.name & 65535) << 1 | hasSubtags;
351
+ if (this.useUtf8Numbers) {
352
+ const encoded = encodeUtf8Number(nameAndSubtags);
353
+ encoded.copy(buffer, currentOffset);
354
+ currentOffset += encoded.length;
355
+ } else {
356
+ buffer.writeUInt16BE(nameAndSubtags, currentOffset);
357
+ currentOffset += 2;
358
+ }
359
+ buffer.writeUInt8(tag.type, currentOffset);
360
+ currentOffset += 1;
361
+ const tagLength = this.calculateReportedLength(tag);
362
+ if (this.useUtf8Numbers) {
363
+ const encoded = encodeUtf8Number(tagLength);
364
+ encoded.copy(buffer, currentOffset);
365
+ currentOffset += encoded.length;
366
+ } else {
367
+ buffer.writeUInt32BE(tagLength, currentOffset);
368
+ currentOffset += 4;
369
+ }
370
+ const nestedCount = tag.nestedTags?.length ?? 0;
371
+ if (hasSubtags) if (this.useUtf8Numbers) {
372
+ const encoded = encodeUtf8Number(nestedCount);
373
+ encoded.copy(buffer, currentOffset);
374
+ currentOffset += encoded.length;
375
+ } else {
376
+ buffer.writeUInt16BE(nestedCount, currentOffset);
377
+ currentOffset += 2;
378
+ }
379
+ if (tag.nestedTags) for (const nestedTag of tag.nestedTags) currentOffset = this.encodeTag(nestedTag, buffer, currentOffset);
380
+ currentOffset = this.encodeValue(tag, buffer, currentOffset);
381
+ return currentOffset;
382
+ }
383
+ /**
384
+ * Encode tag value to buffer
385
+ */
386
+ encodeValue(tag, buffer, offset) {
387
+ const encoded = tag.encodeValue();
388
+ switch (tag.type) {
389
+ case 1:
390
+ if (encoded.length > 0) {
391
+ encoded.copy(buffer, offset);
392
+ return offset + encoded.length;
393
+ }
394
+ return offset;
395
+ case 2:
396
+ case 3:
397
+ case 4:
398
+ case 5:
399
+ case 6:
400
+ case 7:
401
+ case 8:
402
+ case 9:
403
+ case 10:
404
+ encoded.copy(buffer, offset);
405
+ return offset + encoded.length;
406
+ default: return offset;
407
+ }
408
+ }
409
+ /**
410
+ * Encode multiple tags
411
+ */
412
+ encodeTags(tags) {
413
+ let totalSize = 0;
414
+ for (const tag of tags) totalSize += this.calculateTagSize(tag);
415
+ const buffer = Buffer.allocUnsafe(totalSize);
416
+ let offset = 0;
417
+ for (const tag of tags) offset = this.encodeTag(tag, buffer, offset);
418
+ return buffer;
419
+ }
420
+ };
421
+ //#endregion
422
+ //#region src/ec/packet/PacketWriter.ts
423
+ /**
424
+ * Packet Writer - Serializes EC packets to binary data
425
+ */
426
+ var PacketWriter = class {
427
+ static HEADER_SIZE = 8;
428
+ /**
429
+ * Write a packet to a buffer
430
+ */
431
+ static write(packet) {
432
+ const useUtf8 = packet.flags.isUtf8Numbers();
433
+ const useZlib = packet.flags.isZlib();
434
+ let data = this.encodePacketData(packet, useUtf8);
435
+ if (useZlib) data = this.compress(data);
436
+ const buffer = Buffer.allocUnsafe(this.HEADER_SIZE + data.length);
437
+ packet.flags.writeToBuffer(buffer, 0);
438
+ buffer.writeUInt32BE(data.length, 4);
439
+ data.copy(buffer, this.HEADER_SIZE);
440
+ return buffer;
441
+ }
442
+ /**
443
+ * Encode packet data (before compression)
444
+ */
445
+ static encodePacketData(packet, useUtf8) {
446
+ const encoder = new TagEncoder(useUtf8);
447
+ let size = 1;
448
+ if (useUtf8) {
449
+ const countBuf = encodeUtf8Number(packet.tags.length);
450
+ size += countBuf.length;
451
+ } else size += 2;
452
+ for (const tag of packet.tags) size += encoder.calculateTagSize(tag);
453
+ const buffer = Buffer.allocUnsafe(size);
454
+ let offset = 0;
455
+ buffer.writeUInt8(packet.opCode, offset);
456
+ offset += 1;
457
+ if (useUtf8) {
458
+ const countBuf = encodeUtf8Number(packet.tags.length);
459
+ countBuf.copy(buffer, offset);
460
+ offset += countBuf.length;
461
+ } else {
462
+ buffer.writeUInt16BE(packet.tags.length, offset);
463
+ offset += 2;
464
+ }
465
+ for (const tag of packet.tags) offset = encoder.encodeTag(tag, buffer, offset);
466
+ return buffer;
467
+ }
468
+ /**
469
+ * Compress data with ZLIB
470
+ */
471
+ static compress(data) {
472
+ return zlib.deflateSync(data, { level: zlib.constants.Z_BEST_COMPRESSION });
473
+ }
474
+ };
475
+ //#endregion
476
+ //#region src/request/AuthRequest.ts
477
+ /**
478
+ * Auth Request - Send client info to initiate authentication
479
+ */
480
+ var AuthClientInfoRequest = class extends Request {
481
+ constructor(clientName = "amule-ts-client", clientVersion = "1.0.0") {
482
+ super(2);
483
+ this.addTag(new StringTag(256, clientName));
484
+ this.addTag(new StringTag(257, clientVersion));
485
+ this.addTag(new UShortTag(2, 516));
486
+ this.addTag(new CustomTag(12, Buffer.alloc(0)));
487
+ this.addTag(new CustomTag(13, Buffer.alloc(0)));
488
+ }
489
+ };
490
+ /**
491
+ * Auth Password Request - Send hashed password after receiving salt
492
+ */
493
+ var AuthPasswordRequest = class extends Request {
494
+ constructor(hashedPassword) {
495
+ super(80);
496
+ this.addTag(new Hash16Tag(1, hashedPassword));
497
+ }
498
+ };
499
+ //#endregion
500
+ //#region src/response/AuthSaltResponse.ts
501
+ var AuthSaltResponse = class AuthSaltResponse {
502
+ salt;
503
+ constructor(salt) {
504
+ this.salt = salt;
505
+ }
506
+ static fromPacket(packet) {
507
+ const saltTag = findNumericTag(packet.tags, 11);
508
+ if (!saltTag) throw new InvalidECException("Missing salt tag in auth salt response");
509
+ return new AuthSaltResponse(saltTag.getLong());
510
+ }
511
+ };
512
+ //#endregion
513
+ //#region src/auth/PasswordHasher.ts
514
+ /**
515
+ * Password Hasher for aMule EC authentication
516
+ */
517
+ var PasswordHasher = class {
518
+ /**
519
+ * Hash a password with salt using MD5
520
+ *
521
+ * This matches aMule's password hashing algorithm (from jamule):
522
+ * 1. saltHash = MD5(salt.toHexString().uppercase())
523
+ * 2. passwordHash = MD5(password)
524
+ * 3. return MD5(passwordHash.toHex().lowercase() + saltHash.toHex().lowercase())
525
+ *
526
+ * @param password The password to hash
527
+ * @param salt The salt received from the server
528
+ * @returns The hashed password as a Buffer
529
+ */
530
+ static hash(password, salt) {
531
+ const saltHexUpper = salt.toString(16).toUpperCase().padStart(16, "0");
532
+ const saltHash = crypto.createHash("md5").update(saltHexUpper, "utf-8").digest();
533
+ const passwordHashHex = crypto.createHash("md5").update(password, "utf-8").digest().toString("hex").toLowerCase();
534
+ const saltHashHex = saltHash.toString("hex").toLowerCase();
535
+ return crypto.createHash("md5").update(passwordHashHex + saltHashHex, "utf-8").digest();
536
+ }
537
+ /**
538
+ * Hash a password without salt (for testing)
539
+ */
540
+ static hashWithoutSalt(password) {
541
+ const lowerPassword = password.toLowerCase();
542
+ const md5Hash = crypto.createHash("md5");
543
+ md5Hash.update(lowerPassword, "utf-8");
544
+ return md5Hash.digest();
545
+ }
546
+ };
547
+ //#endregion
548
+ //#region src/client/AmuleConnection.ts
549
+ /**
550
+ * AmuleConnection - Manages the low-level connection to aMule EC
551
+ */
552
+ var AmuleConnection = class {
553
+ host;
554
+ port;
555
+ password;
556
+ timeout;
557
+ socket;
558
+ connected = false;
559
+ buffer = Buffer.allocUnsafe(0);
560
+ pendingResponses = [];
561
+ connectionPromise;
562
+ requestTimeout = 0;
563
+ debug = false;
564
+ /**
565
+ * Enable or disable debug logging
566
+ */
567
+ setDebug(enabled) {
568
+ this.debug = !!enabled;
569
+ }
570
+ /**
571
+ * Internal debug logger
572
+ */
573
+ log(...args) {
574
+ if (!this.debug) return;
575
+ console.log("[DEBUG]", ...args);
576
+ }
577
+ constructor(host, port, password, timeout, requestTimeout = 0) {
578
+ this.host = host;
579
+ this.port = port;
580
+ this.password = password;
581
+ this.timeout = timeout;
582
+ this.requestTimeout = Math.max(0, requestTimeout);
583
+ }
584
+ /**
585
+ * Set default timeout for a single request/response cycle.
586
+ * Use 0 to disable.
587
+ */
588
+ setRequestTimeout(timeoutMs) {
589
+ this.requestTimeout = Math.max(0, timeoutMs || 0);
590
+ }
591
+ /**
592
+ * Get current default timeout for requests.
593
+ */
594
+ getRequestTimeout() {
595
+ return this.requestTimeout;
596
+ }
597
+ /**
598
+ * Reject and clear all pending responses.
599
+ */
600
+ rejectAllPending(error) {
601
+ while (this.pendingResponses.length > 0) {
602
+ const pending = this.pendingResponses.shift();
603
+ if (pending?.timeoutHandle) clearTimeout(pending.timeoutHandle);
604
+ pending?.reject(error);
605
+ }
606
+ }
607
+ /**
608
+ * Reconnect to the server
609
+ */
610
+ async reconnect() {
611
+ if (this.connectionPromise) return this.connectionPromise;
612
+ this.connectionPromise = (async () => {
613
+ this.connected = false;
614
+ if (this.socket) this.socket.destroy();
615
+ this.socket = new net.Socket();
616
+ if (this.timeout > 0) this.socket.setTimeout(this.timeout);
617
+ this.socket.on("data", (data) => this.handleData(Buffer.isBuffer(data) ? data : Buffer.from(data)));
618
+ this.socket.on("error", (error) => this.handleError(error));
619
+ this.socket.on("timeout", () => this.handleTimeout());
620
+ this.socket.on("close", () => this.handleClose());
621
+ try {
622
+ await this.connectSocket();
623
+ await this.authenticate();
624
+ this.connected = true;
625
+ } finally {
626
+ this.connectionPromise = void 0;
627
+ }
628
+ })();
629
+ return this.connectionPromise;
630
+ }
631
+ /**
632
+ * Connect socket to server
633
+ */
634
+ connectSocket() {
635
+ return new Promise((resolve, reject) => {
636
+ if (!this.socket) return reject(new CommunicationException("Socket not initialized"));
637
+ this.socket.connect(this.port, this.host, () => {
638
+ resolve();
639
+ });
640
+ this.socket.once("error", reject);
641
+ });
642
+ }
643
+ /**
644
+ * Perform authentication handshake
645
+ */
646
+ async authenticate() {
647
+ this.log("Starting authentication...");
648
+ const clientInfoRequest = new AuthClientInfoRequest();
649
+ this.log("Sending client info...");
650
+ const saltPacket = await this.sendRequestNoAuth(clientInfoRequest);
651
+ this.log("Received salt response");
652
+ if (saltPacket.opCode === 3) throw new ServerException("Authentication failed: server rejected client");
653
+ if (saltPacket.opCode !== 79) throw new ServerException(`Unexpected response to client info: ${saltPacket.opCode}`);
654
+ const saltResponse = AuthSaltResponse.fromPacket(saltPacket);
655
+ this.log("Salt:", saltResponse.salt);
656
+ const hashedPassword = PasswordHasher.hash(this.password, saltResponse.salt);
657
+ this.log("Hashed password:", hashedPassword.toString("hex"));
658
+ const authRequest = new AuthPasswordRequest(hashedPassword);
659
+ this.log("Sending hashed password...");
660
+ const authPacket = await this.sendRequestNoAuth(authRequest);
661
+ this.log("Received auth response");
662
+ if (authPacket.opCode === 3) throw new ServerException("Authentication failed: invalid password");
663
+ if (authPacket.opCode !== 4) throw new ServerException(`Unexpected auth response: ${authPacket.opCode}`);
664
+ this.log("Authentication successful!");
665
+ }
666
+ /**
667
+ * Handle incoming data
668
+ */
669
+ handleData(data) {
670
+ this.log("Received data:", data.length, "bytes, hex:", data.toString("hex").substring(0, 100));
671
+ this.buffer = Buffer.concat([this.buffer, data]);
672
+ while (PacketParser.hasCompletePacket(this.buffer)) try {
673
+ const packet = PacketParser.parse(this.buffer);
674
+ this.log("Parsed packet, opCode:", packet.opCode);
675
+ const consumedBytes = PacketParser.getExpectedPacketSize(this.buffer);
676
+ this.buffer = this.buffer.subarray(consumedBytes);
677
+ const pending = this.pendingResponses.shift();
678
+ if (pending) pending.resolve(packet);
679
+ } catch (error) {
680
+ this.log("Parse error:", error);
681
+ const pending = this.pendingResponses.shift();
682
+ if (pending) pending.reject(error);
683
+ this.buffer = Buffer.allocUnsafe(0);
684
+ break;
685
+ }
686
+ }
687
+ /**
688
+ * Handle socket error
689
+ */
690
+ handleError(error) {
691
+ this.connected = false;
692
+ this.rejectAllPending(new CommunicationException(`Socket error: ${error.message}`));
693
+ }
694
+ /**
695
+ * Handle socket timeout
696
+ */
697
+ handleTimeout() {
698
+ this.connected = false;
699
+ this.rejectAllPending(new CommunicationException("Socket timeout"));
700
+ }
701
+ /**
702
+ * Handle socket close
703
+ */
704
+ handleClose() {
705
+ this.log("Socket closed! connected:", this.connected, "pendingResponses:", this.pendingResponses.length);
706
+ this.connected = false;
707
+ this.rejectAllPending(new CommunicationException("Socket closed"));
708
+ }
709
+ /**
710
+ * Send a request and wait for response
711
+ */
712
+ async sendRequest(request, timeoutMs) {
713
+ if (!this.connected) await this.reconnect();
714
+ try {
715
+ return await this.sendRequestNoAuth(request, timeoutMs);
716
+ } catch (error) {
717
+ this.connected = false;
718
+ throw error;
719
+ }
720
+ }
721
+ /**
722
+ * Send a request without checking authentication
723
+ */
724
+ async sendRequestNoAuth(request, timeoutMs) {
725
+ if (!this.socket) throw new CommunicationException("Socket not initialized");
726
+ const packet = request.buildPacket();
727
+ const buffer = PacketWriter.write(packet);
728
+ this.log("Sending request:", packet.opCode.toString(16), "Size:", buffer.length);
729
+ const responsePromise = new Promise((resolve, reject) => {
730
+ const pending = {
731
+ resolve: (packet) => {
732
+ if (pending.timeoutHandle) clearTimeout(pending.timeoutHandle);
733
+ resolve(packet);
734
+ },
735
+ reject: (error) => {
736
+ if (pending.timeoutHandle) clearTimeout(pending.timeoutHandle);
737
+ reject(error);
738
+ }
739
+ };
740
+ const effectiveTimeout = timeoutMs ?? this.requestTimeout;
741
+ if (effectiveTimeout > 0) pending.timeoutHandle = setTimeout(() => {
742
+ if (!this.pendingResponses.includes(pending)) return;
743
+ this.connected = false;
744
+ this.rejectAllPending(new CommunicationException(`Request timeout after ${effectiveTimeout}ms`));
745
+ if (this.socket && !this.socket.destroyed) this.socket.destroy();
746
+ }, effectiveTimeout);
747
+ this.pendingResponses.push(pending);
748
+ });
749
+ this.socket.write(buffer);
750
+ return responsePromise;
751
+ }
752
+ };
753
+ //#endregion
754
+ //#region src/model/index.ts
755
+ let FileStatus = /* @__PURE__ */ function(FileStatus) {
756
+ FileStatus[FileStatus["READY"] = 0] = "READY";
757
+ FileStatus[FileStatus["EMPTY"] = 1] = "EMPTY";
758
+ FileStatus[FileStatus["WAITINGFORHASH"] = 2] = "WAITINGFORHASH";
759
+ FileStatus[FileStatus["HASHING"] = 3] = "HASHING";
760
+ FileStatus[FileStatus["ERROR"] = 4] = "ERROR";
761
+ FileStatus[FileStatus["INSUFFICIENT"] = 5] = "INSUFFICIENT";
762
+ FileStatus[FileStatus["UNKNOWN"] = 6] = "UNKNOWN";
763
+ FileStatus[FileStatus["PAUSED"] = 7] = "PAUSED";
764
+ FileStatus[FileStatus["COMPLETING"] = 8] = "COMPLETING";
765
+ FileStatus[FileStatus["COMPLETE"] = 9] = "COMPLETE";
766
+ FileStatus[FileStatus["ALLOCATING"] = 10] = "ALLOCATING";
767
+ return FileStatus;
768
+ }({});
769
+ let DownloadCommand = /* @__PURE__ */ function(DownloadCommand) {
770
+ DownloadCommand[DownloadCommand["SWAP_A4AF_THIS"] = 22] = "SWAP_A4AF_THIS";
771
+ DownloadCommand[DownloadCommand["SWAP_A4AF_THIS_AUTO"] = 23] = "SWAP_A4AF_THIS_AUTO";
772
+ DownloadCommand[DownloadCommand["SWAP_A4AF_OTHERS"] = 24] = "SWAP_A4AF_OTHERS";
773
+ DownloadCommand[DownloadCommand["PAUSE"] = 25] = "PAUSE";
774
+ DownloadCommand[DownloadCommand["RESUME"] = 26] = "RESUME";
775
+ DownloadCommand[DownloadCommand["STOP"] = 27] = "STOP";
776
+ DownloadCommand[DownloadCommand["DELETE"] = 29] = "DELETE";
777
+ return DownloadCommand;
778
+ }({});
779
+ let SearchType = /* @__PURE__ */ function(SearchType) {
780
+ SearchType[SearchType["GLOBAL"] = 1] = "GLOBAL";
781
+ SearchType[SearchType["KAD"] = 2] = "KAD";
782
+ SearchType[SearchType["LOCAL"] = 0] = "LOCAL";
783
+ SearchType[SearchType["WEB"] = 3] = "WEB";
784
+ return SearchType;
785
+ }({});
786
+ //#endregion
787
+ //#region src/client/AmuleClient.ts
788
+ /**
789
+ * Main aMule EC Client
790
+ */
791
+ var AmuleClient = class {
792
+ connection;
793
+ constructor(options) {
794
+ this.connection = new AmuleConnection(options.host, options.port, options.password, options.timeout || 0, options.requestTimeout || 0);
795
+ }
796
+ /**
797
+ * Set default timeout for individual request/response cycles.
798
+ * Use 0 to disable request timeout.
799
+ */
800
+ setRequestTimeout(timeoutMs) {
801
+ this.connection.setRequestTimeout(timeoutMs);
802
+ }
803
+ /**
804
+ * Get current default timeout for requests.
805
+ */
806
+ getRequestTimeout() {
807
+ return this.connection.getRequestTimeout();
808
+ }
809
+ /**
810
+ * Reconnect to the server
811
+ */
812
+ async reconnect() {
813
+ await this.connection.reconnect();
814
+ }
815
+ /**
816
+ * Get server statistics
817
+ */
818
+ async getStats() {
819
+ const { StatsRequest } = await import("./StatsRequest-B8v9LLHG.mjs");
820
+ const { StatsResponseParser } = await import("./StatsResponse-BVUxNPbz.mjs");
821
+ const request = new StatsRequest();
822
+ const packet = await this.connection.sendRequest(request);
823
+ return StatsResponseParser.fromPacket(packet);
824
+ }
825
+ /**
826
+ * Get incremental updates for files, clients, servers, and friends
827
+ */
828
+ async getUpdate(detailLevel = 4) {
829
+ const { UpdateRequest } = await import("./UpdateRequest-Bunl73Ga.mjs");
830
+ const { UpdateResponseParser } = await import("./UpdateResponse-1LhyH9Zf.mjs");
831
+ const request = new UpdateRequest(detailLevel);
832
+ const packet = await this.connection.sendRequest(request);
833
+ return UpdateResponseParser.fromPacket(packet);
834
+ }
835
+ /**
836
+ * Start an asynchronous search
837
+ */
838
+ async searchAsync(query, searchType, filters) {
839
+ const { SearchRequest } = await import("./SearchRequest-CNqz-K1Z.mjs");
840
+ const request = new SearchRequest(query, searchType, filters);
841
+ await this.connection.sendRequest(request);
842
+ return query;
843
+ }
844
+ /**
845
+ * Get search status (0-1)
846
+ */
847
+ async searchStatus() {
848
+ const { SearchStatusRequest } = await import("./SearchStatusRequest-Bv6iaC9Y.mjs");
849
+ const { SearchProgressResponse } = await import("./SearchProgressResponse-Bputc9Ww.mjs");
850
+ const request = new SearchStatusRequest();
851
+ const packet = await this.connection.sendRequest(request);
852
+ return SearchProgressResponse.fromPacket(packet).progress;
853
+ }
854
+ /**
855
+ * Get search results
856
+ */
857
+ async searchResults() {
858
+ const { SearchResultsRequest } = await import("./SearchResultsRequest-DKG508KF.mjs");
859
+ const { SearchResultsResponseParser } = await import("./SearchResultsResponse-ChNrKJ6_.mjs");
860
+ const request = new SearchResultsRequest();
861
+ const packet = await this.connection.sendRequest(request);
862
+ return SearchResultsResponseParser.fromPacket(packet);
863
+ }
864
+ /**
865
+ * Perform a synchronous search (waits for results)
866
+ */
867
+ async searchSync(query, searchType, filters, timeout = 3e4) {
868
+ await this.searchAsync(query, searchType, filters);
869
+ const startTime = Date.now();
870
+ while (Date.now() - startTime < timeout) {
871
+ if (await this.searchStatus() >= 1) return await this.searchResults();
872
+ await new Promise((resolve) => setTimeout(resolve, 500));
873
+ }
874
+ throw new Error("Search timeout");
875
+ }
876
+ /**
877
+ * Stop current search
878
+ */
879
+ async searchStop() {
880
+ const { SearchStopRequest } = await import("./SearchStopRequest-rET-2Nsz.mjs");
881
+ const request = new SearchStopRequest();
882
+ await this.connection.sendRequest(request);
883
+ }
884
+ /**
885
+ * Download a file from search results
886
+ */
887
+ async downloadSearchResult(hash) {
888
+ const { DownloadFileRequest } = await import("./DownloadFileRequest-CcNqsBo7.mjs");
889
+ const request = new DownloadFileRequest(hash);
890
+ await this.connection.sendRequest(request);
891
+ }
892
+ /**
893
+ * Download from an ed2k link
894
+ */
895
+ async downloadEd2kLink(link) {
896
+ const { AddLinkRequest } = await import("./AddLinkRequest-DwUbSMXD.mjs");
897
+ const request = new AddLinkRequest(link);
898
+ await this.connection.sendRequest(request);
899
+ }
900
+ /**
901
+ * Get download queue
902
+ */
903
+ async getDownloadQueue() {
904
+ const { DownloadQueueRequest } = await import("./DownloadQueueRequest-bd7lFiMj.mjs");
905
+ const { DownloadQueueResponseParser } = await import("./DownloadQueueResponse-DyP1rLTV.mjs");
906
+ const request = new DownloadQueueRequest();
907
+ const packet = await this.connection.sendRequest(request);
908
+ return DownloadQueueResponseParser.fromPacket(packet).files;
909
+ }
910
+ /**
911
+ * Get shared files
912
+ */
913
+ async getSharedFiles() {
914
+ const { SharedFilesRequest } = await import("./SharedFilesRequest-D2M3K0eB.mjs");
915
+ const { SharedFilesResponseParser } = await import("./SharedFilesResponse-BiXT_oCx.mjs");
916
+ const request = new SharedFilesRequest();
917
+ const packet = await this.connection.sendRequest(request);
918
+ return SharedFilesResponseParser.fromPacket(packet).files;
919
+ }
920
+ /**
921
+ * Reload shared files list
922
+ */
923
+ async reloadSharedFiles() {
924
+ const { ReloadSharedFilesRequest } = await import("./ReloadSharedFilesRequest-CPwAEJYG.mjs");
925
+ const request = new ReloadSharedFilesRequest();
926
+ await this.connection.sendRequest(request);
927
+ }
928
+ /**
929
+ * Get client upload/download queue (clients we are uploading to/downloading from)
930
+ */
931
+ async getClientQueue() {
932
+ const { ClientQueueRequest } = await import("./ClientQueueRequest-D9FaX5K9.mjs");
933
+ const { ClientQueueResponseParser } = await import("./ClientQueueResponse-FVQBR_8b.mjs");
934
+ const request = new ClientQueueRequest();
935
+ const packet = await this.connection.sendRequest(request);
936
+ return ClientQueueResponseParser.fromPacket(packet).clients;
937
+ }
938
+ /**
939
+ * Get the list of servers
940
+ */
941
+ async getServerList() {
942
+ const { ServerListRequest } = await import("./ServerListRequest-FwFQBp12.mjs");
943
+ const { ServerListResponseParser } = await import("./ServerListResponse-ZAKSSCPZ.mjs");
944
+ const request = new ServerListRequest();
945
+ const packet = await this.connection.sendRequest(request);
946
+ return ServerListResponseParser.fromPacket(packet).servers;
947
+ }
948
+ /**
949
+ * Connect to a specific server
950
+ */
951
+ async connectToServer(ip, port) {
952
+ const { ServerConnectRequest } = await import("./ServerConnectRequest-CcO2-Eqd.mjs");
953
+ const request = new ServerConnectRequest(ip, port);
954
+ await this.connection.sendRequest(request);
955
+ }
956
+ /**
957
+ * Disconnect from the current server
958
+ */
959
+ async disconnectFromServer() {
960
+ const { ServerDisconnectRequest } = await import("./ServerDisconnectRequest-DEORM3SF.mjs");
961
+ const request = new ServerDisconnectRequest();
962
+ await this.connection.sendRequest(request);
963
+ }
964
+ /**
965
+ * Create a category
966
+ */
967
+ async createCategory(category) {
968
+ const { CreateCategoryRequest } = await import("./CreateCategoryRequest-Br6yfDhZ.mjs");
969
+ const request = new CreateCategoryRequest(category);
970
+ await this.connection.sendRequest(request);
971
+ }
972
+ /**
973
+ * Update a category
974
+ */
975
+ async updateCategory(id, category) {
976
+ const { UpdateCategoryRequest } = await import("./UpdateCategoryRequest-CgzH7SxP.mjs");
977
+ const request = new UpdateCategoryRequest(id, category);
978
+ await this.connection.sendRequest(request);
979
+ }
980
+ /**
981
+ * Delete a category
982
+ */
983
+ async deleteCategory(id) {
984
+ const { DeleteCategoryRequest } = await import("./DeleteCategoryRequest-C2-p6sx0.mjs");
985
+ const request = new DeleteCategoryRequest(id);
986
+ await this.connection.sendRequest(request);
987
+ }
988
+ /**
989
+ * Get all categories
990
+ */
991
+ async getCategories() {
992
+ const { GetPreferencesRequest } = await import("./GetPreferencesRequest-C94Su9IH.mjs");
993
+ const { PreferencesResponseParser } = await import("./PreferencesResponse-Dth-n2sA.mjs");
994
+ const request = new GetPreferencesRequest(1);
995
+ const packet = await this.connection.sendRequest(request);
996
+ return PreferencesResponseParser.fromPacket(packet).categories;
997
+ }
998
+ /**
999
+ * Set file category
1000
+ */
1001
+ async setFileCategory(hash, categoryId) {
1002
+ const { SetFileCategoryRequest } = await import("./SetFileCategoryRequest-BwEPJVeR.mjs");
1003
+ const request = new SetFileCategoryRequest(hash, categoryId);
1004
+ await this.connection.sendRequest(request);
1005
+ }
1006
+ /**
1007
+ * Send a download command (PAUSE, RESUME, STOP, DELETE, etc.)
1008
+ */
1009
+ async sendDownloadCommand(hash, command) {
1010
+ const { DownloadCommandRequest } = await import("./DownloadCommandRequest-CaULjfNi.mjs");
1011
+ const request = new DownloadCommandRequest(hash, command);
1012
+ await this.connection.sendRequest(request);
1013
+ }
1014
+ /**
1015
+ * Pause a download
1016
+ */
1017
+ async pauseDownload(hash) {
1018
+ await this.sendDownloadCommand(hash, 25);
1019
+ }
1020
+ /**
1021
+ * Resume a download
1022
+ */
1023
+ async resumeDownload(hash) {
1024
+ await this.sendDownloadCommand(hash, 26);
1025
+ }
1026
+ /**
1027
+ * Stop a download
1028
+ */
1029
+ async stopDownload(hash) {
1030
+ await this.sendDownloadCommand(hash, 27);
1031
+ }
1032
+ /**
1033
+ * Delete a download
1034
+ */
1035
+ async deleteDownload(hash) {
1036
+ await this.sendDownloadCommand(hash, 29);
1037
+ }
1038
+ /**
1039
+ * Trigger a server list update from an URL.
1040
+ */
1041
+ async updateServerListFromUrl(url) {
1042
+ const { ServerUpdateFromUrlRequest } = await import("./ServerUpdateFromUrlRequest-D4xBqJDJ.mjs");
1043
+ const request = new ServerUpdateFromUrlRequest(url);
1044
+ await this.connection.sendRequest(request);
1045
+ }
1046
+ /**
1047
+ * Set upload priority for a shared file.
1048
+ */
1049
+ async setSharedFilePriority(hash, priority) {
1050
+ const { SharedFilePriorityRequest } = await import("./SharedFilePriorityRequest-j6ImgagV.mjs");
1051
+ const request = new SharedFilePriorityRequest(hash, priority);
1052
+ await this.connection.sendRequest(request);
1053
+ }
1054
+ /**
1055
+ * Get daemon preferences for general/connection/servers/security sections.
1056
+ */
1057
+ async getPreferences() {
1058
+ const { GetPreferencesRequest } = await import("./GetPreferencesRequest-C94Su9IH.mjs");
1059
+ const { PreferencesDetailsResponseParser } = await import("./PreferencesDetailsResponse-BjMxbvVY.mjs");
1060
+ const request = new GetPreferencesRequest(2118);
1061
+ const packet = await this.connection.sendRequest(request);
1062
+ return PreferencesDetailsResponseParser.fromPacket(packet).preferences;
1063
+ }
1064
+ /**
1065
+ * Set daemon preferences. Only sections present in the input object are sent.
1066
+ */
1067
+ async setPreferences(preferences) {
1068
+ if (!preferences.general && !preferences.connection && !preferences.servers && !preferences.security) return;
1069
+ const { SetPreferencesRequest } = await import("./SetPreferencesRequest-D2fVIH3P.mjs");
1070
+ const request = new SetPreferencesRequest(preferences);
1071
+ await this.connection.sendRequest(request);
1072
+ }
1073
+ };
1074
+ //#endregion
1075
+ export { AmuleClient, AmuleException, ChunkStatus, CommunicationException, CustomTag, DoubleTag, DownloadCommand, ECDetailLevel, ECOpCode, ECSearchType, ECTagName, EcPrefs, FileStatus, Flags, Hash16Tag, InvalidECException, Ipv4Tag, PARTSIZE, Packet, PacketParser, PacketWriter, ProtocolVersion, SearchType, ServerException, StringTag, Tag, UByteTag, UInt128Tag, UIntTag, ULongTag, UShortTag };
1076
+
1077
+ //# sourceMappingURL=index.mjs.map