tsarr 2.9.0 → 2.10.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 (49) hide show
  1. package/README.md +7 -5
  2. package/dist/cli/commands/bazarr.d.ts +2 -0
  3. package/dist/cli/commands/bazarr.d.ts.map +1 -1
  4. package/dist/cli/commands/config.d.ts.map +1 -1
  5. package/dist/cli/commands/lidarr.d.ts +2 -0
  6. package/dist/cli/commands/lidarr.d.ts.map +1 -1
  7. package/dist/cli/commands/radarr.d.ts.map +1 -1
  8. package/dist/cli/commands/readarr.d.ts +2 -0
  9. package/dist/cli/commands/readarr.d.ts.map +1 -1
  10. package/dist/cli/commands/sonarr.d.ts.map +1 -1
  11. package/dist/cli/index.js +2138 -2405
  12. package/dist/clients/base.d.ts +136 -0
  13. package/dist/clients/base.d.ts.map +1 -0
  14. package/dist/clients/bazarr.d.ts +1 -1
  15. package/dist/clients/bazarr.d.ts.map +1 -1
  16. package/dist/clients/bazarr.js +100 -3
  17. package/dist/clients/lidarr.d.ts +24 -681
  18. package/dist/clients/lidarr.d.ts.map +1 -1
  19. package/dist/clients/lidarr.js +323 -196
  20. package/dist/clients/prowlarr.d.ts +25 -638
  21. package/dist/clients/prowlarr.d.ts.map +1 -1
  22. package/dist/clients/prowlarr.js +331 -175
  23. package/dist/clients/qbittorrent.d.ts +1 -17
  24. package/dist/clients/qbittorrent.d.ts.map +1 -1
  25. package/dist/clients/qbittorrent.js +97 -2
  26. package/dist/clients/radarr.d.ts +4 -657
  27. package/dist/clients/radarr.d.ts.map +1 -1
  28. package/dist/clients/radarr.js +323 -164
  29. package/dist/clients/readarr.d.ts +4 -635
  30. package/dist/clients/readarr.d.ts.map +1 -1
  31. package/dist/clients/readarr.js +323 -164
  32. package/dist/clients/seerr.d.ts +1 -1
  33. package/dist/clients/seerr.d.ts.map +1 -1
  34. package/dist/clients/seerr.js +100 -3
  35. package/dist/clients/sonarr.d.ts +181 -772
  36. package/dist/clients/sonarr.d.ts.map +1 -1
  37. package/dist/clients/sonarr.js +346 -157
  38. package/dist/core/client.d.ts +3 -0
  39. package/dist/core/client.d.ts.map +1 -1
  40. package/dist/core/fetch.d.ts +23 -0
  41. package/dist/core/fetch.d.ts.map +1 -0
  42. package/dist/core/index.d.ts +1 -0
  43. package/dist/core/index.d.ts.map +1 -1
  44. package/dist/core/types.d.ts +7 -0
  45. package/dist/core/types.d.ts.map +1 -1
  46. package/dist/index.js +1 -1
  47. package/dist/tsarr-2.10.0.tgz +0 -0
  48. package/package.json +2 -4
  49. package/dist/tsarr-2.9.0.tgz +0 -0
@@ -18,6 +18,93 @@ class ConnectionError extends TsarrError {
18
18
  }
19
19
  }
20
20
 
