lunibee 0.1.7 → 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.
Files changed (78) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +225 -223
  3. package/dist/builders/commands.d.ts +6 -4
  4. package/dist/builders/components.d.ts +23 -0
  5. package/dist/builders/index.d.ts +2 -2
  6. package/dist/builders/index.js +120 -24
  7. package/dist/builders/index.js.map +9 -8
  8. package/dist/collection/index.d.ts +68 -1
  9. package/dist/collection/index.js +335 -1
  10. package/dist/collection/index.js.map +4 -4
  11. package/dist/core/collector.d.ts +13 -0
  12. package/dist/core/events.d.ts +112 -2
  13. package/dist/core/index.d.ts +30 -93
  14. package/dist/core/index.js +3386 -957
  15. package/dist/core/index.js.map +48 -24
  16. package/dist/core/permissions.d.ts +48 -0
  17. package/dist/formatters/index.js.map +1 -1
  18. package/dist/handlers/index.js.map +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js +3806 -1193
  21. package/dist/index.js.map +64 -43
  22. package/dist/managers/advanced.d.ts +116 -0
  23. package/dist/managers/base.d.ts +13 -2
  24. package/dist/managers/emoji.d.ts +1 -1
  25. package/dist/managers/guild-resources.d.ts +109 -0
  26. package/dist/managers/guild.d.ts +33 -2
  27. package/dist/managers/index.d.ts +44 -6
  28. package/dist/managers/index.js +1186 -150
  29. package/dist/managers/index.js.map +27 -21
  30. package/dist/managers/message.d.ts +10 -1
  31. package/dist/rest/decoder.d.ts +23 -0
  32. package/dist/rest/errors.d.ts +53 -0
  33. package/dist/rest/index.d.ts +21 -17
  34. package/dist/rest/index.js +526 -264
  35. package/dist/rest/index.js.map +14 -8
  36. package/dist/rest/limiter.d.ts +42 -0
  37. package/dist/rest/redis.d.ts +18 -1
  38. package/dist/rest/route.d.ts +31 -0
  39. package/dist/rest/routes.d.ts +22 -0
  40. package/dist/rest/scheduler.d.ts +42 -0
  41. package/dist/rest/store.d.ts +29 -0
  42. package/dist/rest/transport.d.ts +40 -0
  43. package/dist/sharding/bus.d.ts +31 -0
  44. package/dist/sharding/cluster.d.ts +10 -0
  45. package/dist/sharding/index.d.ts +39 -2
  46. package/dist/sharding/index.js +1105 -404
  47. package/dist/sharding/index.js.map +18 -8
  48. package/dist/structures/base.d.ts +10 -0
  49. package/dist/structures/channels.d.ts +56 -0
  50. package/dist/structures/index.d.ts +1 -0
  51. package/dist/structures/index.js +305 -311
  52. package/dist/structures/index.js.map +18 -14
  53. package/dist/structures/interactions.d.ts +38 -52
  54. package/dist/structures/options.d.ts +55 -0
  55. package/dist/structures/resources.d.ts +3 -3
  56. package/dist/types/gateway-events.d.ts +132 -0
  57. package/dist/types/gateway.d.ts +197 -0
  58. package/dist/types/index.d.ts +53 -260
  59. package/dist/types/index.js +44 -2
  60. package/dist/types/index.js.map +5 -4
  61. package/dist/utils/index.js.map +1 -1
  62. package/dist/voice/index.d.ts +32 -1
  63. package/dist/voice/index.js +73 -7
  64. package/dist/voice/index.js.map +3 -3
  65. package/dist/ws/close-codes.d.ts +23 -0
  66. package/dist/ws/decoder.d.ts +58 -0
  67. package/dist/ws/heartbeat.d.ts +87 -0
  68. package/dist/ws/index.d.ts +18 -65
  69. package/dist/ws/index.js +911 -385
  70. package/dist/ws/index.js.map +15 -5
  71. package/dist/ws/opcodes.d.ts +14 -0
  72. package/dist/ws/protocol.d.ts +110 -0
  73. package/dist/ws/reconnect.d.ts +108 -0
  74. package/dist/ws/send-budget.d.ts +17 -0
  75. package/dist/ws/session.d.ts +100 -0
  76. package/dist/ws/state.d.ts +27 -0
  77. package/dist/ws/transport.d.ts +80 -0
  78. package/package.json +21 -20
