lunibee 0.3.0 → 0.3.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 (38) hide show
  1. package/dist/builders/embed.d.ts +1 -0
  2. package/dist/builders/index.js +6 -2
  3. package/dist/builders/index.js.map +4 -4
  4. package/dist/core/index.js +66 -15
  5. package/dist/core/index.js.map +8 -8
  6. package/dist/index.js +2345 -199
  7. package/dist/index.js.map +22 -14
  8. package/dist/managers/index.js +2 -2
  9. package/dist/managers/index.js.map +3 -3
  10. package/dist/rest/errors.d.ts +8 -0
  11. package/dist/rest/index.d.ts +2 -0
  12. package/dist/rest/index.js +65 -14
  13. package/dist/rest/index.js.map +5 -5
  14. package/dist/rest/transport.d.ts +9 -0
  15. package/dist/sharding/bus.d.ts +8 -0
  16. package/dist/sharding/cluster.d.ts +224 -14
  17. package/dist/sharding/context.d.ts +41 -0
  18. package/dist/sharding/gateway-bot.d.ts +20 -0
  19. package/dist/sharding/health.d.ts +94 -0
  20. package/dist/sharding/host.d.ts +70 -0
  21. package/dist/sharding/identify.d.ts +62 -0
  22. package/dist/sharding/index.d.ts +56 -12
  23. package/dist/sharding/index.js +2255 -164
  24. package/dist/sharding/index.js.map +18 -10
  25. package/dist/sharding/protocol.d.ts +47 -0
  26. package/dist/sharding/remote-protocol.d.ts +100 -0
  27. package/dist/sharding/remote.d.ts +118 -0
  28. package/dist/sharding/requests.d.ts +54 -0
  29. package/dist/sharding/worker.d.ts +45 -0
  30. package/dist/structures/index.js +2 -2
  31. package/dist/structures/index.js.map +3 -3
  32. package/dist/types/gateway.d.ts +3 -0
  33. package/dist/types/index.js.map +1 -1
  34. package/dist/ws/index.d.ts +1 -1
  35. package/dist/ws/index.js +2 -2
  36. package/dist/ws/index.js.map +5 -5
  37. package/dist/ws/send-budget.d.ts +1 -1
  38. package/package.json +18 -16
@@ -26,6 +26,14 @@ export type RESTErrorKind =
26
26
  * credential.
27
27
  */
28
28
  export declare function redactPath(path: string): string;
29
+ /** Hides webhook and interaction tokens in free text. */
30
+ export declare function redactText(text: string): string;
31
+ /**
32
+ * A copy of a lower-level error that is safe to attach as a `cause`: the same
33
+ * name and code, with tokens removed from the message and stack. Runtimes put
34
+ * the request URL in network error messages, and the URL holds webhook tokens.
35
+ */
36
+ export declare function sanitizeCause(error: unknown): unknown;
29
37
  /** Error thrown when Discord rejects a REST request. */