21
+ // src/core/fetch.ts
22
+ var DEFAULT_TIMEOUT = 30000;
23
+ var DEFAULT_MAX_RETRIES = 3;
24
+ var DEFAULT_INITIAL_DELAY = 1000;
25
+ var DEFAULT_MAX_DELAY = 1e4;
26
+ var RETRYABLE_STATUS_CODES = new Set([408, 429, 502, 503, 504]);
27
+ function isRetryable(error) {
28
+ if (error instanceof DOMException && error.name === "AbortError") {
29
+ return false;
30
+ }
31
+ if (error instanceof TypeError) {
32
+ return true;
33
+ }
34
+ return false;
35
+ }
36
+ function getRetryDelay(attempt, initialDelayMs, maxDelayMs) {
37
+ const delay = initialDelayMs * 2 ** attempt;
38
+ const jitter = delay * 0.2 * Math.random();
39
+ return Math.min(delay + jitter, maxDelayMs);
40
+ }
41
+ function createResilientFetch(options = {}) {
42
+ const timeout = options.timeout ?? DEFAULT_TIMEOUT;
43
+ const maxRetries = options.retry ? options.retry.maxRetries ?? DEFAULT_MAX_RETRIES : 0;
44
+ const initialDelayMs = options.retry?.initialDelayMs ?? DEFAULT_INITIAL_DELAY;
45
+ const maxDelayMs = options.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY;
46
+ const resilientFetch = async (input, init) => {
47
+ let lastError;
48
+ const template = createRequestTemplate(input, init);
49
+ for (let attempt = 0;attempt <= maxRetries; attempt++) {
50
+ const controller = new AbortController;
51
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
52
+ const callerSignal = init?.signal;
53
+ if (callerSignal?.aborted) {
54
+ clearTimeout(timeoutId);
55
+ throw callerSignal.reason ?? new DOMException("The operation was aborted.", "AbortError");
56
+ }
57
+ const onCallerAbort = () => controller.abort(callerSignal.reason);
58
+ callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
59
+ try {
60
+ const response = await globalThis.fetch(new Request(template.clone(), { signal: controller.signal }));
61
+ clearTimeout(timeoutId);
62
+ callerSignal?.removeEventListener("abort", onCallerAbort);
63
+ if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < maxRetries) {
64
+ lastError = new ConnectionError(`Request failed with status ${response.status}`);
65
+ const delay = getRetryDelay(attempt, initialDelayMs, maxDelayMs);
66
+ await new Promise((resolve) => setTimeout(resolve, delay));
67
+ continue;
68
+ }
69
+ return response;
70
+ } catch (error) {
71
+ clearTimeout(timeoutId);
72
+ callerSignal?.removeEventListener("abort", onCallerAbort);
73
+ if (callerSignal?.aborted) {
74
+ throw callerSignal.reason ?? new DOMException("The operation was aborted.", "AbortError");
75
+ }
76
+ if (error instanceof DOMException && error.name === "AbortError") {
77
+ lastError = new ConnectionError(`Request timed out after ${timeout}ms`);
78
+ if (attempt < maxRetries) {
79
+ const delay = getRetryDelay(attempt, initialDelayMs, maxDelayMs);
80
+ await new Promise((resolve) => setTimeout(resolve, delay));
81
+ continue;
82
+ }
83
+ throw lastError;
84
+ }
85
+ if (isRetryable(error) && attempt < maxRetries) {
86
+ lastError = error;
87
+ const delay = getRetryDelay(attempt, initialDelayMs, maxDelayMs);
88
+ await new Promise((resolve) => setTimeout(resolve, delay));
89
+ continue;
90
+ }
91
+ throw error;
92
+ }
93
+ }
94
+ throw lastError;
95
+ };
96
+ return Object.assign(resilientFetch, {
97
+ preconnect: globalThis.fetch.preconnect?.bind(globalThis.fetch)
98
+ });
99
+ }
100
+ function createRequestTemplate(input, init) {
101
+ const { signal: _signal, ...requestInit } = init ?? {};
102
+ if (input instanceof Request) {
103
+ return init ? new Request(input.clone(), requestInit) : input.clone();
104
+ }
105
+ return new Request(input, requestInit);
106
+ }
107
+
21
108
  // src/generated/qbittorrent/core/bodySerializer.gen.ts
22
109
  var serializeUrlSearchParamsPair = (data, key, value) => {
23
110
  if (typeof value === "string") {
@@ -917,11 +1004,14 @@ var torrentsDeletePost = (options) => (options.client ?? client).post({
917
1004
  }
918
1005
  });
919
1006
  // src/clients/qbittorrent.ts
1007
+ var DEFAULT_TIMEOUT_MS = 30000;
1008
+
920
1009
  class QBittorrentClient {
921
1010
  baseUrl;
922
1011
  username;
923
1012
  password;
924
1013
  sid = null;
1014
+ fetch;
925
1015
  constructor(config) {
926
1016
  if (!config.baseUrl) {
927
1017
  throw new ConnectionError("No base URL provided");
@@ -929,9 +1019,14 @@ class QBittorrentClient {
929
1019
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
930
1020
  this.username = config.username;
931
1021
  this.password = config.password;
1022
+ this.fetch = createResilientFetch({
1023
+ timeout: config.timeout ?? DEFAULT_TIMEOUT_MS,
1024
+ retry: config.retry
1025
+ });
932
1026
  client.setConfig({
933
1027
  baseUrl: `${this.baseUrl}/api/v2`,
934
- auth: () => this.ensureAuth()
1028
+ auth: () => this.ensureAuth(),
1029
+ fetch: this.fetch
935
1030
  });
936
1031
  }
937
1032
  async ensureAuth() {
@@ -941,7 +1036,7 @@ class QBittorrentClient {
941
1036
  return this.sid;
942
1037
  }
943
1038
  async login() {
944
- const response = await fetch(`${this.baseUrl}/api/v2/auth/login`, {
1039
+ const response = await this.fetch(`${this.baseUrl}/api/v2/auth/login`, {
945
1040
  method: "POST",
946
1041
  headers: {
947
1042
  "Content-Type": "application/x-www-form-urlencoded",