@@ -0,0 +1,42 @@
1
+ import type { RateLimitStore } from "./store.js";
2
+ /**
3
+ * Owns Discord's rate-limit state: when a request may go out, and what a
4
+ * response says about the bucket it landed in.
5
+ *
6
+ * The limiter holds no queueing logic of its own — {@link RequestScheduler}
7
+ * decides ordering — and no HTTP knowledge beyond reading rate-limit headers.
8
+ */
9
+ export declare class RateLimiter {
10
+ #private;
11
+ /**
12
+ * @param store Rate-limit state store.
13
+ * @param options `concurrent`: set when several requests per bucket can be
14
+ * in flight, so a late response cannot raise `remaining` within its window.
15
+ */
16
+ constructor(store: RateLimitStore, options?: {
17
+ concurrent?: boolean;
18
+ });
19
+ /** The backing store, shared across processes when a distributed store is used. */
20
+ get store(): RateLimitStore;
21
+ /** Resolves the bucket key a request should be gated on. */
22
+ resolveBucketKey(route: string, major: string): Promise<string>;
23
+ /** Whether the backing store can reserve allowance atomically. */
24
+ get reserves(): boolean;
25
+ /**
26
+ * Waits until both the route bucket and the global limit permit sending.
27
+ *
28
+ * When the store supports it, allowance is *reserved* rather than merely
29
+ * observed. Reading `remaining` and deciding to send is a race across
30
+ * workers: three processes all read `remaining: 1` and all send. A
31
+ * reservation hands the unit to exactly one of them.
32
+ */
33
+ acquire(bucketKey: string, signal: AbortSignal | undefined, path: string): Promise<void>;
34
+ /** Extends the global limit, never shortening a longer one already recorded. */
35
+ noteGlobalReset(resetAt: number): Promise<void>;
36
+ /**
37
+ * Records a response's rate-limit headers and returns the bucket key the
38
+ * request actually belongs to: Discord may reveal a server bucket hash that
39
+ * differs from the route-derived key used before the first response.
40
+ */
41
+ applyResponse(response: Response, bucketKey: string, route: string, major: string): Promise<string>;
42
+ }
@@ -1,4 +1,4 @@
1
- import type { RateLimitStore, BucketState } from "./store.js";
1
+ import { type RateLimitStore, type BucketState, type Reservation } from "./store.js";
2
2
  /**
3
3
  * A Redis client interface that matches the subset of ioredis
4
4
  * methods needed for the RedisRateLimitStore.
@@ -8,6 +8,12 @@ export interface MinimalRedisClient {
8
8
  mget(keys: string[]): Promise<Array<string | null>>;
9
9
  set(key: string, value: string | number, mode?: string, duration?: number): Promise<any>;
10
10
  del(...keys: string[]): Promise<number>;
11
+ /**
12
+ * Optional Lua evaluation (ioredis/node-redis compatible). When present,
13
+ * {@link RedisRateLimitStore.reserve} becomes available and the limiter
14
+ * reserves allowance atomically instead of racing on observed state.
15
+ */
16
+ eval?(script: string, numKeys: number, ...args: Array<string | number>): Promise<unknown>;
11
17
  }
