@adbjs/sdk 0.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -72,30 +72,35 @@ var BaseResource = class {
72
72
  }
73
73
  /**
74
74
  * Make a GET request
75
- * @param T - The generated response type (e.g., GetLinkUnlockResponse)
75
+ * @template T - The generated response type (e.g., GetLinkUnlockResponse)
76
+ * @param path - API endpoint path
77
+ * @param params - Optional query parameters
76
78
  * @returns The extracted data from the response (without the { status, data } wrapper)
77
79
  */
78
80
  async get(path, params) {
79
- const clientAny = this.client;
80
- return clientAny.get(path, params);
81
+ return this.client.get(path, params);
81
82
  }
82
83
  /**
83
84
  * Make a POST request
84
- * @param T - The generated response type
85
+ * @template T - The generated response type
86
+ * @param path - API endpoint path
87
+ * @param body - Request body
88
+ * @param params - Optional query parameters
85
89
  * @returns The extracted data from the response (without the { status, data } wrapper)
86
90
  */
87
91
  async post(path, body, params) {
88
- const clientAny = this.client;
89
- return clientAny.post(path, body, params);
92
+ return this.client.post(path, body, params);
90
93
  }
91
94
  /**
92
95
  * Make a POST request with FormData (multipart/form-data)
93
- * @param T - The generated response type
96
+ * @template T - The generated response type
97
+ * @param path - API endpoint path
98
+ * @param formData - Form data to send
99
+ * @param params - Optional query parameters
94
100
  * @returns The extracted data from the response (without the { status, data } wrapper)
95
101
  */
96
102
  async postFormData(path, formData, params) {
97
- const clientAny = this.client;
98
- return clientAny.postFormData(path, formData, params);
103
+ return this.client.postFormData(path, formData, params);
99
104
  }
100
105
  };
101
106
 