30
38
  export declare class RESTError extends Error {
31
39
  /** Failure category. */
@@ -107,6 +107,8 @@ export declare class REST {
107
107
  retries?: number;
108
108
  retryPolicy?: RetryPolicy;
109
109
  baseURL?: string;
110
+ /** Allow a cleartext `http://` base URL on a network you trust (a REST proxy). Default false. */
111
+ allowInsecureBaseURL?: boolean;
110
112
  hooks?: RESTHooks;
111
113
  store?: RateLimitStore;
112
114
  /** Replaces the HTTP stage, e.g. with a recording transport in tests. */
@@ -2,7 +2,7 @@
2
2
  // packages/rest/package.json
3
3
  var package_default = {
4
4
  name: "@lunibee/rest",
5
- version: "0.3.0",
5
+ version: "0.3.1",
6
6
  type: "module",
7
7
  module: "./src/index.ts",
8
8
  types: "./src/index.ts",
@@ -85,6 +85,24 @@ function kindFromStatus(status) {
85
85
  function redactPath(path) {
86
86
  return path.replace(/^(\/webhooks\/[^/?]+\/)[^/?]+/i, "$1:token").replace(/^(\/interactions\/[^/?]+\/)[^/?]+/i, "$1:token");
87
87
  }
88
+ var TOKEN_IN_TEXT = /(\/(?:webhooks|interactions)\/[^/\s?"'`]+\/)[^/\s?"'`]+/gi;
89
+ function redactText(text) {
90
+ return text.replace(TOKEN_IN_TEXT, "$1:token");
91
+ }
92
+ function sanitizeCause(error) {
93
+ if (!(error instanceof Error))
94
+ return;
95
+ const copy = new Error(redactText(error.message));
96
+ copy.name = error.name;
97
+ if (error.stack)
98
+ copy.stack = redactText(error.stack);
99
+ for (const key of ["code", "errno", "syscall"]) {
100
+ const value = error[key];
101
+ if (typeof value === "string" || typeof value === "number")
102
+ copy[key] = value;
103
+ }
104
+ return copy;
105
+ }
88
106
  var HINTS = {
89
107
  50001: "The bot cannot see this resource: check that it is in the guild and has View Channel there.",
90
108
  50013: "The bot lacks a permission for this action, or the target role or member is at or above the bot's highest role."
@@ -120,14 +138,19 @@ async function sleep(delay, signal, path) {
120
138
  if (delay <= 0)
121
139
  return;
122
140
  await new Promise((resolve, reject) => {
123
- const timer = setTimeout(resolve, delay);
124
- const abort = () => {
141
+ const finish = (error) => {
125
142
  clearTimeout(timer);
126
- reject(abortError(path, signal?.reason));
143
+ signal?.removeEventListener("abort", onAbort);
144
+ if (error)
145
+ reject(error);
146
+ else
147
+ resolve();
127
148
  };
149
+ const onAbort = () => finish(abortError(path, signal?.reason));
150
+ const timer = setTimeout(finish, delay);
128
151
  if (signal?.aborted)
129
- return abort();
130
- signal?.addEventListener("abort", abort, { once: true });
152
+ return onAbort();
153
+ signal?.addEventListener("abort", onAbort, { once: true });
131
154
  });
132
155
  }
133
156
  async function abortable(promise, signal, path) {
@@ -135,10 +158,17 @@ async function abortable(promise, signal, path) {
135
158
  return promise;
136
159
  if (signal.aborted)
137
160
  throw abortError(path, signal.reason);
138
- return Promise.race([
139
- promise,
140
- new Promise((_, reject) => signal.addEventListener("abort", () => reject(abortError(path, signal.reason)), { once: true }))
141
- ]);
161
+ return new Promise((resolve, reject) => {
162
+ const onAbort = () => reject(abortError(path, signal.reason));
163
+ signal.addEventListener("abort", onAbort, { once: true });
164
+ promise.then(() => {
165
+ signal.removeEventListener("abort", onAbort);
166
+ resolve();
167
+ }, (error) => {
168
+ signal.removeEventListener("abort", onAbort);
169
+ reject(error);
170
+ });
171
+ });
142
172
  }
143
173
 
144
174
  // packages/rest/src/route.ts
@@ -312,6 +342,25 @@ class RateLimiter {
312
342
  }
313
343
 
314
344
  // packages/rest/src/transport.ts
345
+ var LOOPBACK = /^(?:localhost|127(?:\.\d{1,3}){3}|\[::1\])$/i;
346
+ function parseBaseURL(raw, allowInsecure = false) {
347
+ let url;
348
+ try {
349
+ url = new URL(raw);
350
+ } catch {
351
+ throw new TypeError("baseURL must be an absolute http(s) URL.");
352
+ }
353
+ if (url.protocol !== "https:" && url.protocol !== "http:")
354
+ throw new TypeError("baseURL must use https.");
355
+ if (url.username || url.password)
356
+ throw new TypeError("baseURL must not contain credentials.");
357
+ if (url.search || url.hash)
358
+ throw new TypeError("baseURL must not contain a query or fragment.");
359
+ if (url.protocol === "http:" && !allowInsecure && !LOOPBACK.test(url.hostname))
360
+ throw new TypeError("baseURL must use https: the bot token would travel in cleartext (allowInsecureBaseURL: true overrides this for a trusted network).");
361
+ return raw.replace(/\/$/, "");
362
+ }
363
+
315
364
  class TransportError extends Error {
316
365
  timedOut;
317
366
  constructor(message, timedOut, cause) {
@@ -326,7 +375,7 @@ class HttpTransport {
326
375
  #timeout;
327
376
  #fetch;
328
377
  constructor(options = {}) {
329
- this.#baseURL = (options.baseURL ?? "https://discord.com/api/v10").replace(/\/$/, "");
378
+ this.#baseURL = parseBaseURL(options.baseURL ?? "https://discord.com/api/v10", options.allowInsecureBaseURL);
330
379
  this.#timeout = Math.max(1, options.timeout ?? 15000);
331
380
  this.#fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
332
381
  }
@@ -348,13 +397,14 @@ class HttpTransport {
348
397
  method: request.method,
349
398
  headers: request.headers,
350
399
  body: request.body,
351
- signal: controller.signal
400
+ signal: controller.signal,
401
+ redirect: "error"
352
402
  });
353
403
  } catch (error) {
354
404
  if (error instanceof RESTError)
355
405
  throw error;
356
406
  const timedOut = error instanceof DOMException && (error.name === "TimeoutError" || error.name === "AbortError");
357
- throw new TransportError(timedOut ? `Discord REST request timed out after ${this.#timeout}ms` : "Discord REST request failed", timedOut, error);
407
+ throw new TransportError(timedOut ? `Discord REST request timed out after ${this.#timeout}ms` : "Discord REST request failed", timedOut, sanitizeCause(error));
358
408
  } finally {
359
409
  clearTimeout(timer);
360
410
  request.signal?.removeEventListener("abort", onAbort);
@@ -793,6 +843,7 @@ class REST {
793
843
  this.#retryPolicy = options.retryPolicy ?? createRetryPolicy(options.retries ?? 2);
794
844
  this.#transport = options.transport ?? new HttpTransport({
795
845
  baseURL: options.baseURL,
846
+ allowInsecureBaseURL: options.allowInsecureBaseURL,
796
847
  timeout: options.timeout
797
848
  });
798
849
  const store = options.store ?? new MemoryRateLimitStore;
@@ -999,4 +1050,4 @@ export {
999
1050
  serializeQuery
1000
1051
  };
1001
1052
 
1002
- //# debugId=76878940534439C564756E2164756E21
1053
+ //# debugId=67C38AAB1AEA94A764756E2164756E21