12
18
  export interface RedisRateLimitStoreOptions {
13
19
  /** The Redis client instance (e.g. ioredis). */
@@ -32,6 +38,17 @@ export declare class RedisRateLimitStore implements RateLimitStore {
32
38
  setBucketHash(route: string, hash: string): Promise<void>;
33
39
  getBucket(key: string): Promise<BucketState | undefined>;
34
40
  updateBucket(key: string, state: BucketState): Promise<void>;
41
+ /** Whether the backing client supports atomic reservations. */
42
+ get supportsReservation(): boolean;
43
+ /**
44
+ * Atomically consumes one unit of a bucket's allowance across the fleet.
45
+ *
46
+ * Assigned in the constructor only when the client exposes `eval`, so a
47
+ * client that cannot reserve atomically does not advertise that it can.
48
+ * A Redis failure falls back to the local mirror rather than blocking:
49
+ * refusing every request during an outage would stall the bot.
50
+ */
51
+ reserve?: (key: string) => Promise<Reservation>;
35
52
  getGlobalReset(): Promise<number>;
36
53
  setGlobalReset(resetAt: number): Promise<void>;
37
54
  }
@@ -0,0 +1,31 @@
1
+ /** Query-string value accepted by a request. */
2
+ export type RESTQuery = URLSearchParams | Record<string, string | number | boolean | null | undefined> | string;
3
+ /**
4
+ * Identity of a request as Discord's rate limiter sees it.
5
+ *
6
+ * `route` is the shape of the endpoint (ids replaced by `:id`), which is what
7
+ * Discord returns a bucket hash for. `major` is the resource that gives an
8
+ * endpoint its own independent limit. Both are needed: the hash alone would
9
+ * make two channels share one counter.
10
+ */
11
+ export interface RouteKey {
12
+ /** Uppercased HTTP method. */ method: string;
13
+ /** Raw request path, without query string. */ path: string;
14
+ /** Normalized `METHOD:/path/:id` route used for bucket-hash lookup. */ route: string;
15
+ /** Major parameter scoping the limit (channel, guild, webhook id[:token]). */ major: string;
16
+ }
17
+ /** Normalizes Discord routes for stable bucket discovery. */
18
+ export declare function normalizeRoutePath(path: string): string;
19
+ /**
20
+ * Discord's *major parameters* — the resource ids that give an endpoint its own
21
+ * independent rate limit. Per the API documentation these are `channel_id`,
22
+ * `guild_id` and `webhook_id`; webhook routes are additionally scoped by
23
+ * their token. Any other id in a path shares its limit with sibling resources.
24
+ */
25
+ export declare function majorParameter(path: string): string;
26
+ /** Combines a bucket hash (or route) with a major parameter into a bucket key. */
27
+ export declare function scopeBucket(hashOrRoute: string, major: string): string;
28
+ /** Builds the routing identity for one request. */
29
+ export declare function createRouteKey(method: string, path: string): RouteKey;
30
+ /** Serialises a query into a `?...` suffix (empty string when nothing to add). */
31
+ export declare function serializeQuery(query: RESTQuery | undefined): string;
@@ -39,6 +39,8 @@ export declare const Routes: {
39
39
  /** Returns a thread's members. @param threadId Thread identifier. @returns Thread-member route. */ readonly threadMembers: (threadId: string) => string;
40
40
  /** Returns guild scheduled events. @param guildId Guild identifier. @returns Scheduled-event collection route. */ readonly guildScheduledEvents: (guildId: string) => string;
41
41
  /** Returns one guild scheduled event. @param guildId Guild identifier. @param eventId Event identifier. @returns Scheduled-event route. */ readonly guildScheduledEvent: (guildId: string, eventId: string) => string;
42
+ /** Returns users subscribed to a scheduled event. @param guildId Guild identifier. @param eventId Event identifier. @returns Scheduled-event users route. */ readonly guildScheduledEventUsers: (guildId: string, eventId: string) => string;
43
+ /** Returns a channel permission overwrite. @param channelId Channel identifier. @param overwriteId Role or user identifier. @returns Permission overwrite route. */ readonly channelPermission: (channelId: string, overwriteId: string) => string;
42
44
  /** Returns guild automod rules. @param guildId Guild identifier. @returns Automod-rule collection route. */ readonly guildAutoModerationRules: (guildId: string) => string;
43
45
  /** Returns one automod rule. @param guildId Guild identifier. @param ruleId Rule identifier. @returns Automod-rule route. */ readonly guildAutoModerationRule: (guildId: string, ruleId: string) => string;
44
46
  /** Returns current voice regions. @returns Voice-region route. */ readonly voiceRegions: () => string;
@@ -66,4 +68,24 @@ export declare const Routes: {
66
68
  readonly guildEmoji: (guildId: string, emojiId?: string) => string;
67
69
  /** Returns application emojis, or a specific emoji. @param applicationId Application identifier. @param emojiId Optional emoji identifier. */
68
70
  readonly applicationEmoji: (applicationId: string, emojiId?: string) => string;
71
+ /** Ends a poll early. @param channelId Channel identifier. @param messageId Poll message identifier. */
72
+ readonly pollExpire: (channelId: string, messageId: string) => string;
73
+ /** Returns the users who voted for a poll answer. @param answerId Answer identifier (an integer, not a snowflake). */
74
+ readonly pollAnswerVoters: (channelId: string, messageId: string, answerId: number) => string;
75
+ /** Returns guild stickers, or one sticker. */
76
+ readonly guildSticker: (guildId: string, stickerId?: string) => string;
77
+ /** Plays a soundboard sound in a voice channel. */
78
+ readonly sendSoundboardSound: (channelId: string) => string;
79
+ /** Returns Discord's default soundboard sounds. */
80
+ readonly soundboardDefaultSounds: () => string;
81
+ /** Returns guild soundboard sounds, or one sound. */
82
+ readonly guildSoundboardSound: (guildId: string, soundId?: string) => string;
83
+ /** Returns an application's SKUs. */
84
+ readonly applicationSkus: (applicationId: string) => string;
85
+ /** Returns an application's entitlements, or one entitlement. */
86
+ readonly applicationEntitlement: (applicationId: string, entitlementId?: string) => string;
87
+ /** Marks a one-time-purchase entitlement as consumed. */
88
+ readonly consumeEntitlement: (applicationId: string, entitlementId: string) => string;
89
+ /** Returns a SKU's subscriptions, or one subscription. */
90
+ readonly skuSubscription: (skuId: string, subscriptionId?: string) => string;
69
91
  };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Serialises work per rate-limit bucket.
3
+ *
4
+ * One request per bucket key is in flight at a time, so each request sees the
5
+ * limit state its predecessor's response wrote. The queue slot is released on
6
+ * every exit path — including an abort raised while still waiting — because a
7
+ * slot that is never released wedges its bucket for the process lifetime.
8
+ */
9
+ export declare class RequestScheduler {
10
+ #private;
11
+ /** Maximum times a request may be moved to a different bucket queue. */
12
+ static readonly MAX_REMAPS = 2;
13
+ /** Number of bucket keys currently holding a queue slot. */
14
+ get size(): number;
15
+ /**
16
+ * Runs `task` under the bucket key that `resolveKey` reports *at the moment
17
+ * the slot is acquired*, not at enqueue time.
18
+ *
19
+ * Discord can map several routes onto one bucket hash, and a route only
20
+ * learns its hash from a response. A request that queued under its
21
+ * route-derived key while another request discovered the shared hash would
22
+ * otherwise run concurrently with that bucket's other traffic. Re-resolving
23
+ * after the wait moves it onto the right queue instead.
24
+ *
25
+ * @param resolveKey Returns the current bucket key for this request.
26
+ * @param signal Cancellation signal honoured while queued.
27
+ * @param path Request path, used for cancellation error context.
28
+ * @param task Work to run while holding the slot, given the final key and
29
+ * a callback that frees the slot early.
30
+ */
31
+ runResolved<T>(resolveKey: () => Promise<string>, signal: AbortSignal | undefined, path: string, task: (key: string, release: () => void) => Promise<T>): Promise<T>;
32
+ /**
33
+ * Runs `task` once every earlier task for `key` has settled, or has freed
34
+ * its slot early through the `release` callback it was given.
35
+ * @param key Bucket key to serialise on.
36
+ * @param signal Cancellation signal honoured while queued.
37
+ * @param path Request path, used for cancellation error context.
38
+ * @param task Work to run while holding the slot, given an idempotent
39
+ * callback that frees the slot before the task settles.
40
+ */
41
+ run<T>(key: string, signal: AbortSignal | undefined, path: string, task: (release: () => void) => Promise<T>): Promise<T>;
42
+ }
@@ -2,6 +2,19 @@ export interface BucketState {
2
2
  remaining: number;
3
3
  resetAt: number;
4
4
  }
5
+ /**
6
+ * Outcome of an atomic reservation.
7
+ *
8
+ * `granted` means one unit of the bucket's allowance now belongs to the caller
9
+ * and to nobody else — the distinction that a read-then-write cannot make when
10
+ * several workers share a store.
11
+ */
12
+ export type Reservation = {
13
+ granted: true;
14
+ } | {
15
+ granted: false;
16
+ retryAfterMs: number;
17
+ };
5
18
  /** Interface for distributed or local rate limit synchronization. */
6
19
  export interface RateLimitStore {
7
20
  /** Gets the server bucket hash for a normalized route. */
@@ -16,6 +29,15 @@ export interface RateLimitStore {
16
29
  getGlobalReset(): Promise<number> | number;
17
30
  /** Sets the global reset timestamp. */
18
31
  setGlobalReset(resetAt: number): Promise<void> | void;
32
+ /**
33
+ * Atomically consumes one unit of a bucket's allowance.
34
+ *
35
+ * Optional: a store that cannot do this atomically must omit it, and the
36
+ * limiter falls back to waiting on observed state. Implementations must
37
+ * grant when the bucket is unknown or its window has elapsed — an unknown
38
+ * bucket is discovered by sending, and refusing would deadlock the route.
39
+ */
40
+ reserve?(key: string): Promise<Reservation> | Reservation;
19
41
  /**
20
42
  * Evicts expired bucket entries. Optional: stores with native key
21
43
  * expiry (e.g. Redis) can omit it. `maxAge` (ms) additionally drops
@@ -30,6 +52,13 @@ export declare class MemoryRateLimitStore implements RateLimitStore {
30
52
  getBucketHash(route: string): string | undefined;
31
53
  setBucketHash(route: string, hash: string): void;
32
54
  getBucket(key: string): BucketState | undefined;
55
+ /**
56
+ * Consumes one unit of the bucket's allowance.
57
+ *
58
+ * Single-process reservation is trivially atomic: JavaScript will not
59
+ * interleave this method with another caller's copy of it.
60
+ */
61
+ reserve(key: string): Reservation;
33
62
  updateBucket(key: string, state: BucketState): void;
34
63
  /** Removes buckets whose reset is in the past (or older than `maxAge` ms). */
35
64
  prune(maxAge?: number): void;
@@ -0,0 +1,40 @@
1
+ /** Minimal fetch shape the transport depends on, so a stub needs no extras. */
2
+ export type FetchLike = (input: string, init: RequestInit) => Promise<Response>;
3
+ /** One HTTP attempt, fully resolved: no routing, retry or rate-limit concerns. */
4
+ export interface TransportRequest {
5
+ /** Uppercased HTTP method. */ method: string;
6
+ /** Path with query string, relative to the transport's base URL. */ path: string;
7
+ /** Request headers, already including authorization. */ headers: Record<string, string>;
8
+ /** Encoded body, or undefined for a bodyless request. */ body?: BodyInit;
9
+ /** Caller cancellation signal. */ signal?: AbortSignal;
10
+ }
11
+ /** Thrown by {@link HttpTransport} when the attempt never produced a response. */
12
+ export declare class TransportError extends Error {
13
+ /** Whether the attempt was cut short by the transport timeout or an abort. */
14
+ readonly timedOut: boolean;
15
+ constructor(message: string, timedOut: boolean, cause?: unknown);
16
+ }
17
+ /**
18
+ * Sends one HTTP attempt and returns the raw {@link Response}.
19
+ *
20
+ * Everything above it — routing, rate limiting, retries, decoding — is somebody
21
+ * else's job, which is what makes the layer substitutable in tests.
22
+ */
23
+ export declare class HttpTransport {
24
+ #private;
25
+ constructor(options?: {
26
+ baseURL?: string;
27
+ timeout?: number;
28
+ fetch?: FetchLike;
29
+ });
30
+ /** Base URL every request path is resolved against. */
31
+ get baseURL(): string;
32
+ /** Per-attempt timeout in milliseconds. */
33
+ get timeout(): number;
34
+ /**
35
+ * Performs the attempt.
36
+ * @throws {RESTError} If the caller's signal was already aborted.
37
+ * @throws {TransportError} If the attempt times out or the transport fails.
38
+ */
39
+ send(request: TransportRequest): Promise<Response>;
40
+ }
@@ -5,9 +5,18 @@ export interface ShardMessage<T = unknown> {
5
5
  /** Application message type. */ type: string;
6
6
  /** Message payload. */ data: T;
7
7
  /** Unique message ID. */ id: string;
8
+ /** Set on requests that expect a reply (see {@link ShardBus.request}). */ expectsReply?: boolean;
9
+ }
10
+ /** A reply collected by {@link ShardBus.broadcastRequest}. */
11
+ export interface ShardReply<R = unknown> {
12
+ /** Replying shard ID. */ shardId: number;
13
+ /** Handler result, when it succeeded. */ result?: R;
14
+ /** Handler error message, when it failed. */ error?: string;
8
15
  }
9
16
  /** Handler invoked for shard messages. @typeParam T Message payload type. */
10
17
  export type ShardMessageHandler<T = unknown> = (message: ShardMessage<T>) => unknown;
18
+ /** Listener for errors thrown or rejected by shard message handlers. */
19
+ export type ShardBusErrorHandler = (error: unknown, message: ShardMessage) => void;
11
20
  /** Bun/Node-compatible cross-shard transport using BroadcastChannel. */
12
21
  export declare class ShardBus {
13
22
  #private;
@@ -16,7 +25,29 @@ export declare class ShardBus {
16
25
  constructor(shardId: number, channelName: string);
17
26
  /** Registers a message handler. @param type Message type. @param handler Handler callback. @returns This bus. @throws {TypeError} If type or handler is invalid. */ on<T>(type: string, handler: ShardMessageHandler<T>): this;
18
27
  /** Removes a message handler. @param type Message type. @param handler Handler callback. @returns This bus. */ off<T>(type: string, handler: ShardMessageHandler<T>): this;
28
+ /** Registers a listener for errors thrown or rejected by message handlers. Without one, handler errors are isolated and dropped. @param handler Error listener. @returns This bus. @throws {TypeError} If handler is invalid. */ onError(handler: ShardBusErrorHandler): this;
29
+ /** Removes a handler error listener. @param handler Error listener. @returns This bus. */ offError(handler: ShardBusErrorHandler): this;
19
30
  /** Sends a targeted shard message. @param target Target shard ID. @param type Message type. @param data Payload. @returns Unique message ID. */ send<T>(target: number, type: string, data: T): string;
20
31
  /** Broadcasts to all other shards. @param type Message type. @param data Payload. @returns Unique message ID. */ broadcast<T>(type: string, data: T): string;
32
+ /**
33
+ * Registers a handler that answers requests of `type`. Its return value (or
34
+ * thrown error) is sent back to the requesting shard. Discord.js-familiar
35
+ * replacement for `broadcastEval` that never evaluates received code.
36
+ * @returns This bus.
37
+ */
38
+ respond<T, R>(type: string, handler: (data: T, message: ShardMessage<T>) => R | Promise<R>): this;
39
+ /**
40
+ * Sends a request to one shard and resolves with its handler's result.
41
+ * @throws {Error} If the handler failed or no reply arrives in time.
42
+ */
43
+ request<R = unknown, T = unknown>(target: number, type: string, data: T, timeoutMs?: number): Promise<R>;
44
+ /**
45
+ * Sends a request to every other shard and collects replies until
46
+ * `expected` have arrived or `timeoutMs` elapses (never rejects).
47
+ */
48
+ broadcastRequest<R = unknown, T = unknown>(type: string, data: T, options?: {
49
+ timeoutMs?: number;
50
+ expected?: number;
51
+ }): Promise<ShardReply<R>[]>;
21
52
  /** Closes the transport. @returns Nothing. */ close(): void;
22
53
  }
@@ -15,6 +15,16 @@ export interface ClusterManagerOptions {
15
15
  onAutoScaleError?: (error: Error) => void;
16
16
  /** Grace period in milliseconds to await a cluster's clean exit after SIGTERM before force-killing. Defaults to 5000. */
17
17
  shutdownTimeout?: number;
18
+ /**
19
+ * Whether a cluster that exits unexpectedly is re-forked with the same
20
+ * shard assignment. Defaults to true: without it a crashed child leaves
21
+ * its shards permanently offline and nothing reports it.
22
+ */
23
+ restartOnExit?: boolean;
24
+ /** Delay in milliseconds before re-forking a crashed cluster. Defaults to 5000. */
25
+ restartDelay?: number;
26
+ /** Called whenever a cluster process exits, before any restart. */
27
+ onClusterExit?(cluster: ClusterInfo, code: number | null, signal: NodeJS.Signals | null): void;
18
28
  }
19
29
  /** Information about a running cluster. */
20
30
  export interface ClusterInfo {
@@ -1,6 +1,6 @@
1
1
  import { Gateway } from "@lunibee/ws";
2
2
  export { ShardBus } from "./bus.js";
3
- export type { ShardMessage, ShardMessageHandler } from "./bus.js";
3
+ export type { ShardBusErrorHandler, ShardMessage, ShardMessageHandler, ShardReply, } from "./bus.js";
4
4
  export { ClusterManager } from "./cluster.js";
5
5
  export type { ClusterManagerOptions, ClusterInfo } from "./cluster.js";
6
6
  /** Configuration for a sharded Gateway client. */
@@ -9,10 +9,39 @@ export interface ShardManagerOptions {
9
9
  /** Gateway intents. */ intents: number;
10
10
  /** Number of shards. Use `"auto"` to request Discord's recommended count. */ shardCount?: number | "auto";
11
11
  /** Gateway reconnect behavior. */ reconnect?: boolean;
12
- /** Delay between shard starts in milliseconds. */ spawnDelay?: number;
12
+ /**
13
+ * Delay between shard starts in milliseconds. Defaults to 5000 to respect
14
+ * Discord's IDENTIFY rate limit (one per 5s per rate-limit key); set 0 to
15
+ * opt out when an external scheduler already paces the handshakes.
16
+ */ spawnDelay?: number;
17
+ /**
18
+ * Shards that may IDENTIFY at once (Discord's `max_concurrency`). Defaults
19
+ * to the value from `/gateway/bot` when it was fetched (auto shard count),
20
+ * else 1.
21
+ */ maxConcurrency?: number;
22
+ /**
23
+ * How long a startup round waits for its shards to send IDENTIFY/RESUME
24
+ * before the next round's `spawnDelay` starts. Defaults to 15000 ms.
25
+ */ handshakeTimeout?: number;
13
26
  /** Interval in milliseconds to automatically check for recommended shard count and re-scale if needed. Must be an integer >= 1000. */ autoScaleInterval?: number;
14
27
  /** Optional handler invoked when a background auto-scale check fails. Receives the thrown error. */ onAutoScaleError?: (error: unknown) => void;
15
28
  }
29
+ /** `/gateway/bot` information used to pace shard startup. */
30
+ export interface GatewayBotInfo {
31
+ /** Recommended shard count. */ shards: number;
32
+ /** IDENTIFY budget, when Discord reported it. */ sessionStartLimit?: {
33
+ total: number;
34
+ remaining: number;
35
+ /** Milliseconds until `remaining` resets. */ resetAfter: number;
36
+ maxConcurrency: number;
37
+ };
38
+ }
39
+ /** Health snapshot for one shard. */
40
+ export interface ShardHealth {
41
+ id: number;
42
+ /** Gateway connection state. */ state: Gateway["state"];
43
+ /** Last heartbeat round-trip in ms, or -1 before the first ACK. */ ping: number;
44
+ }
16
45
  /** Runtime state for a managed shard. */
17
46
  export interface ShardInfo {
18
47
  /** Shard identifier. */ id: number;
@@ -21,12 +50,16 @@ export interface ShardInfo {
21
50
  /** Manages independent Discord Gateway shards with explicit destruction and reinitialization semantics. */
22
51
  export declare class ShardManager {
23
52
  #private;
53
+ /** Discord's minimum interval between IDENTIFY payloads, in milliseconds. */
54
+ static readonly IDENTIFY_INTERVAL = 5000;
24
55
  /** Active Gateway shards indexed by shard identifier. */ readonly shards: Map<number, Gateway>;
25
56
  /** Number of shards managed by this instance after initialization. */ get shardCount(): number;
26
57
  /** Creates a shard manager. @param options Sharding configuration. @throws {TypeError} If token or intents are invalid. @throws {RangeError} If shard count is invalid. */
27
58
  constructor(options: ShardManagerOptions);
28
59
  /** Retrieves Discord's recommended shard count. @returns Recommended shard count. @throws {Error} If discovery fails or returns invalid data. */
29
60
  fetchRecommendedShardCount(): Promise<number>;
61
+ /** Retrieves `/gateway/bot`: recommended shards and the IDENTIFY budget. Remembered for pacing the next connect. @throws {Error} If discovery fails or returns invalid data. */
62
+ fetchGatewayInfo(): Promise<GatewayBotInfo>;
30
63
  /** Connects all shards sequentially. A destroyed manager is reinitialized before connecting. @returns A promise fulfilled after all shards connect. @throws {Error} If a shard fails to connect. */
31
64
  connect(): Promise<void>;
32
65
  /** Checks if the recommended shard count has changed and reconnects if so. */
@@ -38,5 +71,9 @@ export declare class ShardManager {
38
71
  /** Calculates the target shard ID for a Discord guild snowflake. @param guildId Guild snowflake. @returns Shard identifier. */
39
72
  getShardIdForGuild(guildId: string): number;
40
73
  /** Gets a shard by ID. @param id Shard identifier. @returns Gateway instance or undefined. */ get(id: number): Gateway | undefined;
74
+ /** Returns each shard's connection state and heartbeat latency. */
75
+ health(): ShardHealth[];
41
76
  /** Returns information for all managed shards. @returns Shard information snapshots. */ values(): ShardInfo[];
42
77
  }
78
+ /** Discord.js-familiar alias for {@link ShardManager}. */
79
+ export { ShardManager as ShardingManager };