lunibee 0.1.8 → 0.2.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/README.md +2 -0
- package/dist/builders/components.d.ts +5 -0
- package/dist/builders/index.js +72 -12
- package/dist/builders/index.js.map +5 -5
- package/dist/collection/index.d.ts +68 -1
- package/dist/collection/index.js +334 -1
- package/dist/collection/index.js.map +3 -3
- package/dist/core/collector.d.ts +13 -0
- package/dist/core/events.d.ts +29 -3
- package/dist/core/index.d.ts +22 -8
- package/dist/core/index.js +1526 -124
- package/dist/core/index.js.map +26 -22
- package/dist/core/permissions.d.ts +48 -0
- package/dist/index.js +1578 -268
- package/dist/index.js.map +30 -29
- package/dist/managers/advanced.d.ts +116 -0
- package/dist/managers/base.d.ts +13 -2
- package/dist/managers/emoji.d.ts +1 -1
- package/dist/managers/guild.d.ts +28 -2
- package/dist/managers/index.d.ts +37 -5
- package/dist/managers/index.js +870 -152
- package/dist/managers/index.js.map +18 -16
- package/dist/managers/message.d.ts +10 -1
- package/dist/rest/errors.d.ts +33 -0
- package/dist/rest/index.d.ts +1 -1
- package/dist/rest/index.js +52 -6
- package/dist/rest/index.js.map +5 -5
- package/dist/rest/routes.d.ts +20 -0
- package/dist/sharding/index.d.ts +29 -0
- package/dist/sharding/index.js +99 -15
- package/dist/sharding/index.js.map +8 -8
- package/dist/structures/base.d.ts +2 -0
- package/dist/structures/index.js +96 -42
- package/dist/structures/index.js.map +10 -9
- package/dist/structures/interactions.d.ts +26 -0
- package/dist/types/gateway.d.ts +16 -0
- package/dist/types/index.d.ts +55 -1
- package/dist/types/index.js.map +2 -2
- package/dist/voice/index.d.ts +3 -0
- package/dist/voice/index.js +15 -1
- package/dist/voice/index.js.map +3 -3
- package/dist/ws/index.js +13 -5
- package/dist/ws/index.js.map +6 -6
- package/package.json +14 -14
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { Collection } from "@lunibee/collection";
|
|
2
|
+
/** Opt-in per-channel message cache. Messages are temporary data, so they
|
|
3
|
+
* use bounded TTL/LRU storage rather than permanent resource storage. */
|
|
4
|
+
export interface MessageCacheOptions {
|
|
5
|
+
/** Messages kept per channel; least-recently-used are evicted. Default 100. */
|
|
6
|
+
maxSize?: number;
|
|
7
|
+
/** Sliding TTL in ms. Omit for no expiry. */
|
|
8
|
+
ttl?: number;
|
|
9
|
+
}
|
|
2
10
|
import { REST } from "@lunibee/rest";
|
|
3
11
|
import { Message, type ResourceContext } from "@lunibee/structures";
|
|
4
12
|
export type MessageCreateOptions = Record<string, unknown> & {
|
|
@@ -9,8 +17,9 @@ export type MessageEditOptions = Record<string, unknown> & {
|
|
|
9
17
|
};
|
|
10
18
|
export declare class MessageManager {
|
|
11
19
|
#private;
|
|
20
|
+
/** Cached messages; always empty unless a message cache is configured. */
|
|
12
21
|
readonly cache: Collection<string, Message>;
|
|
13
|
-
constructor(rest: REST, context: ResourceContext, channelId: string);
|
|
22
|
+
constructor(rest: REST, context: ResourceContext, channelId: string, cache?: MessageCacheOptions);
|
|
14
23
|
resolve(messageId: string): Promise<Message>;
|
|
15
24
|
send(options: MessageCreateOptions): Promise<Message>;
|
|
16
25
|
fetch(messageId: string): Promise<Message>;
|
package/dist/rest/errors.d.ts
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
|
+
/** Why a REST request failed, for handling without matching on status codes. */
|
|
2
|
+
export type RESTErrorKind =
|
|
3
|
+
/** Cancelled through an AbortSignal. */
|
|
4
|
+
"aborted"
|
|
5
|
+
/** The attempt exceeded the transport timeout. */
|
|
6
|
+
| "timeout"
|
|
7
|
+
/** No response: DNS, TLS, connection reset. */
|
|
8
|
+
| "network"
|
|
9
|
+
/** 429 that outlived the retry budget. */
|
|
10
|
+
| "rateLimited"
|
|
11
|
+
/** 401: invalid or revoked token. */
|
|
12
|
+
| "authentication"
|
|
13
|
+
/** 403: missing access or permissions. */
|
|
14
|
+
| "permission"
|
|
15
|
+
/** 404: unknown resource. */
|
|
16
|
+
| "notFound"
|
|
17
|
+
/** 400 / 422: the request body or parameters were rejected. */
|
|
18
|
+
| "validation"
|
|
19
|
+
/** 5xx: Discord failed to handle the request. */
|
|
20
|
+
| "server"
|
|
21
|
+
/** Any other 4xx. */
|
|
22
|
+
| "client";
|
|
23
|
+
/**
|
|
24
|
+
* Replaces the token segment of webhook and interaction paths with `:token`,
|
|
25
|
+
* so request paths can appear in errors, hooks and logs without leaking a
|
|
26
|
+
* credential.
|
|
27
|
+
*/
|
|
28
|
+
export declare function redactPath(path: string): string;
|
|
1
29
|
/** Error thrown when Discord rejects a REST request. */
|
|
2
30
|
export declare class RESTError extends Error {
|
|
31
|
+
/** Failure category. */ readonly kind: RESTErrorKind;
|
|
3
32
|
/** HTTP status returned by Discord. */ readonly status: number;
|
|
4
33
|
/** Discord API error code, when provided. */ readonly code?: number;
|
|
5
34
|
/** Raw Discord validation/error payload. */ readonly errors?: unknown;
|
|
@@ -10,7 +39,11 @@ export declare class RESTError extends Error {
|
|
|
10
39
|
method?: string;
|
|
11
40
|
path?: string;
|
|
12
41
|
cause?: unknown;
|
|
42
|
+
kind?: RESTErrorKind;
|
|
13
43
|
});
|
|
44
|
+
/** Whether sending the same request again later could succeed. Validation,
|
|
45
|
+
* permission, authentication and not-found failures never will. */
|
|
46
|
+
get retryable(): boolean;
|
|
14
47
|
}
|
|
15
48
|
/** Creates the cancellation error shared by every stage of the request pipeline. */
|
|
16
49
|
export declare function abortError(path: string, reason: unknown): RESTError;
|
package/dist/rest/index.d.ts
CHANGED
|
@@ -160,7 +160,7 @@ export declare class REST {
|
|
|
160
160
|
patchWithFiles<T>(path: string, payload: unknown, files: RESTFileAttachment[], options?: RESTRequestOptions): Promise<T>;
|
|
161
161
|
}
|
|
162
162
|
export { Routes } from "./routes.js";
|
|
163
|
-
export { RESTError, abortError } from "./errors.js";
|
|
163
|
+
export { RESTError, abortError, redactPath, type RESTErrorKind, } from "./errors.js";
|
|
164
164
|
export { createRouteKey, normalizeRoutePath, majorParameter, scopeBucket, serializeQuery, type RouteKey, type RESTQuery, } from "./route.js";
|
|
165
165
|
export { RequestScheduler } from "./scheduler.js";
|
|
166
166
|
export { RateLimiter } from "./limiter.js";
|
package/dist/rest/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// packages/rest/package.json
|
|
3
3
|
var package_default = {
|
|
4
4
|
name: "@lunibee/rest",
|
|
5
|
-
version: "0.
|
|
5
|
+
version: "0.2.0",
|
|
6
6
|
type: "module",
|
|
7
7
|
module: "./src/index.ts",
|
|
8
8
|
types: "./src/index.ts",
|
|
@@ -62,7 +62,29 @@ class MemoryRateLimitStore {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// packages/rest/src/errors.ts
|
|
65
|
+
function kindFromStatus(status) {
|
|
66
|
+
if (status === 429)
|
|
67
|
+
return "rateLimited";
|
|
68
|
+
if (status === 401)
|
|
69
|
+
return "authentication";
|
|
70
|
+
if (status === 403)
|
|
71
|
+
return "permission";
|
|
72
|
+
if (status === 404)
|
|
73
|
+
return "notFound";
|
|
74
|
+
if (status === 400 || status === 422)
|
|
75
|
+
return "validation";
|
|
76
|
+
if (status >= 500)
|
|
77
|
+
return "server";
|
|
78
|
+
if (status === 0)
|
|
79
|
+
return "network";
|
|
80
|
+
return "client";
|
|
81
|
+
}
|
|
82
|
+
function redactPath(path) {
|
|
83
|
+
return path.replace(/^(\/webhooks\/[^/?]+\/)[^/?]+/, "$1:token").replace(/^(\/interactions\/[^/?]+\/)[^/?]+/, "$1:token");
|
|
84
|
+
}
|
|
85
|
+
|
|
65
86
|
class RESTError extends Error {
|
|
87
|
+
kind;
|
|
66
88
|
status;
|
|
67
89
|
code;
|
|
68
90
|
errors;
|
|
@@ -72,14 +94,18 @@ class RESTError extends Error {
|
|
|
72
94
|
super(message, options.cause === undefined ? undefined : { cause: options.cause });
|
|
73
95
|
this.name = "RESTError";
|
|
74
96
|
this.status = status;
|
|
97
|
+
this.kind = options.kind ?? kindFromStatus(status);
|
|
75
98
|
this.code = code;
|
|
76
99
|
this.errors = errors;
|
|
77
100
|
this.method = options.method;
|
|
78
|
-
this.path = options.path;
|
|
101
|
+
this.path = options.path === undefined ? undefined : redactPath(options.path);
|
|
102
|
+
}
|
|
103
|
+
get retryable() {
|
|
104
|
+
return this.kind === "rateLimited" || this.kind === "server" || this.kind === "network" || this.kind === "timeout";
|
|
79
105
|
}
|
|
80
106
|
}
|
|
81
107
|
function abortError(path, reason) {
|
|
82
|
-
return new RESTError(reason instanceof Error ? reason.message : "Discord REST request was aborted", 0, undefined, undefined, { path, cause: reason });
|
|
108
|
+
return new RESTError(reason instanceof Error ? reason.message : "Discord REST request was aborted", 0, undefined, undefined, { path, cause: reason, kind: "aborted" });
|
|
83
109
|
}
|
|
84
110
|
async function sleep(delay, signal, path) {
|
|
85
111
|
if (delay <= 0)
|
|
@@ -414,8 +440,23 @@ var Routes = {
|
|
|
414
440
|
invite: (code) => `/invites/${encodeURIComponent(code)}`,
|
|
415
441
|
guildVanity: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/vanity-url`,
|
|
416
442
|
guildEmoji: (guildId, emojiId) => emojiId ? `/guilds/${snowflake(guildId, "Guild ID")}/emojis/${snowflake(emojiId, "Emoji ID")}` : `/guilds/${snowflake(guildId, "Guild ID")}/emojis`,
|
|
417
|
-
applicationEmoji: (applicationId, emojiId) => emojiId ? `/applications/${snowflake(applicationId, "Application ID")}/emojis/${snowflake(emojiId, "Emoji ID")}` : `/applications/${snowflake(applicationId, "Application ID")}/emojis
|
|
443
|
+
applicationEmoji: (applicationId, emojiId) => emojiId ? `/applications/${snowflake(applicationId, "Application ID")}/emojis/${snowflake(emojiId, "Emoji ID")}` : `/applications/${snowflake(applicationId, "Application ID")}/emojis`,
|
|
444
|
+
pollExpire: (channelId, messageId) => `/channels/${snowflake(channelId, "Channel ID")}/polls/${snowflake(messageId, "Message ID")}/expire`,
|
|
445
|
+
pollAnswerVoters: (channelId, messageId, answerId) => `/channels/${snowflake(channelId, "Channel ID")}/polls/${snowflake(messageId, "Message ID")}/answers/${integer(answerId, "Answer ID")}`,
|
|
446
|
+
guildSticker: (guildId, stickerId) => stickerId ? `/guilds/${snowflake(guildId, "Guild ID")}/stickers/${snowflake(stickerId, "Sticker ID")}` : `/guilds/${snowflake(guildId, "Guild ID")}/stickers`,
|
|
447
|
+
sendSoundboardSound: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/send-soundboard-sound`,
|
|
448
|
+
soundboardDefaultSounds: () => "/soundboard-default-sounds",
|
|
449
|
+
guildSoundboardSound: (guildId, soundId) => soundId ? `/guilds/${snowflake(guildId, "Guild ID")}/soundboard-sounds/${snowflake(soundId, "Sound ID")}` : `/guilds/${snowflake(guildId, "Guild ID")}/soundboard-sounds`,
|
|
450
|
+
applicationSkus: (applicationId) => `/applications/${snowflake(applicationId, "Application ID")}/skus`,
|
|
451
|
+
applicationEntitlement: (applicationId, entitlementId) => entitlementId ? `/applications/${snowflake(applicationId, "Application ID")}/entitlements/${snowflake(entitlementId, "Entitlement ID")}` : `/applications/${snowflake(applicationId, "Application ID")}/entitlements`,
|
|
452
|
+
consumeEntitlement: (applicationId, entitlementId) => `/applications/${snowflake(applicationId, "Application ID")}/entitlements/${snowflake(entitlementId, "Entitlement ID")}/consume`,
|
|
453
|
+
skuSubscription: (skuId, subscriptionId) => subscriptionId ? `/skus/${snowflake(skuId, "SKU ID")}/subscriptions/${snowflake(subscriptionId, "Subscription ID")}` : `/skus/${snowflake(skuId, "SKU ID")}/subscriptions`
|
|
418
454
|
};
|
|
455
|
+
function integer(value, field) {
|
|
456
|
+
if (!Number.isInteger(value) || value < 0)
|
|
457
|
+
throw new TypeError(`${field} must be a non-negative integer.`);
|
|
458
|
+
return String(value);
|
|
459
|
+
}
|
|
419
460
|
function snowflake(value, field) {
|
|
420
461
|
if (!/^\d{1,20}$/.test(value))
|
|
421
462
|
throw new TypeError(`${field} must be a valid Discord snowflake.`);
|
|
@@ -729,7 +770,8 @@ class REST {
|
|
|
729
770
|
throw new RESTError(failure.message, 0, undefined, undefined, {
|
|
730
771
|
method,
|
|
731
772
|
path,
|
|
732
|
-
cause: failure.cause ?? failure
|
|
773
|
+
cause: failure.cause ?? failure,
|
|
774
|
+
kind: failure.timedOut ? "timeout" : "network"
|
|
733
775
|
});
|
|
734
776
|
}
|
|
735
777
|
this.#emit(this.#hooks.onResponse, {
|
|
@@ -828,6 +870,9 @@ class REST {
|
|
|
828
870
|
#emit(hook, ctx) {
|
|
829
871
|
if (!hook)
|
|
830
872
|
return;
|
|
873
|
+
const withPath = ctx;
|
|
874
|
+
if (typeof withPath.path === "string")
|
|
875
|
+
ctx = { ...ctx, path: redactPath(withPath.path) };
|
|
831
876
|
Promise.resolve().then(() => hook(ctx)).catch(() => {});
|
|
832
877
|
}
|
|
833
878
|
}
|
|
@@ -849,8 +894,9 @@ export {
|
|
|
849
894
|
createRouteKey,
|
|
850
895
|
majorParameter,
|
|
851
896
|
normalizeRoutePath,
|
|
897
|
+
redactPath,
|
|
852
898
|
scopeBucket,
|
|
853
899
|
serializeQuery
|
|
854
900
|
};
|
|
855
901
|
|
|
856
|
-
//# debugId=
|
|
902
|
+
//# debugId=3E6A1976705386FE64756E2164756E21
|