lunibee 0.1.6 → 0.1.8

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 (80) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +223 -223
  3. package/dist/builders/commands.d.ts +39 -0
  4. package/dist/builders/components.d.ts +37 -12
  5. package/dist/builders/embed.d.ts +3 -1
  6. package/dist/builders/index.d.ts +2 -2
  7. package/dist/builders/index.js +192 -29
  8. package/dist/builders/index.js.map +9 -7
  9. package/dist/collection/index.d.ts +30 -0
  10. package/dist/collection/index.js +79 -5
  11. package/dist/collection/index.js.map +4 -4
  12. package/dist/core/events.d.ts +88 -2
  13. package/dist/core/index.d.ts +46 -85
  14. package/dist/core/index.js +2395 -789
  15. package/dist/core/index.js.map +44 -22
  16. package/dist/core/permissions.d.ts +112 -53
  17. package/dist/formatters/index.d.ts +43 -3
  18. package/dist/formatters/index.js +64 -1
  19. package/dist/formatters/index.js.map +3 -3
  20. package/dist/handlers/index.js.map +2 -2
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +3783 -1009
  23. package/dist/index.js.map +63 -38
  24. package/dist/managers/emoji.d.ts +38 -0
  25. package/dist/managers/guild-resources.d.ts +109 -0
  26. package/dist/managers/guild.d.ts +117 -1
  27. package/dist/managers/index.d.ts +37 -1
  28. package/dist/managers/index.js +1125 -81
  29. package/dist/managers/index.js.map +25 -20
  30. package/dist/rest/decoder.d.ts +23 -0
  31. package/dist/rest/errors.d.ts +20 -0
  32. package/dist/rest/index.d.ts +119 -25
  33. package/dist/rest/index.js +676 -204
  34. package/dist/rest/index.js.map +14 -6
  35. package/dist/rest/limiter.d.ts +42 -0
  36. package/dist/rest/redis.d.ts +54 -0
  37. package/dist/rest/route.d.ts +31 -0
  38. package/dist/rest/routes.d.ts +24 -0
  39. package/dist/rest/scheduler.d.ts +42 -0
  40. package/dist/rest/store.d.ts +67 -0
  41. package/dist/rest/transport.d.ts +40 -0
  42. package/dist/rest/webhook.d.ts +2 -2
  43. package/dist/sharding/bus.d.ts +31 -0
  44. package/dist/sharding/cluster.d.ts +71 -0
  45. package/dist/sharding/index.d.ts +18 -2
  46. package/dist/sharding/index.js +1202 -317
  47. package/dist/sharding/index.js.map +18 -7
  48. package/dist/structures/audit-log.d.ts +26 -0
  49. package/dist/structures/base.d.ts +40 -0
  50. package/dist/structures/channels.d.ts +56 -0
  51. package/dist/structures/index.d.ts +30 -1
  52. package/dist/structures/index.js +963 -87
  53. package/dist/structures/index.js.map +17 -10
  54. package/dist/structures/interactions.d.ts +12 -52
  55. package/dist/structures/options.d.ts +55 -0
  56. package/dist/structures/resources.d.ts +194 -6
  57. package/dist/types/gateway-events.d.ts +132 -0
  58. package/dist/types/gateway.d.ts +181 -0
  59. package/dist/types/index.d.ts +78 -263
  60. package/dist/types/index.js +44 -2
  61. package/dist/types/index.js.map +5 -4
  62. package/dist/utils/index.js.map +2 -2
  63. package/dist/voice/index.d.ts +116 -0
  64. package/dist/voice/index.js +261 -10
  65. package/dist/voice/index.js.map +3 -3
  66. package/dist/ws/close-codes.d.ts +23 -0
  67. package/dist/ws/decoder.d.ts +58 -0
  68. package/dist/ws/heartbeat.d.ts +87 -0
  69. package/dist/ws/index.d.ts +24 -32
  70. package/dist/ws/index.js +920 -318
  71. package/dist/ws/index.js.map +15 -5
  72. package/dist/ws/opcodes.d.ts +14 -0
  73. package/dist/ws/protocol.d.ts +110 -0
  74. package/dist/ws/reconnect.d.ts +108 -0
  75. package/dist/ws/send-budget.d.ts +17 -0
  76. package/dist/ws/session.d.ts +100 -0
  77. package/dist/ws/state.d.ts +27 -0
  78. package/dist/ws/transport.d.ts +80 -0
  79. package/package.json +23 -22
  80. package/dist/structures/permissions.d.ts +0 -73