@@ -150,19 +155,24 @@ var LinkResource = class extends BaseResource {
150
155
  * Get information about one or more links
151
156
  *
152
157
  * @param links - Single link or array of links to get info for
158
+ * @param password - Optional password for password-protected links
153
159
  *
154
160
  * @example
155
161
  * ```ts
156
- * const info = await client.link.infos('https://example.com/file.zip')
157
- * console.log(info)
162
+ * const data = await client.link.infos('https://example.com/file.zip')
163
+ * console.log(data.infos)
164
+ *
165
+ * // With password
166
+ * const protectedData = await client.link.infos(
167
+ * 'https://example.com/protected.zip',
168
+ * 'mypassword'
169
+ * )
158
170
  * ```
159
171
  */
160
- async infos(links) {
172
+ async infos(links, password) {
161
173
  const linksArray = Array.isArray(links) ? links : [links];
162
- const data = await this.get("/link/infos", {
163
- link: linksArray
164
- });
165
- return data?.infos;
174
+ const params = password ? { password } : void 0;
175
+ return this.post("/link/infos", { "link[]": linksArray }, params);
166
176
  }
167
177
  /**
168
178
  * Extract links from redirectors/link protectors
@@ -171,19 +181,20 @@ var LinkResource = class extends BaseResource {
171
181
  *
172
182
  * @example
173
183
  * ```ts
174
- * const links = await client.link.redirector('https://linkprotector.com/abc123')
184
+ * const data = await client.link.redirector('https://linkprotector.com/abc123')
185
+ * console.log(data.links)
175
186
  * ```
176
187
  */
177
188
  async redirector(link) {
178
- const data = await this.get("/link/redirector", {
189
+ return this.post("/link/redirector", {
179
190
  link
180
191
  });
181
- return data?.links;
182
192
  }
183
193
  /**
184
194
  * Unlock a download link
185
195
  *
186
196
  * @param link - The link to unlock
197
+ * @param password - Optional password for password-protected links
187
198
  *
188
199
  * @example
189
200
  * ```ts
@@ -194,12 +205,17 @@ var LinkResource = class extends BaseResource {
194
205
  * // Handle delayed generation
195
206
  * const delayedResult = await client.link.delayed(result.delayed)
196
207
  * }
208
+ *
209
+ * // With password
210
+ * const protectedResult = await client.link.unlock(
211
+ * 'https://example.com/protected.zip',
212
+ * 'mypassword'
213
+ * )
197
214
  * ```
198
215
  */
199
- async unlock(link) {
200
- return this.get("/link/unlock", {
201
- link
202
- });
216
+ async unlock(link, password) {
217
+ const params = password ? { password } : void 0;
218
+ return this.post("/link/unlock", { link }, params);
203
219
  }
204
220
  /**
205
221
  * Unlock a link and automatically poll if delayed
@@ -228,9 +244,10 @@ var LinkResource = class extends BaseResource {
228
244
  * const streams = await client.link.streaming('abc123')
229
245
  * ```
230
246
  */
231
- async streaming(id) {
232
- return this.get("/link/streaming", {
233
- id
247
+ async streaming(id, stream) {
248
+ return this.post("/link/streaming", {
249
+ id,
250
+ stream
234
251
  });
235
252
  }
236
253
  /**
@@ -244,7 +261,7 @@ var LinkResource = class extends BaseResource {
244
261
  * ```
245
262
  */
246
263
  async delayed(id) {
247
- return this.get("/link/delayed", {
264
+ return this.post("/link/delayed", {
248
265
  id
249
266
  });
250
267
  }
@@ -265,7 +282,7 @@ var MagnetResource = class extends BaseResource {
265
282
  */
266
283
  async upload(magnets) {
267
284
  const magnetsArray = Array.isArray(magnets) ? magnets : [magnets];
268
- return this.get("/magnet/upload", {
285
+ return this.post("/magnet/upload", {
269
286
  magnets: magnetsArray
270
287
  });
271
288
  }
@@ -292,23 +309,22 @@ var MagnetResource = class extends BaseResource {
292
309
  return this.postFormData("/magnet/upload/file", formData);
293
310
  }
294
311
  /**
295
- * Get the status of one or more magnets
312
+ * Get the status of a specific magnet by ID
296
313
  *
297
- * @param id - Optional magnet ID to get status for a specific magnet
314
+ * @param id - Magnet ID to get status for
298
315
  *
299
316
  * @example
300
317
  * ```ts
301
- * // Get all magnets status
302
- * const allStatus = await client.magnet.status()
303
- *
304
- * // Get specific magnet status
305
- * const status = await client.magnet.status('123')
318
+ * const status = await client.magnet.status(123)
306
319
  * console.log(status.magnets[0]?.status) // 'Downloading', 'Ready', etc.
307
320
  * ```
321
+ *
322
+ * @remarks
323
+ * This endpoint uses AllDebrid API v4.1 (POST method)
324
+ * Migrated from v4 GET endpoint which was deprecated on 2024-10-16
308
325
  */
309
326
  async status(id) {
310
- const params = id ? { id } : void 0;
311
- const data = await this.get("/magnet/status", params);
327
+ const data = await this.post("/magnet/status", { id });
312
328
  if (data?.magnets && !Array.isArray(data.magnets)) {
313
329
  return {
314
330
  ...data,
@@ -317,71 +333,215 @@ var MagnetResource = class extends BaseResource {
317
333
  }
318
334
  return data;
319
335
  }
336
+ /**
337
+ * Get list of magnets with optional status filter
338
+ *
339
+ * @param statusFilter - Optional filter by status: 'active', 'ready', 'expired', or 'error'
340
+ *
341
+ * @example
342
+ * ```ts
343
+ * // Get all magnets
344
+ * const allMagnets = await client.magnet.statusList()
345
+ *
346
+ * // Get only active magnets
347
+ * const activeMagnets = await client.magnet.statusList('active')
348
+ *
349
+ * // Get only ready magnets
350
+ * const readyMagnets = await client.magnet.statusList('ready')
351
+ * ```
352
+ *
353
+ * @remarks
354
+ * This endpoint uses AllDebrid API v4.1 (POST method)
355
+ */
356
+ async statusList(statusFilter) {
357
+ const body = statusFilter ? { status: statusFilter } : void 0;
358
+ return this.post("/magnet/status", body);
359
+ }
360
+ /**
361
+ * Get magnet status using live mode for bandwidth-optimized delta synchronization
362
+ *
363
+ * Live mode is designed for monitoring multiple magnets efficiently by only transmitting
364
+ * changes between polling intervals, drastically reducing bandwidth usage for dashboards
365
+ * and real-time monitoring applications.
366
+ *
367
+ * ## How it works
368
+ *
369
+ * 1. **Session initialization**: Generate a random session ID and start with counter = 0
370
+ * 2. **First call (fullsync)**: Returns ALL magnets with `fullsync: true`
371
+ * 3. **Update counter**: Use the `counter` value returned by the API for the next call
372
+ * 4. **Subsequent calls (delta)**: Returns ONLY magnets that changed since last call
373
+ * 5. **Repeat**: Keep calling with updated counter to receive only deltas
374
+ *
375
+ * ## When to use
376
+ *
377
+ * - ✅ Monitoring multiple active magnets simultaneously
378
+ * - ✅ Building real-time dashboards
379
+ * - ✅ High-frequency polling scenarios (every few seconds)
380
+ * - ❌ Watching a single specific magnet (use `watch()` instead)
381
+ *
382
+ * ## Important notes
383
+ *
384
+ * - **Don't use the `id` parameter**: Passing an ID defeats the purpose of live mode
385
+ * as it disables delta sync and behaves like a regular `status()` call
386
+ * - **Session persistence**: Keep the same session ID for the entire monitoring session
387
+ * - **Counter tracking**: Always update the counter with the value returned by the API
388
+ * - **Empty deltas**: When no magnets changed, `magnets` will be an empty array
389
+ *
390
+ * @param options - Live mode session options
391
+ * @param options.session - Unique session ID (generate once: `Math.floor(Math.random() * 1000000)`)
392
+ * @param options.counter - Sync counter (start at 0, then use value from previous API response)
393
+ *
394
+ * @example
395
+ * ```ts
396
+ * // Initialize session
397
+ * const session = Math.floor(Math.random() * 1000000)
398
+ * let counter = 0
399
+ *
400
+ * // First call - returns all magnets (fullsync: true)
401
+ * const firstCall = await client.magnet.statusLive({ session, counter })
402
+ * console.log('Full sync:', firstCall.fullsync) // true
403
+ * console.log('All magnets:', firstCall.magnets) // Array of all magnets
404
+ * counter = firstCall.counter // Update counter for next call
405
+ *
406
+ * // Second call - returns only magnets that changed
407
+ * await new Promise(resolve => setTimeout(resolve, 3000)) // Wait 3 seconds
408
+ * const secondCall = await client.magnet.statusLive({ session, counter })
409
+ * console.log('Delta sync:', secondCall.magnets) // Only changed magnets
410
+ * counter = secondCall.counter
411
+ *
412
+ * // Example: Monitor all magnets until none are active
413
+ * const activeMagnets = new Map()
414
+ *
415
+ * while (true) {
416
+ * const response = await client.magnet.statusLive({ session, counter })
417
+ * counter = response.counter ?? counter
418
+ *
419
+ * // Update our local state with changes
420
+ * if (response.fullsync) {
421
+ * activeMagnets.clear()
422
+ * response.magnets?.forEach(m => activeMagnets.set(m.id, m))
423
+ * } else {
424
+ * response.magnets?.forEach(m => {
425
+ * if (m.status === 'Ready' || m.status === 'Error' || m.status === 'Expired') {
426
+ * activeMagnets.delete(m.id)
427
+ * } else {
428
+ * activeMagnets.set(m.id, m)
429
+ * }
430
+ * })
431
+ * }
432
+ *
433
+ * // Display current state
434
+ * console.log(`Active downloads: ${activeMagnets.size}`)
435
+ * activeMagnets.forEach(m => {
436
+ * console.log(` ${m.filename}: ${m.status} - ${m.downloaded}/${m.size} bytes`)
437
+ * })
438
+ *
439
+ * // Stop when no more active magnets
440
+ * if (activeMagnets.size === 0) {
441
+ * console.log('All downloads completed!')
442
+ * break
443
+ * }
444
+ *
445
+ * await new Promise(resolve => setTimeout(resolve, 3000))
446
+ * }
447
+ * ```
448
+ *
449
+ * @remarks
450
+ * This method is ideal for scenarios where you're monitoring multiple magnets and want
451
+ * to minimize bandwidth. For simple single-magnet monitoring, use `watch()` instead.
452
+ */
453
+ async statusLive(options) {
454
+ const body = {
455
+ session: options.session,
456
+ counter: options.counter
457
+ };
458
+ return this.post("/magnet/status", body);
459
+ }
320
460
  /**
321
461
  * Delete a magnet
322
462
  *
323
- * @param id - The magnet ID to delete (string)
463
+ * @param id - The magnet ID to delete
324
464
  *
325
465
  * @example
326
466
  * ```ts
327
- * await client.magnet.delete('123')
467
+ * await client.magnet.delete(123)
328
468
  * ```
329
469
  */
330
470
  async delete(id) {
331
- return this.get("/magnet/delete", {
471
+ return this.post("/magnet/delete", {
332
472
  id
333
473
  });
334
474
  }
335
475
  /**
336
476
  * Restart one or more failed magnets
337
477
  *
338
- * @param ids - Single magnet ID or array of magnet IDs to restart (strings)
478
+ * @param ids - Single magnet ID or array of magnet IDs to restart (numbers)
339
479
  *
340
480
  * @example
341
481
  * ```ts
342
482
  * // Restart single magnet
343
- * await client.magnet.restart('123')
483
+ * await client.magnet.restart(123)
344
484
  *
345
485
  * // Restart multiple magnets
346
- * await client.magnet.restart(['123', '456'])
486
+ * await client.magnet.restart([123, 456])
347
487
  * ```
348
488
  */
349
489
  async restart(ids) {
350
490
  const idsArray = Array.isArray(ids) ? ids : [ids];
351
- return this.get("/magnet/restart", {
491
+ return this.post("/magnet/restart", {
352
492
  ids: idsArray
353
493
  });
354
494
  }
355
495
  /**
356
- * Check instant availability of magnets
496
+ * Get files for a completed magnet
357
497
  *
358
- * @param magnets - Single magnet hash or array of hashes
498
+ * @param ids - The magnet ID or IDs to get files for
359
499
  *
360
500
  * @example
361
501
  * ```ts
362
- * const available = await client.magnet.instant(['hash1', 'hash2'])
363
- * console.log(available.magnets)
502
+ * const data = await client.magnet.files(123)
503
+ * data?.magnets?.forEach(magnet => {
504
+ * console.log(magnet.filename, magnet.files)
505
+ * })
364
506
  * ```
507
+ *
508
+ * @remarks
509
+ * Files are now retrieved separately from magnet status (since v4.1)
510
+ * Only available for magnets with status 'Ready'
365
511
  */
366
- async instant(magnets) {
367
- const magnetsArray = Array.isArray(magnets) ? magnets : [magnets];
368
- return this.get("/magnet/instant", {
369
- magnets: magnetsArray
370
- });
512
+ async files(ids) {
513
+ const formData = new FormData();
514
+ const idsArray = Array.isArray(ids) ? ids : [ids];
515
+ for (const id of idsArray) {
516
+ formData.append("id[]", String(id));
517
+ }
518
+ return this.postFormData("/magnet/files", formData);
371
519
  }
372
520
  /**
373
521
  * Watch a magnet's status with automatic polling
374
522
  *
375
- * @param id - The magnet ID to watch (string)
523
+ * This is a simple helper that polls the status of a specific magnet until it reaches
524
+ * a target status (default: 'Ready'). For advanced use cases with multiple magnets
525
+ * or bandwidth optimization, use `statusLive()` directly instead.
526
+ *
527
+ * @param id - The magnet ID to watch
376
528
  * @param options - Watch options
529
+ * @param options.interval - Polling interval in milliseconds (default: 3000)
530
+ * @param options.maxAttempts - Maximum polling attempts, 0 for infinite (default: 0)
531
+ * @param options.onUpdate - Callback called on each status update
532
+ * @param options.stopOnStatus - Stop when magnet reaches this status (default: 'Ready')
377
533
  *
378
534
  * @example
379
535
  * ```ts
380
- * await client.magnet.watch('123', {
536
+ * await client.magnet.watch(123, {
381
537
  * onUpdate: (status) => console.log('Status:', status.magnets[0]?.status),
382
538
  * stopOnStatus: 'Ready'
383
539
  * })
384
540
  * ```
541
+ *
542
+ * @remarks
543
+ * For monitoring multiple magnets efficiently, use `statusLive()` directly.
544
+ * See the `statusLive()` documentation for details on delta synchronization.
385
545
  */
386
546
  async watch(id, options = {}) {
387
547
  const { interval = 3e3, maxAttempts = 0, onUpdate, stopOnStatus = "Ready" } = options;
@@ -402,6 +562,111 @@ var MagnetResource = class extends BaseResource {
402
562
  }
403
563
  };
404
564
 
565
+ // src/resources/pin.ts
566
+ var PinResource = class extends BaseResource {
567
+ /**
568
+ * Generate a new PIN code for authentication
569
+ *
570
+ * This initiates the PIN authentication flow. The user should visit the
571
+ * returned URL to authorize the application.
572
+ *
573
+ * @example
574
+ * ```ts
575
+ * const pinData = await client.pin.generate()
576
+ * console.log('Visit:', pinData.user_url)
577
+ * console.log('PIN:', pinData.pin)
578
+ *
579
+ * // Poll the check endpoint until user authorizes
580
+ * const auth = await client.pin.check(pinData.check, pinData.pin)
581
+ * if (auth.activated) {
582
+ * console.log('API Key:', auth.apikey)
583
+ * }
584
+ * ```
585
+ *
586
+ * @returns PIN code and authorization URL
587
+ */
588
+ async generate() {
589
+ return super.get("/pin/get");
590
+ }
591
+ /**
592
+ * Check the status of a PIN authentication
593
+ *
594
+ * Poll this endpoint to check if the user has authorized the application.
595
+ * Once authorized, the response will include the API key.
596
+ *
597
+ * @param check - Check ID from /pin/get
598
+ * @param pin - PIN code from /pin/get
599
+ *
600
+ * @example
601
+ * ```ts
602
+ * const pinData = await client.pin.generate()
603
+ *
604
+ * // Poll every few seconds until activated
605
+ * const checkStatus = async () => {
606
+ * const result = await client.pin.check(pinData.check, pinData.pin)
607
+ * if (result?.activated && result?.apikey) {
608
+ * console.log('Authorized! API Key:', result.apikey)
609
+ * return result.apikey
610
+ * }
611
+ * // Wait and try again
612
+ * await new Promise(resolve => setTimeout(resolve, 3000))
613
+ * return checkStatus()
614
+ * }
615
+ *
616
+ * const apikey = await checkStatus()
617
+ * ```
618
+ *
619
+ * @returns Authorization status and API key (if activated)
620
+ */
621
+ async check(check, pin) {
622
+ return super.get("/pin/check", { check, pin });
623
+ }
624
+ /**
625
+ * Helper method to wait for PIN authorization with automatic polling
626
+ *
627
+ * This method handles the polling logic for you, making it easier to
628
+ * implement the PIN authentication flow.
629
+ *
630
+ * @param check - Check ID from /pin/get
631
+ * @param pin - PIN code from /pin/get
632
+ * @param options - Polling options
633
+ * @param options.timeout - Maximum time to wait in milliseconds (default: 600000 = 10 minutes)
634
+ * @param options.interval - Polling interval in milliseconds (default: 3000 = 3 seconds)
635
+ *
636
+ * @example
637
+ * ```ts
638
+ * const pinData = await client.pin.generate()
639
+ * console.log('Visit:', pinData.user_url)
640
+ *
641
+ * try {
642
+ * const apikey = await client.pin.waitForAuth(pinData.check, pinData.pin, {
643
+ * timeout: 600000, // 10 minutes
644
+ * interval: 3000, // Check every 3 seconds
645
+ * })
646
+ * console.log('Authorized! API Key:', apikey)
647
+ * } catch (error) {
648
+ * console.error('Authorization timed out or failed')
649
+ * }
650
+ * ```
651
+ *
652
+ * @returns The API key once authorized
653
+ * @throws Error if timeout is reached or authorization fails
654
+ */
655
+ async waitForAuth(check, pin, options = {}) {
656
+ const timeout = options.timeout ?? 6e5;
657
+ const interval = options.interval ?? 3e3;
658
+ const startTime = Date.now();
659
+ while (Date.now() - startTime < timeout) {
660
+ const result = await this.check(check, pin);
661
+ if (result?.activated && result?.apikey) {
662
+ return result.apikey;
663
+ }
664
+ await new Promise((resolve) => setTimeout(resolve, interval));
665
+ }
666
+ throw new Error("PIN authorization timeout - user did not authorize within the time limit");
667
+ }
668
+ };
669
+
405
670
  // src/resources/user.ts
406
671
  var UserResource = class extends BaseResource {
407
672
  /**
@@ -409,13 +674,12 @@ var UserResource = class extends BaseResource {
409
674
  *
410
675
  * @example
411
676
  * ```ts
412
- * const user = await client.user.getInfo()
413
- * console.log(user.username, user.isPremium)
677
+ * const data = await client.user.getInfo()
678
+ * console.log(data.user.username, data.user.isPremium)
414
679
  * ```
415
680
  */
416
681
  async getInfo() {
417
- const data = await this.get("/user");
418
- return data?.user;
682
+ return this.get("/user");
419
683
  }
420
684
  /**
421
685
  * Get available hosts for the current user based on their subscription
@@ -443,7 +707,7 @@ var UserResource = class extends BaseResource {
443
707
  * ```
444
708
  */
445
709
  async clearNotification(code) {
446
- await this.get("/user/notification/clear", { code });
710
+ await this.post("/user/notification/clear", { code });
447
711
  }
448
712
  // ============================================
449
713
  // Saved Links Management
@@ -461,30 +725,60 @@ var UserResource = class extends BaseResource {
461
725
  return this.get("/user/links");
462
726
  }
463
727
  /**
464
- * Save a link for later use
728
+ * Save one or multiple links for later use
465
729
  *
466
- * @param link - The link to save
730
+ * Supports batch operations - you can save multiple links in a single request.
731
+ *
732
+ * @param links - Single link or array of links to save
467
733
  *
468
734
  * @example
469
735
  * ```ts
736
+ * // Save single link
470
737
  * await client.user.saveLink('https://example.com/file.zip')
738
+ *
739
+ * // Save multiple links at once
740
+ * await client.user.saveLink([
741
+ * 'https://example.com/file1.zip',
742
+ * 'https://example.com/file2.zip',
743
+ * 'https://example.com/file3.zip'
744
+ * ])
471
745
  * ```
472
746
  */
473
- async saveLink(link) {
474
- return this.get("/user/links/save", { link });
747
+ async saveLink(links) {
748
+ const linksArray = Array.isArray(links) ? links : [links];
749
+ const formData = new FormData();
750
+ for (const link of linksArray) {
751
+ formData.append("links[]", link);
752
+ }
753
+ return this.postFormData("/user/links/save", formData);
475
754
  }
476
755
  /**
477
- * Delete a saved link
756
+ * Delete one or multiple saved links
478
757
  *
479
- * @param link - The link to delete (can be the saved link ID or URL)
758
+ * Supports batch operations - you can delete multiple links in a single request.
759
+ *
760
+ * @param links - Single link or array of links to delete (can be saved link IDs or URLs)
480
761
  *
481
762
  * @example
482
763
  * ```ts
764
+ * // Delete single link
483
765
  * await client.user.deleteLink('saved-link-id')
766
+ *
767
+ * // Delete multiple links at once
768
+ * await client.user.deleteLink([
769
+ * 'saved-link-id-1',
770
+ * 'saved-link-id-2',
771
+ * 'saved-link-id-3'
772
+ * ])
484
773
  * ```
485
774
  */
486
- async deleteLink(link) {
487
- return this.get("/user/links/delete", { link });
775
+ async deleteLink(links) {
776
+ const linksArray = Array.isArray(links) ? links : [links];
777
+ const formData = new FormData();
778
+ for (const link of linksArray) {
779
+ formData.append("links[]", link);
780
+ }
781
+ return this.postFormData("/user/links/delete", formData);
488
782
  }
489
783
  // ============================================
490
784
  // History Management
@@ -510,12 +804,114 @@ var UserResource = class extends BaseResource {
510
804
  * ```
511
805
  */
512
806
  async clearHistory() {
513
- return this.get("/user/history/delete");
807
+ return this.post("/user/history/delete");
808
+ }
809
+ // ============================================
810
+ // Email Verification
811
+ // ============================================
812
+ /**
813
+ * Check email verification status
814
+ *
815
+ * @param token - Verification token
816
+ *
817
+ * @example
818
+ * ```ts
819
+ * const status = await client.user.getVerificationStatus('verification-token')
820
+ * console.log(status.verif) // 'waiting', 'allowed', or 'denied'
821
+ * ```
822
+ */
823
+ async getVerificationStatus(token) {
824
+ return this.post("/user/verif", void 0, { token });
825
+ }
826
+ /**
827
+ * Resend verification email
828
+ *
829
+ * @param token - Verification token
830
+ *
831
+ * @example
832
+ * ```ts
833
+ * await client.user.resendVerification('verification-token')
834
+ * ```
835
+ */
836
+ async resendVerification(token) {
837
+ return this.post("/user/verif/resend", void 0, { token });
838
+ }
839
+ };
840
+
841
+ // src/resources/voucher.ts
842
+ var VoucherResource = class extends BaseResource {
843
+ /**
844
+ * Get voucher balance for reseller accounts
845
+ *
846
+ * This endpoint allows resellers to check their remaining voucher balance.
847
+ * Only available for accounts with reseller privileges.
848
+ *
849
+ * @example
850
+ * ```ts
851
+ * const balance = await client.voucher.getBalance()
852
+ * console.log('Remaining balance:', balance.balance, '€')
853
+ * ```
854
+ *
855
+ * @returns Voucher balance information
856
+ */
857
+ async getBalance() {
858
+ return this.get("/voucher/balance");
859
+ }
860
+ /**
861
+ * Retrieve existing vouchers from reseller inventory
862
+ *
863
+ * This endpoint retrieves vouchers that were previously generated
864
+ * and are available in your inventory.
865
+ *
866
+ * @param quantity - Optional number of vouchers to retrieve
867
+ *
868
+ * @example
869
+ * ```ts
870
+ * // Get all available vouchers
871
+ * const allVouchers = await client.voucher.getVouchers()
872
+ * console.log('Vouchers:', allVouchers?.codes)
873
+ *
874
+ * // Get specific quantity
875
+ * const fiveVouchers = await client.voucher.getVouchers(5)
876
+ * ```
877
+ *
878
+ * @returns List of voucher codes
879
+ */
880
+ async getVouchers(quantity) {
881
+ const params = quantity ? { quantity } : void 0;
882
+ return this.get("/voucher/get", params);
883
+ }
884
+ /**
885
+ * Generate new vouchers (deducts from reseller balance)
886
+ *
887
+ * This endpoint creates new vouchers and deducts the cost from your
888
+ * reseller account balance.
889
+ *
890
+ * @param quantity - Number of vouchers to generate
891
+ * @param duration - Voucher duration in days
892
+ *
893
+ * @example
894
+ * ```ts
895
+ * // Generate 10 vouchers valid for 30 days
896
+ * const vouchers = await client.voucher.generateVouchers(10, 30)
897
+ * console.log('Generated vouchers:', vouchers?.codes)
898
+ *
899
+ * // Generate 5 vouchers valid for 7 days
900
+ * const weekVouchers = await client.voucher.generateVouchers(5, 7)
901
+ * ```
902
+ *
903
+ * @returns List of newly generated voucher codes
904
+ */
905
+ async generateVouchers(quantity, duration) {
906
+ return this.post("/voucher/generate", {
907
+ quantity,
908
+ duration
909
+ });
514
910
  }
515
911
  };
516
912
 
517
913
  // src/client.ts
518
- var DEFAULT_BASE_URL = "https://api.alldebrid.com/v4";
914
+ var DEFAULT_BASE_URL = "https://api.alldebrid.com/v4.1";
519
915
  var DEFAULT_AGENT = "@alldebrid/sdk";
520
916
  var DEFAULT_TIMEOUT = 3e4;
521
917
  var DEFAULT_MAX_RETRIES = 3;
@@ -537,6 +933,14 @@ var AllDebridClient = class {
537
933
  * Host resource for getting information about supported hosts
538
934
  */
539
935
  host;
936
+ /**
937
+ * Pin resource for PIN-based authentication
938
+ */
939
+ pin;
940
+ /**
941
+ * Voucher resource for reseller voucher management
942
+ */
943
+ voucher;
540
944
  constructor(config) {
541
945
  this.config = {
542
946
  apiKey: config.apiKey,
@@ -550,6 +954,8 @@ var AllDebridClient = class {
550
954
  this.link = new LinkResource(this);
551
955
  this.magnet = new MagnetResource(this);
552
956
  this.host = new HostResource(this);
957
+ this.pin = new PinResource(this);
958
+ this.voucher = new VoucherResource(this);
553
959
  }
554
960
  /**
555
961
  * Build query string from params
@@ -574,8 +980,11 @@ var AllDebridClient = class {
574
980
  }
575
981
  /**
576
982
  * Make a GET request
577
- * @param T - The generated response type (e.g., GetLinkUnlockResponse)
983
+ * @template T - The generated response type (e.g., GetLinkUnlockResponse)
984
+ * @param path - API endpoint path
985
+ * @param params - Optional query parameters
578
986
  * @returns The extracted data from the response (without the { status, data } wrapper)
987
+ * @internal
579
988
  */
580
989
  async get(path, params) {
581
990
  try {
@@ -602,14 +1011,30 @@ var AllDebridClient = class {
602
1011
  }
603
1012
  }
604
1013
  /**
605
- * Make a POST request
606
- * @param T - The generated response type
1014
+ * Make a POST request with application/x-www-form-urlencoded
1015
+ * @template T - The generated response type
1016
+ * @param path - API endpoint path
1017
+ * @param body - Request body (will be converted to URLSearchParams)
1018
+ * @param params - Optional query parameters
607
1019
  * @returns The extracted data from the response (without the { status, data } wrapper)
1020
+ * @internal
608
1021
  */
609
1022
  async post(path, body, params) {
610
1023
  try {
611
1024
  const url = this.buildUrl(path, params);
612
- const json = await wretch__default.default(this.config.baseUrl).auth(`Bearer ${this.config.apiKey}`).url(url).post(body).json();
1025
+ const formData = new URLSearchParams();
1026
+ if (body && typeof body === "object") {
1027
+ for (const [key, value] of Object.entries(body)) {
1028
+ if (value !== void 0 && value !== null) {
1029
+ if (Array.isArray(value)) {
1030
+ value.forEach((v) => formData.append(key, String(v)));
1031
+ } else {
1032
+ formData.append(key, String(value));
1033
+ }
1034
+ }
1035
+ }
1036
+ }
1037
+ const json = await wretch__default.default(this.config.baseUrl).auth(`Bearer ${this.config.apiKey}`).url(url).body(formData).post().json();
613
1038
  if (json.status === "error" && json.error) {
614
1039
  throw createTypedError(json.error.code, json.error.message);
615
1040
  }
@@ -632,8 +1057,12 @@ var AllDebridClient = class {
632
1057
  }
633
1058
  /**
634
1059
  * Make a POST request with FormData (multipart/form-data)
635
- * @param T - The generated response type
1060
+ * @template T - The generated response type
1061
+ * @param path - API endpoint path
1062
+ * @param formData - Form data to send
1063
+ * @param params - Optional query parameters
636
1064
  * @returns The extracted data from the response (without the { status, data } wrapper)
1065
+ * @internal
637
1066
  */
638
1067
  async postFormData(path, formData, params) {
639
1068
  try {
@@ -661,14 +1090,20 @@ var AllDebridClient = class {
661
1090
  }
662
1091
  /**
663
1092
  * Test the API connection
1093
+ *
1094
+ * This endpoint doesn't require authentication and can be used to verify
1095
+ * that the AllDebrid API is reachable.
1096
+ *
1097
+ * @example
1098
+ * ```ts
1099
+ * const result = await client.ping()
1100
+ * console.log(result.ping) // 'pong'
1101
+ * ```
1102
+ *
1103
+ * @returns Ping response with 'pong' message
664
1104
  */
665
1105
  async ping() {
666
- try {
667
- await this.get("/user");
668
- return true;
669
- } catch {
670
- return false;
671
- }
1106
+ return this.get("/ping");
672
1107
  }
673
1108
  };
674
1109
 
@@ -681,7 +1116,9 @@ exports.LinkResource = LinkResource;
681
1116
  exports.MagnetError = MagnetError;
682
1117
  exports.MagnetResource = MagnetResource;
683
1118
  exports.NetworkError = NetworkError;
1119
+ exports.PinResource = PinResource;
684
1120
  exports.UserResource = UserResource;
1121
+ exports.VoucherResource = VoucherResource;
685
1122
  exports.createTypedError = createTypedError;
686
1123
  //# sourceMappingURL=index.cjs.map
687
1124
  //# sourceMappingURL=index.cjs.map