@@ -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
+ }
@@ -0,0 +1,67 @@
1
+ export interface BucketState {
2
+ remaining: number;
3
+ resetAt: number;
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
+ };
18
+ /** Interface for distributed or local rate limit synchronization. */
19
+ export interface RateLimitStore {
20
+ /** Gets the server bucket hash for a normalized route. */
21
+ getBucketHash(route: string): Promise<string | undefined> | string | undefined;
22
+ /** Associates a route with a server bucket hash. */
23
+ setBucketHash(route: string, hash: string): Promise<void> | void;
24
+ /** Gets the current state for a bucket key. */
25
+ getBucket(key: string): Promise<BucketState | undefined> | BucketState | undefined;
26
+ /** Updates the state for a bucket key. */
27
+ updateBucket(key: string, state: BucketState): Promise<void> | void;
28
+ /** Gets the global reset timestamp. */
29
+ getGlobalReset(): Promise<number> | number;
30
+ /** Sets the global reset timestamp. */
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;
41
+ /**
42
+ * Evicts expired bucket entries. Optional: stores with native key
43
+ * expiry (e.g. Redis) can omit it. `maxAge` (ms) additionally drops
44
+ * buckets whose reset is older than that window; defaults to 0 (only
45
+ * already-reset buckets are removed).
46
+ */
47
+ prune?(maxAge?: number): Promise<void> | void;
48
+ }
49
+ /** Local memory implementation of RateLimitStore. */
50
+ export declare class MemoryRateLimitStore implements RateLimitStore {
51
+ #private;
52
+ getBucketHash(route: string): string | undefined;
53
+ setBucketHash(route: string, hash: string): void;
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;
62
+ updateBucket(key: string, state: BucketState): void;
63
+ /** Removes buckets whose reset is in the past (or older than `maxAge` ms). */
64
+ prune(maxAge?: number): void;
65
+ getGlobalReset(): number;
66
+ setGlobalReset(resetAt: number): void;
67
+ }
@@ -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
+ }
@@ -1,4 +1,4 @@
1
- import type { APIEmbed } from "@lunibee/types";
1
+ import type { APIEmbed, APIMessage } from "@lunibee/types";
2
2
  export interface WebhookClientOptions {
3
3
  id?: string;
4
4
  token?: string;
@@ -29,7 +29,7 @@ export declare class WebhookClient {
29
29
  retries?: number;
30
30
  });
31
31
  /** Sends a message to the webhook. */
32
- send(options: string | WebhookMessageOptions): Promise<any>;
32
+ send(options: string | WebhookMessageOptions): Promise<APIMessage>;
33
33
  /** Edits a previously sent webhook message. */
34
34
  editMessage(messageId: string, options: string | {
35
35
  content?: string;
@@ -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
  }
@@ -0,0 +1,71 @@
1
+ import { type ChildProcess } from "node:child_process";
2
+ /** Configuration for managing multiple shard clusters. */
3
+ export interface ClusterManagerOptions {
4
+ /** Discord bot token. */
5
+ token: string;
6
+ /** Path to the worker script to execute for each cluster. */
7
+ script: string;
8
+ /** Number of shards. Use "auto" to request Discord's recommended count. */
9
+ shardCount?: number | "auto";
10
+ /** Number of clusters to spawn. Defaults to the number of logical CPUs. */
11
+ clusterCount?: number;
12
+ /** Interval in milliseconds to automatically check for recommended shard count and re-scale if needed. */
13
+ autoScaleInterval?: number;
14
+ /** Optional handler invoked when a background auto-scale check fails. Receives the thrown error. */
15
+ onAutoScaleError?: (error: Error) => void;
16
+ /** Grace period in milliseconds to await a cluster's clean exit after SIGTERM before force-killing. Defaults to 5000. */
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;
28
+ }
29
+ /** Information about a running cluster. */
30
+ export interface ClusterInfo {
31
+ /** Cluster identifier. */
32
+ id: number;
33
+ /** The child process running the cluster. */
34
+ process: ChildProcess;
35
+ /** The shard IDs managed by this cluster. */
36
+ shards: number[];
37
+ }
38
+ /**
39
+ * Manages multiple clusters of shards, distributing shards across child processes.
40
+ *
41
+ * **Runtime requirement:** clustering relies on Node.js `child_process.fork()`
42
+ * and IPC. `fork()` is not fully supported under Bun, so run the cluster
43
+ * manager under the Node.js runtime; `spawn()` will fail on a runtime whose
44
+ * `fork()` is unavailable.
45
+ */
46
+ export declare class ClusterManager {
47
+ #private;
48
+ /** Active clusters indexed by cluster ID. */
49
+ readonly clusters: Map<number, ClusterInfo>;
50
+ /** Total number of shards across all clusters. */
51
+ shardCount: number;
52
+ /** Creates a cluster manager. @param options Clustering configuration. @throws {TypeError} If token or script is missing. */
53
+ constructor(options: ClusterManagerOptions);
54
+ /** Retrieves Discord's recommended shard count. @returns Recommended shard count. @throws {Error} If discovery fails or returns invalid data. */
55
+ fetchRecommendedShardCount(): Promise<number>;
56
+ /** Spawns all clusters. @returns A promise fulfilled after clusters have launched. */
57
+ spawn(): Promise<void>;
58
+ /** Checks if the recommended shard count has changed and respawns if so. */
59
+ checkAutoScale(): Promise<void>;
60
+ /** Gracefully shuts the existing clusters down and respawns them with the new shard count. @param newShardCount The new total shard count. */
61
+ respawn(newShardCount: number): Promise<void>;
62
+ /**
63
+ * Gracefully stops all active clusters: sends SIGTERM, awaits each child's
64
+ * clean exit, and force-kills (SIGKILL) only children that outlast the
65
+ * shutdown timeout. Clears the auto-scale timer and cluster map.
66
+ * @param timeoutMs Grace period per child before force-kill. Defaults to the configured `shutdownTimeout` (5000 ms).
67
+ */
68
+ shutdownAll(timeoutMs?: number): Promise<void>;
69
+ /** Immediately force-kills all active cluster processes and clears the cluster map. Prefer {@link shutdownAll} for a graceful stop. */
70
+ killAll(): void;
71
+ }
@@ -1,13 +1,21 @@
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
+ export { ClusterManager } from "./cluster.js";
5
+ export type { ClusterManagerOptions, ClusterInfo } from "./cluster.js";
4
6
  /** Configuration for a sharded Gateway client. */
5
7
  export interface ShardManagerOptions {
6
8
  /** Bot token. */ token: string;
7
9
  /** Gateway intents. */ intents: number;
8
10
  /** Number of shards. Use `"auto"` to request Discord's recommended count. */ shardCount?: number | "auto";
9
11
  /** Gateway reconnect behavior. */ reconnect?: boolean;
10
- /** 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
+ /** Interval in milliseconds to automatically check for recommended shard count and re-scale if needed. Must be an integer >= 1000. */ autoScaleInterval?: number;
18
+ /** Optional handler invoked when a background auto-scale check fails. Receives the thrown error. */ onAutoScaleError?: (error: unknown) => void;
11
19
  }
12
20
  /** Runtime state for a managed shard. */
13
21
  export interface ShardInfo {
@@ -17,6 +25,8 @@ export interface ShardInfo {
17
25
  /** Manages independent Discord Gateway shards with explicit destruction and reinitialization semantics. */
18
26
  export declare class ShardManager {
19
27
  #private;
28
+ /** Discord's minimum interval between IDENTIFY payloads, in milliseconds. */
29
+ static readonly IDENTIFY_INTERVAL = 5000;
20
30
  /** Active Gateway shards indexed by shard identifier. */ readonly shards: Map<number, Gateway>;
21
31
  /** Number of shards managed by this instance after initialization. */ get shardCount(): number;
22
32
  /** Creates a shard manager. @param options Sharding configuration. @throws {TypeError} If token or intents are invalid. @throws {RangeError} If shard count is invalid. */
@@ -25,6 +35,10 @@ export declare class ShardManager {
25
35
  fetchRecommendedShardCount(): Promise<number>;
26
36
  /** 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. */
27
37
  connect(): Promise<void>;
38
+ /** Checks if the recommended shard count has changed and reconnects if so. */
39
+ checkAutoScale(): Promise<void>;
40
+ /** Forces a reshard to a new shard count, replacing all active shards. @param count New shard count. */
41
+ reshard(count: number): Promise<void>;
28
42
  /** Permanently closes the current shard set and releases Gateway resources. The next connect recreates them. @returns Nothing. */
29
43
  destroy(): void;
30
44
  /** Calculates the target shard ID for a Discord guild snowflake. @param guildId Guild snowflake. @returns Shard identifier. */
@@ -32,3 +46,5 @@ export declare class ShardManager {
32
46
  /** Gets a shard by ID. @param id Shard identifier. @returns Gateway instance or undefined. */ get(id: number): Gateway | undefined;
33
47
  /** Returns information for all managed shards. @returns Shard information snapshots. */ values(): ShardInfo[];
34
48
  }
49
+ /** Discord.js-familiar alias for {@link ShardManager}. */
50
+ export { ShardManager as ShardingManager };