lunibee 0.1.8 → 0.2.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 (55) hide show
  1. package/README.md +2 -0
  2. package/dist/builders/components.d.ts +30 -2
  3. package/dist/builders/embed.d.ts +7 -7
  4. package/dist/builders/index.d.ts +1 -1
  5. package/dist/builders/index.js +110 -18
  6. package/dist/builders/index.js.map +6 -6
  7. package/dist/collection/index.d.ts +68 -1
  8. package/dist/collection/index.js +334 -1
  9. package/dist/collection/index.js.map +3 -3
  10. package/dist/core/collector.d.ts +13 -0
  11. package/dist/core/events.d.ts +29 -3
  12. package/dist/core/index.d.ts +25 -9
  13. package/dist/core/index.js +1745 -134
  14. package/dist/core/index.js.map +30 -26
  15. package/dist/core/permissions.d.ts +48 -0
  16. package/dist/index.js +2102 -362
  17. package/dist/index.js.map +36 -34
  18. package/dist/managers/advanced.d.ts +116 -0
  19. package/dist/managers/base.d.ts +13 -2
  20. package/dist/managers/emoji.d.ts +1 -1
  21. package/dist/managers/guild-resources.d.ts +22 -2
  22. package/dist/managers/guild.d.ts +31 -3
  23. package/dist/managers/index.d.ts +38 -6
  24. package/dist/managers/index.js +996 -160
  25. package/dist/managers/index.js.map +20 -18
  26. package/dist/managers/member.d.ts +13 -12
  27. package/dist/managers/message.d.ts +21 -4
  28. package/dist/rest/errors.d.ts +33 -0
  29. package/dist/rest/index.d.ts +1 -1
  30. package/dist/rest/index.js +52 -6
  31. package/dist/rest/index.js.map +5 -5
  32. package/dist/rest/routes.d.ts +20 -0
  33. package/dist/sharding/index.d.ts +29 -0
  34. package/dist/sharding/index.js +99 -15
  35. package/dist/sharding/index.js.map +8 -8
  36. package/dist/structures/base.d.ts +38 -0
  37. package/dist/structures/index.d.ts +29 -0
  38. package/dist/structures/index.js +208 -46
  39. package/dist/structures/index.js.map +13 -12
  40. package/dist/structures/interactions.d.ts +51 -1
  41. package/dist/structures/options.d.ts +2 -0
  42. package/dist/structures/resources.d.ts +29 -3
  43. package/dist/types/gateway.d.ts +16 -0
  44. package/dist/types/index.d.ts +67 -1
  45. package/dist/types/index.js.map +3 -3
  46. package/dist/utils/args.d.ts +79 -0
  47. package/dist/utils/index.d.ts +1 -0
  48. package/dist/utils/index.js +190 -2
  49. package/dist/utils/index.js.map +5 -4
  50. package/dist/voice/index.d.ts +3 -0
  51. package/dist/voice/index.js +15 -1
  52. package/dist/voice/index.js.map +3 -3
  53. package/dist/ws/index.js +13 -5
  54. package/dist/ws/index.js.map +6 -6
  55. package/package.json +14 -14
@@ -1,4 +1,185 @@
1
1
  // @bun
2
+ // packages/utils/src/args.ts
3
+ var SNOWFLAKE = /^\d{16,22}$/;
4
+ var TRUE_WORDS = new Set([
5
+ "true",
6
+ "yes",
7
+ "y",
8
+ "on",
9
+ "1",
10
+ "enable",
11
+ "enabled"
12
+ ]);
13
+ var FALSE_WORDS = new Set([
14
+ "false",
15
+ "no",
16
+ "n",
17
+ "off",
18
+ "0",
19
+ "disable",
20
+ "disabled"
21
+ ]);
22
+ function tokenizeArgs(input) {
23
+ const args = [];
24
+ let current = "";
25
+ let quote = null;
26
+ let started = false;
27
+ for (let index = 0;index < input.length; index++) {
28
+ const character = input[index];
29
+ if (character === "\\" && index + 1 < input.length) {
30
+ current += input[++index];
31
+ started = true;
32
+ } else if (quote) {
33
+ if (character === quote)
34
+ quote = null;
35
+ else
36
+ current += character;
37
+ } else if (character === '"' || character === "'") {
38
+ if (started)
39
+ current += character;
40
+ else {
41
+ quote = character;
42
+ started = true;
43
+ }
44
+ } else if (/\s/.test(character)) {
45
+ if (started)
46
+ args.push(current);
47
+ current = "";
48
+ started = false;
49
+ } else {
50
+ current += character;
51
+ started = true;
52
+ }
53
+ }
54
+ if (started)
55
+ args.push(current);
56
+ return args;
57
+ }
58
+ function parseUserMention(value) {
59
+ const id = /^<@!?(\d{16,22})>$/.exec(value)?.[1] ?? value;
60
+ return SNOWFLAKE.test(id) ? id : null;
61
+ }
62
+ function parseRoleMention(value) {
63
+ const id = /^<@&(\d{16,22})>$/.exec(value)?.[1] ?? value;
64
+ return SNOWFLAKE.test(id) ? id : null;
65
+ }
66
+ function parseChannelMention(value) {
67
+ const id = /^<#(\d{16,22})>$/.exec(value)?.[1] ?? value;
68
+ return SNOWFLAKE.test(id) ? id : null;
69
+ }
70
+ function parseMentionable(value) {
71
+ const role = /^<@&(\d{16,22})>$/.exec(value)?.[1];
72
+ if (role)
73
+ return { id: role, type: "role" };
74
+ const user = parseUserMention(value);
75
+ return user ? { id: user, type: "user" } : null;
76
+ }
77
+ function readOne(spec, raw) {
78
+ switch (spec.type) {
79
+ case "string":
80
+ case "rest":
81
+ if (spec.maxLength !== undefined && raw.length > spec.maxLength)
82
+ return {
83
+ error: `must be at most ${spec.maxLength} characters`
84
+ };
85
+ if (spec.choices && !spec.choices.some((choice) => String(choice).toLowerCase() === raw.toLowerCase()))
86
+ return { error: `must be one of: ${spec.choices.join(", ")}` };
87
+ return { value: raw };
88
+ case "integer":
89
+ case "number": {
90
+ const value = Number(raw);
91
+ if (!Number.isFinite(value) || raw.trim() === "")
92
+ return { error: "must be a number" };
93
+ if (spec.type === "integer" && !Number.isSafeInteger(value))
94
+ return { error: "must be a whole number" };
95
+ if (spec.min !== undefined && value < spec.min)
96
+ return { error: `must be at least ${spec.min}` };
97
+ if (spec.max !== undefined && value > spec.max)
98
+ return { error: `must be at most ${spec.max}` };
99
+ if (spec.choices && !spec.choices.includes(value))
100
+ return { error: `must be one of: ${spec.choices.join(", ")}` };
101
+ return { value };
102
+ }
103
+ case "boolean": {
104
+ const word = raw.toLowerCase();
105
+ if (TRUE_WORDS.has(word))
106
+ return { value: true };
107
+ if (FALSE_WORDS.has(word))
108
+ return { value: false };
109
+ return { error: "must be yes or no" };
110
+ }
111
+ case "user": {
112
+ const id = parseUserMention(raw);
113
+ return id ? { value: id } : { error: "must be a user mention or ID" };
114
+ }
115
+ case "role": {
116
+ const id = parseRoleMention(raw);
117
+ return id ? { value: id } : { error: "must be a role mention or ID" };
118
+ }
119
+ case "channel": {
120
+ const id = parseChannelMention(raw);
121
+ return id ? { value: id } : { error: "must be a channel mention or ID" };
122
+ }
123
+ case "mentionable": {
124
+ const value = parseMentionable(raw);
125
+ return value ? { value } : { error: "must be a user or role mention" };
126
+ }
127
+ }
128
+ }
129
+ function parsePrefixArgs(input, specs) {
130
+ const args = typeof input === "string" ? tokenizeArgs(input) : [...input];
131
+ const values = {};
132
+ let position = 0;
133
+ for (const [index, spec] of specs.entries()) {
134
+ if (spec.type === "rest" && index !== specs.length - 1)
135
+ throw new TypeError(`The rest argument "${spec.name}" must be the last one.`);
136
+ const raw = spec.type === "rest" ? args.slice(position).join(" ") : args[position];
137
+ if (raw === undefined || raw === "") {
138
+ if (spec.required)
139
+ return { ok: false, arg: spec.name, error: "is required" };
140
+ values[spec.name] = undefined;
141
+ continue;
142
+ }
143
+ const read = readOne(spec, raw);
144
+ if ("error" in read)
145
+ return { ok: false, arg: spec.name, error: read.error };
146
+ values[spec.name] = read.value;
147
+ position += spec.type === "rest" ? args.length : 1;
148
+ }
149
+ return { ok: true, values };
150
+ }
151
+ var OPTION_TYPES = {
152
+ 3: "string",
153
+ 4: "integer",
154
+ 5: "boolean",
155
+ 6: "user",
156
+ 7: "channel",
157
+ 8: "role",
158
+ 9: "mentionable",
159
+ 10: "number"
160
+ };
161
+ function argsFromCommandOptions(options, restLast = false) {
162
+ const specs = [];
163
+ for (const option of options) {
164
+ const type = OPTION_TYPES[option.type];
165
+ if (!type)
166
+ continue;
167
+ specs.push({
168
+ name: option.name,
169
+ type,
170
+ required: option.required,
171
+ min: option.min_value,
172
+ max: option.max_value,
173
+ maxLength: option.max_length,
174
+ choices: option.choices?.map((choice) => choice.value)
175
+ });
176
+ }
177
+ const last = specs[specs.length - 1];
178
+ if (restLast && last?.type === "string")
179
+ last.type = "rest";
180
+ return specs;
181
+ }
182
+
2
183
  // packages/utils/src/index.ts
3
184
  function sleep(ms) {
4
185
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -10,9 +191,16 @@ function isSnowflake(value) {
10
191
  return /^\d{16,22}$/.test(value);
11
192
  }
12
193
  export {
194
+ argsFromCommandOptions,
13
195
  isSnowflake,
196
+ parseChannelMention,
197
+ parseMentionable,
198
+ parsePrefixArgs,
199
+ parseRoleMention,
200
+ parseUserMention,
14
201
  randomInt,
15
- sleep
202
+ sleep,
203
+ tokenizeArgs
16
204
  };
17
205
 
18
- //# debugId=4F20A8D367F5985F64756E2164756E21
206
+ //# debugId=3EBA957FB9AD89B264756E2164756E21
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../packages/utils/src/index.ts"],
3
+ "sources": ["../../packages/utils/src/args.ts", "../../packages/utils/src/index.ts"],
4
4
  "sourcesContent": [
5
- "/** Returns a promise that resolves after the specified duration. */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Returns an integer between the supplied inclusive bounds. */\nexport function randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\n/** Tests whether a value is a Discord snowflake-like ID. */\nexport function isSnowflake(value: string): boolean {\n return /^\\d{16,22}$/.test(value);\n}\n"
5
+ "/**\n * Prefix-command arguments: split a message into arguments and read them as\n * typed values, the way slash-command options are read.\n */\n\n/** What an argument is read as. `rest` takes everything left, as one string. */\nexport type PrefixArgType =\n | \"string\"\n | \"integer\"\n | \"number\"\n | \"boolean\"\n | \"user\"\n | \"role\"\n | \"channel\"\n | \"mentionable\"\n | \"rest\";\n\n/** One expected argument. */\nexport interface PrefixArgSpec {\n name: string;\n type: PrefixArgType;\n required?: boolean;\n /** For `string` and `rest`: the longest accepted value. */\n maxLength?: number;\n /** For `integer` and `number`: the accepted range. */\n min?: number;\n max?: number;\n /** Accepted values (compared case-insensitively for strings). */\n choices?: readonly (string | number)[];\n}\n\n/** A mentionable argument: which kind of mention it was. An unmarked ID reads as a user. */\nexport interface MentionableArg {\n id: string;\n type: \"user\" | \"role\";\n}\n\nexport type PrefixArgValue = string | number | boolean | MentionableArg;\n\nexport type PrefixArgsResult =\n | { ok: true; values: Record<string, PrefixArgValue | undefined> }\n | { ok: false; arg: string; error: string };\n\nconst SNOWFLAKE = /^\\d{16,22}$/;\nconst TRUE_WORDS = new Set([\n \"true\",\n \"yes\",\n \"y\",\n \"on\",\n \"1\",\n \"enable\",\n \"enabled\",\n]);\nconst FALSE_WORDS = new Set([\n \"false\",\n \"no\",\n \"n\",\n \"off\",\n \"0\",\n \"disable\",\n \"disabled\",\n]);\n\n/**\n * Splits text into arguments on whitespace. \"Double quotes\" or 'single quotes'\n * keep spaces inside one argument; a backslash escapes the next character.\n * @example tokenizeArgs(`ban @user \"being rude\"`) // [\"ban\", \"@user\", \"being rude\"]\n */\nexport function tokenizeArgs(input: string): string[] {\n const args: string[] = [];\n let current = \"\";\n let quote: string | null = null;\n let started = false;\n for (let index = 0; index < input.length; index++) {\n const character = input[index]!;\n if (character === \"\\\\\" && index + 1 < input.length) {\n current += input[++index];\n started = true;\n } else if (quote) {\n if (character === quote) quote = null;\n else current += character;\n } else if (character === '\"' || character === \"'\") {\n // A quote opens a quoted part only at the start of an argument.\n if (started) current += character;\n else {\n quote = character;\n started = true;\n }\n } else if (/\\s/.test(character)) {\n if (started) args.push(current);\n current = \"\";\n started = false;\n } else {\n current += character;\n started = true;\n }\n }\n if (started) args.push(current);\n return args;\n}\n\n/** The user ID in `<@id>`, `<@!id>` or a bare ID; null otherwise. */\nexport function parseUserMention(value: string): string | null {\n const id = /^<@!?(\\d{16,22})>$/.exec(value)?.[1] ?? value;\n return SNOWFLAKE.test(id) ? id : null;\n}\n\n/** The role ID in `<@&id>` or a bare ID; null otherwise. */\nexport function parseRoleMention(value: string): string | null {\n const id = /^<@&(\\d{16,22})>$/.exec(value)?.[1] ?? value;\n return SNOWFLAKE.test(id) ? id : null;\n}\n\n/** The channel ID in `<#id>` or a bare ID; null otherwise. */\nexport function parseChannelMention(value: string): string | null {\n const id = /^<#(\\d{16,22})>$/.exec(value)?.[1] ?? value;\n return SNOWFLAKE.test(id) ? id : null;\n}\n\n/** A user or role mention; a bare ID reads as a user. Null otherwise. */\nexport function parseMentionable(value: string): MentionableArg | null {\n const role = /^<@&(\\d{16,22})>$/.exec(value)?.[1];\n if (role) return { id: role, type: \"role\" };\n const user = parseUserMention(value);\n return user ? { id: user, type: \"user\" } : null;\n}\n\nfunction readOne(\n spec: PrefixArgSpec,\n raw: string,\n): { value: PrefixArgValue } | { error: string } {\n switch (spec.type) {\n case \"string\":\n case \"rest\":\n if (spec.maxLength !== undefined && raw.length > spec.maxLength)\n return {\n error: `must be at most ${spec.maxLength} characters`,\n };\n if (\n spec.choices &&\n !spec.choices.some(\n (choice) =>\n String(choice).toLowerCase() === raw.toLowerCase(),\n )\n )\n return { error: `must be one of: ${spec.choices.join(\", \")}` };\n return { value: raw };\n case \"integer\":\n case \"number\": {\n const value = Number(raw);\n if (!Number.isFinite(value) || raw.trim() === \"\")\n return { error: \"must be a number\" };\n if (spec.type === \"integer\" && !Number.isSafeInteger(value))\n return { error: \"must be a whole number\" };\n if (spec.min !== undefined && value < spec.min)\n return { error: `must be at least ${spec.min}` };\n if (spec.max !== undefined && value > spec.max)\n return { error: `must be at most ${spec.max}` };\n if (spec.choices && !spec.choices.includes(value))\n return { error: `must be one of: ${spec.choices.join(\", \")}` };\n return { value };\n }\n case \"boolean\": {\n const word = raw.toLowerCase();\n if (TRUE_WORDS.has(word)) return { value: true };\n if (FALSE_WORDS.has(word)) return { value: false };\n return { error: \"must be yes or no\" };\n }\n case \"user\": {\n const id = parseUserMention(raw);\n return id\n ? { value: id }\n : { error: \"must be a user mention or ID\" };\n }\n case \"role\": {\n const id = parseRoleMention(raw);\n return id\n ? { value: id }\n : { error: \"must be a role mention or ID\" };\n }\n case \"channel\": {\n const id = parseChannelMention(raw);\n return id\n ? { value: id }\n : { error: \"must be a channel mention or ID\" };\n }\n case \"mentionable\": {\n const value = parseMentionable(raw);\n return value\n ? { value }\n : { error: \"must be a user or role mention\" };\n }\n }\n}\n\n/**\n * Reads prefix-command arguments by position, as typed values. IDs come back\n * as strings (user, role, channel); a mentionable says which it was. The first\n * problem is reported, with the argument's name, instead of throwing.\n * @param input The text after the command name, or already-split arguments.\n * @param specs The expected arguments in order; a `rest` argument must be last.\n * @example\n * const result = parsePrefixArgs(`@user 10 spamming links`, [\n * { name: \"user\", type: \"user\", required: true },\n * { name: \"minutes\", type: \"integer\", min: 1, max: 40320 },\n * { name: \"reason\", type: \"rest\" },\n * ]);\n * if (!result.ok) return message.reply(`\\`${result.arg}\\` ${result.error}.`);\n */\nexport function parsePrefixArgs(\n input: string | readonly string[],\n specs: readonly PrefixArgSpec[],\n): PrefixArgsResult {\n const args = typeof input === \"string\" ? tokenizeArgs(input) : [...input];\n const values: Record<string, PrefixArgValue | undefined> = {};\n let position = 0;\n for (const [index, spec] of specs.entries()) {\n if (spec.type === \"rest\" && index !== specs.length - 1)\n throw new TypeError(\n `The rest argument \"${spec.name}\" must be the last one.`,\n );\n const raw =\n spec.type === \"rest\"\n ? args.slice(position).join(\" \")\n : args[position];\n if (raw === undefined || raw === \"\") {\n if (spec.required)\n return { ok: false, arg: spec.name, error: \"is required\" };\n values[spec.name] = undefined;\n continue;\n }\n const read = readOne(spec, raw);\n if (\"error\" in read)\n return { ok: false, arg: spec.name, error: read.error };\n values[spec.name] = read.value;\n position += spec.type === \"rest\" ? args.length : 1;\n }\n return { ok: true, values };\n}\n\n/** Discord application-command option types, as numbers (this package has no dependencies). */\nconst OPTION_TYPES: Readonly<Record<number, PrefixArgType>> = {\n 3: \"string\",\n 4: \"integer\",\n 5: \"boolean\",\n 6: \"user\",\n 7: \"channel\",\n 8: \"role\",\n 9: \"mentionable\",\n 10: \"number\",\n};\n\n/**\n * Argument specs from a slash command's options, so one definition serves both\n * `/command` and the prefix command. Subcommands and attachments are left out.\n * @param options The command's (or subcommand's) `options` as sent to Discord.\n * @param restLast Read the last string option as the rest of the message (handy for reasons).\n */\nexport function argsFromCommandOptions(\n options: readonly {\n name: string;\n type: number;\n required?: boolean;\n min_value?: number;\n max_value?: number;\n max_length?: number;\n choices?: readonly { value: string | number }[];\n }[],\n restLast = false,\n): PrefixArgSpec[] {\n const specs: PrefixArgSpec[] = [];\n for (const option of options) {\n const type = OPTION_TYPES[option.type];\n if (!type) continue;\n specs.push({\n name: option.name,\n type,\n required: option.required,\n min: option.min_value,\n max: option.max_value,\n maxLength: option.max_length,\n choices: option.choices?.map((choice) => choice.value),\n });\n }\n const last = specs[specs.length - 1];\n if (restLast && last?.type === \"string\") last.type = \"rest\";\n return specs;\n}\n",
6
+ "/** Returns a promise that resolves after the specified duration. */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Returns an integer between the supplied inclusive bounds. */\nexport function randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\n/** Tests whether a value is a Discord snowflake-like ID. */\nexport function isSnowflake(value: string): boolean {\n return /^\\d{16,22}$/.test(value);\n}\n\nexport {\n tokenizeArgs,\n parsePrefixArgs,\n argsFromCommandOptions,\n parseUserMention,\n parseRoleMention,\n parseChannelMention,\n parseMentionable,\n type PrefixArgType,\n type PrefixArgSpec,\n type PrefixArgValue,\n type PrefixArgsResult,\n type MentionableArg,\n} from \"./args.js\";\n"
6
7
  ],
7
- "mappings": ";;AACO,SAAS,KAAK,CAAC,IAA2B;AAAA,EAC7C,OAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA;AAIpD,SAAS,SAAS,CAAC,KAAa,KAAqB;AAAA,EACxD,OAAO,KAAK,MAAM,KAAK,OAAO,KAAK,MAAM,MAAM,EAAE,IAAI;AAAA;AAIlD,SAAS,WAAW,CAAC,OAAwB;AAAA,EAChD,OAAO,cAAc,KAAK,KAAK;AAAA;",
8
- "debugId": "4F20A8D367F5985F64756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "3EBA957FB9AD89B264756E2164756E21",
9
10
  "names": []
10
11
  }
@@ -178,6 +178,9 @@ export declare class VoiceReceiver {
178
178
  mapSsrc(ssrc: number, userId: string): void;
179
179
  /** Subscribes to incoming audio from a specific user. @param userId User identifier. @returns A binary readable stream of the audio. */
180
180
  subscribe(userId: string): AudioStream;
181
+ /** Ends every subscriber stream and forgets SSRC mappings. Called when the
182
+ * connection disconnects or is destroyed, so readers do not wait forever. */
183
+ close(): void;
181
184
  /** Processes an incoming UDP packet, extracting SSRC and routing the audio
182
185
  * payload (RTP header stripped) to every subscriber for the mapped user. */
183
186
  onPacket(packet: Uint8Array): void;
@@ -84,12 +84,14 @@ class VoiceConnection {
84
84
  if (this.state === "destroyed" /* Destroyed */)
85
85
  return;
86
86
  this.#closeTransports();
87
+ this.receiver.close();
87
88
  this.#transition("disconnected" /* Disconnected */);
88
89
  }
89
90
  destroy() {
90
91
  if (this.state === "destroyed" /* Destroyed */)
91
92
  return;
92
93
  this.#closeTransports();
94
+ this.receiver.close();
93
95
  this.channelId = undefined;
94
96
  this.#transition("destroyed" /* Destroyed */);
95
97
  this.#listeners.clear();
@@ -297,6 +299,8 @@ class VoiceReceiver {
297
299
  this.#ssrcMap.set(ssrc, userId);
298
300
  }
299
301
  subscribe(userId) {
302
+ if (this.connection.state === "destroyed" /* Destroyed */)
303
+ throw new VoiceError("Voice connection has been destroyed.");
300
304
  let controllerRef;
301
305
  const stream = new ReadableStream({
302
306
  start: (controller) => {
@@ -317,6 +321,16 @@ class VoiceReceiver {
317
321
  });
318
322
  return new AudioStream(stream, { title: `User ${userId} Audio` });
319
323
  }
324
+ close() {
325
+ for (const controllers of this.#streams.values())
326
+ for (const controller of controllers) {
327
+ try {
328
+ controller.close();
329
+ } catch {}
330
+ }
331
+ this.#streams.clear();
332
+ this.#ssrcMap.clear();
333
+ }
320
334
  onPacket(packet) {
321
335
  if (packet.length < 12)
322
336
  return;
@@ -409,4 +423,4 @@ export {
409
423
  joinVoiceChannel
410
424
  };
411
425
 
412
- //# debugId=C61FD04CD1E9BF4D64756E2164756E21
426
+ //# debugId=9E96BE25F2F4C69B64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../packages/voice/src/index.ts"],
4
4
  "sourcesContent": [
5
- "/** Voice session lifecycle states. */\nexport enum VoiceConnectionState {\n Disconnected = \"disconnected\",\n Connecting = \"connecting\",\n Connected = \"connected\",\n Destroyed = \"destroyed\",\n}\n\n/** Voice protocol transport errors. */\nexport class VoiceError extends Error {\n /** Creates a voice error. @param message Error message. @param options Optional native error options. */\n public constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"VoiceError\";\n }\n}\n\n/** Options used to establish a voice session. */\nexport interface VoiceConnectionOptions {\n /** Guild containing the voice channel. */\n guildId: string;\n /** Voice channel to join. */\n channelId?: string;\n /** Whether the self user should be muted. */\n selfMute?: boolean;\n /** Whether the self user should be deafened. */\n selfDeaf?: boolean;\n}\n\n/** UDP transport supplied by the runtime-specific voice implementation. */\nexport interface VoiceUdpTransport {\n /** Sends an RTP/RTCP packet. @param packet Packet bytes. @param host Destination host. @param port Destination port. @returns Optional completion promise. */\n send(packet: Uint8Array, host: string, port: number): Promise<void> | void;\n /** Closes the UDP socket. @returns Nothing. */\n close(): void;\n /** Optional listener for incoming UDP packets. */\n onMessage?(listener: (packet: Uint8Array) => void): void;\n}\n\n/** Voice Gateway transport abstraction. */\nexport interface VoiceGatewayTransport {\n /** Opens the voice gateway. @param endpoint Voice gateway endpoint. @param sessionId Voice session identifier. @returns Promise fulfilled when connected. */\n connect(endpoint: string, sessionId: string): Promise<void>;\n /** Sends a voice gateway payload. @param payload Gateway payload. @returns Nothing. */\n send(payload: unknown): void;\n /** Closes the voice gateway. @param code Optional close code. @param reason Optional close reason. @returns Nothing. */\n close(code?: number, reason?: string): void;\n}\n\n/** Speaking flags used by the voice gateway. */\nexport const SpeakingFlags = {\n Microphone: 1,\n Soundshare: 2,\n Priority: 4,\n} as const;\n\n/** Voice session event payloads. */\nexport interface VoiceEvents {\n /** State transition event. */\n stateChange: [VoiceConnectionState, VoiceConnectionState];\n /** Speaking flag event. */\n speaking: [number, number];\n /** Voice transport error event. */\n error: [VoiceError];\n}\n\ntype VoiceListener = (...args: any[]) => unknown;\n\n/** Tracks a Discord voice session and exposes transport-independent lifecycle controls. */\nexport class VoiceConnection {\n /** Current voice connection state. */\n public state: VoiceConnectionState = VoiceConnectionState.Disconnected;\n /** Guild associated with this voice session. */\n public readonly guildId: string;\n /** Voice channel associated with this session. */\n public channelId?: string;\n /** Whether the client is self-muted. */\n public selfMute: boolean;\n /** Whether the client is self-deafened. */\n public selfDeaf: boolean;\n /** Active voice gateway transport, when attached. */\n public gateway?: VoiceGatewayTransport;\n /** Active UDP transport, when attached. */\n public udp?: VoiceUdpTransport;\n /** Registered lifecycle listeners. */\n readonly #listeners = new Map<keyof VoiceEvents, Set<VoiceListener>>();\n /** Receives incoming audio. */\n public readonly receiver: VoiceReceiver;\n\n /** Creates a voice connection state manager. @param options Guild ID or connection options. @throws {TypeError} If the guild ID is empty. */\n public constructor(options: string | VoiceConnectionOptions) {\n const resolved =\n typeof options === \"string\" ? { guildId: options } : options;\n if (!resolved.guildId?.trim()) {\n throw new TypeError(\n \"A guild ID is required for a voice connection.\",\n );\n }\n this.guildId = resolved.guildId;\n this.channelId = resolved.channelId;\n this.selfMute = resolved.selfMute ?? false;\n this.selfDeaf = resolved.selfDeaf ?? false;\n this.receiver = new VoiceReceiver(this);\n }\n\n /** Registers a voice lifecycle listener. @param event Event name. @param listener Listener callback. @returns This connection. @throws {TypeError} If listener is not callable. */\n public on<K extends keyof VoiceEvents>(\n event: K,\n listener: (...args: VoiceEvents[K]) => unknown,\n ): this {\n if (typeof listener !== \"function\")\n throw new TypeError(\"Voice listener must be a function.\");\n let listeners = this.#listeners.get(event);\n if (!listeners) this.#listeners.set(event, (listeners = new Set()));\n listeners.add(listener);\n return this;\n }\n\n /** Removes a voice lifecycle listener. @param event Event name. @param listener Listener callback. @returns This connection. */\n public off<K extends keyof VoiceEvents>(\n event: K,\n listener: (...args: VoiceEvents[K]) => unknown,\n ): this {\n this.#listeners.get(event)?.delete(listener);\n return this;\n }\n\n /** Attaches protocol transports, replacing and cleaning up the old pair when present. @param gateway Voice gateway transport. @param udp UDP transport. @returns This connection. @throws {VoiceError} If the connection is destroyed. @throws {TypeError} If a transport is missing. */\n public attachTransports(\n gateway: VoiceGatewayTransport,\n udp: VoiceUdpTransport,\n ): this {\n this.assertUsable();\n if (!gateway || !udp)\n throw new TypeError(\n \"Both voice gateway and UDP transports are required.\",\n );\n if (this.gateway || this.udp) this.#closeTransports();\n this.gateway = gateway;\n this.udp = udp;\n // Capture the transport this listener belongs to so that packets from a\n // later-replaced transport (whose listener cannot be unregistered — the\n // VoiceUdpTransport interface exposes no removal API) are ignored.\n const attachedUdp = udp;\n if (attachedUdp.onMessage) {\n attachedUdp.onMessage((packet) => {\n if (this.udp === attachedUdp) this.receiver.onPacket(packet);\n });\n }\n return this;\n }\n\n /** Begins the connection lifecycle. @returns Nothing. @throws {VoiceError} If the connection is destroyed. */\n public connect(): void {\n this.assertUsable();\n if (this.state === VoiceConnectionState.Connected) return;\n this.#transition(VoiceConnectionState.Connecting);\n if (this.state !== VoiceConnectionState.Connecting) return;\n this.#transition(VoiceConnectionState.Connected);\n }\n\n /** Disconnects the current voice session. @returns Nothing. */\n public disconnect(): void {\n if (this.state === VoiceConnectionState.Destroyed) return;\n this.#closeTransports();\n this.#transition(VoiceConnectionState.Disconnected);\n }\n\n /** Permanently destroys this voice connection. @returns Nothing. */\n public destroy(): void {\n if (this.state === VoiceConnectionState.Destroyed) return;\n this.#closeTransports();\n this.channelId = undefined;\n this.#transition(VoiceConnectionState.Destroyed);\n this.#listeners.clear();\n }\n\n /** Updates the voice channel and connection state. @param channelId Voice channel identifier. @returns Nothing. @throws {VoiceError} If the connection is destroyed. @throws {TypeError} If channelId is empty. */\n public setChannel(channelId: string): void {\n this.assertUsable();\n if (!channelId?.trim())\n throw new TypeError(\"A voice channel ID is required.\");\n this.channelId = channelId;\n }\n\n /** Updates self mute/deaf state. @param options Suppression flags. @returns Nothing. @throws {VoiceError} If the connection is destroyed. */\n public setSuppression(options: {\n selfMute?: boolean;\n selfDeaf?: boolean;\n }): void {\n this.assertUsable();\n if (options.selfMute !== undefined) this.selfMute = options.selfMute;\n if (options.selfDeaf !== undefined) this.selfDeaf = options.selfDeaf;\n }\n\n /** Publishes a speaking state through the attached voice gateway. @param flags Speaking flags. @returns Nothing. @throws {VoiceError} If the connection is destroyed or has no gateway transport. */\n public setSpeaking(flags: number = SpeakingFlags.Microphone): void {\n this.assertUsable();\n if (!this.gateway) {\n throw new VoiceError(\n \"A voice gateway transport is required to change speaking state.\",\n );\n }\n this.gateway.send({ op: 5, d: { speaking: flags, delay: 0, ssrc: 0 } });\n this.#emit(\"speaking\", flags, 0);\n }\n\n /** Ensures the connection is still usable. @returns Nothing. @throws {VoiceError} If destroyed. */\n #assertUsable(): void {\n if (this.state === VoiceConnectionState.Destroyed) {\n throw new VoiceError(\"Voice connection has been destroyed.\");\n }\n }\n\n /** Closes both active transports while isolating cleanup failures. @returns Nothing. */\n #closeTransports(): void {\n try {\n this.gateway?.close(1000, \"Voice connection replaced or closed\");\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof VoiceError\n ? error\n : new VoiceError(\"Voice gateway cleanup failed.\", {\n cause: error,\n }),\n );\n }\n try {\n this.udp?.close();\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof VoiceError\n ? error\n : new VoiceError(\"Voice UDP cleanup failed.\", {\n cause: error,\n }),\n );\n }\n this.gateway = undefined;\n this.udp = undefined;\n }\n\n /** Transitions to a new lifecycle state and emits the transition event. @param next Next state. @returns Nothing. */\n #transition(next: VoiceConnectionState): void {\n const previous = this.state;\n this.state = next;\n if (previous === next) return;\n for (const listener of this.#listeners.get(\"stateChange\") ?? []) {\n try {\n void listener(next, previous);\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof VoiceError\n ? error\n : new VoiceError(\"Voice event listener failed.\", {\n cause: error,\n }),\n );\n }\n if (this.state !== next && next !== VoiceConnectionState.Destroyed)\n return;\n }\n }\n\n /** Emits a voice event while isolating consumer failures. @param event Event name. @param args Event arguments. @returns Nothing. */\n #emit<K extends keyof VoiceEvents>(\n event: K,\n ...args: VoiceEvents[K]\n ): void {\n for (const listener of this.#listeners.get(event) ?? []) {\n try {\n void listener(...args);\n } catch {\n /* Consumer failures are isolated. */\n }\n }\n }\n\n /** Ensures the connection is still usable. @returns Nothing. @throws {VoiceError} If destroyed. */\n private assertUsable(): void {\n this.#assertUsable();\n }\n}\n\n// ─── Audio Pipeline ───────────────────────────────────────────────────────────\n\n/** Audio player lifecycle states. */\nexport type AudioPlayerState = \"idle\" | \"playing\" | \"paused\" | \"stopped\";\n\n/** An audio stream — wraps a `ReadableStream<Uint8Array>` with optional metadata. */\nexport class AudioStream {\n /** Underlying binary stream. */ public readonly stream: ReadableStream<Uint8Array>;\n /** Optional human-readable title for the stream. */ public readonly title?: string;\n /** Optional duration hint in seconds. */ public readonly duration?: number;\n\n /** Creates an audio stream.\n * @param stream Binary audio data stream.\n * @param metadata Optional metadata: title and duration in seconds.\n */\n public constructor(\n stream: ReadableStream<Uint8Array>,\n metadata?: { title?: string; duration?: number },\n ) {\n this.stream = stream;\n this.title = metadata?.title;\n this.duration = metadata?.duration;\n }\n}\n\ntype AudioPlayerListener<K extends keyof AudioPlayerEvents> = (\n ...args: AudioPlayerEvents[K]\n) => unknown;\n\ninterface AudioPlayerEvents {\n stateChange: [from: AudioPlayerState, to: AudioPlayerState];\n error: [error: Error];\n finish: [];\n}\n\n/**\n * Lightweight audio player with a simple state machine.\n *\n * States:\n * - `idle` — no stream loaded.\n * - `playing` — actively feeding audio data.\n * - `paused` — temporarily suspended; can be resumed.\n * - `stopped` — stream was stopped and discarded; load a new stream to play again.\n *\n * Events: `stateChange(from, to)`, `error(err)`, `finish()`.\n */\nexport class AudioPlayer {\n #state: AudioPlayerState = \"idle\";\n #stream?: AudioStream;\n #reader?: ReadableStreamDefaultReader<Uint8Array>;\n /** Resolves the pump's pause await when the player is resumed or stopped. */\n #resumeSignal?: () => void;\n readonly #listeners = new Map<\n string,\n Set<AudioPlayerListener<keyof AudioPlayerEvents>>\n >();\n\n /** Current player state. */\n public get state(): AudioPlayerState {\n return this.#state;\n }\n\n /** The audio stream currently loaded, if any. */\n public get current(): AudioStream | undefined {\n return this.#stream;\n }\n\n /**\n * Begins playing an audio stream.\n * If a stream is already playing, it is stopped first; the previous\n * stream's reader cancellation is awaited before the new stream starts so\n * the old and new pump loops cannot run concurrently.\n * @param audioStream Stream to play.\n */\n public async play(audioStream: AudioStream): Promise<void> {\n if (this.#state === \"playing\" || this.#state === \"paused\")\n await this.stop();\n this.#stream = audioStream;\n this.#reader = audioStream.stream.getReader();\n this.#transition(\"playing\");\n this.#pump().catch((err) => this.#emitError(err));\n }\n\n /** Pauses a currently-playing stream. No-op if not playing. */\n public pause(): void {\n if (this.#state !== \"playing\") return;\n this.#transition(\"paused\");\n }\n\n /** Resumes a paused stream. No-op if not paused. */\n public resume(): void {\n if (this.#state !== \"paused\") return;\n this.#transition(\"playing\");\n // Wake the existing pump loop rather than starting a second one.\n const signal = this.#resumeSignal;\n this.#resumeSignal = undefined;\n signal?.();\n }\n\n /**\n * Stops and discards the current stream. The returned promise resolves once\n * the underlying reader's cancellation has settled, so a subsequent `play`\n * never overlaps with the previous pump loop.\n */\n public async stop(): Promise<void> {\n if (this.#state === \"idle\" || this.#state === \"stopped\") return;\n const reader = this.#reader;\n this.#reader = undefined;\n this.#stream = undefined;\n this.#transition(\"stopped\");\n // Release a pump loop that is currently awaiting a resume.\n const signal = this.#resumeSignal;\n this.#resumeSignal = undefined;\n signal?.();\n if (reader) {\n try {\n await reader.cancel();\n } catch {\n /* Cancellation failures are non-fatal. */\n }\n }\n }\n\n /** Registers an event listener. @returns `this` for chaining. */\n public on<K extends keyof AudioPlayerEvents>(\n event: K,\n listener: AudioPlayerListener<K>,\n ): this {\n let set = this.#listeners.get(event) as\n Set<AudioPlayerListener<K>> | undefined;\n if (!set) {\n this.#listeners.set(\n event,\n (set = new Set() as unknown as Set<\n AudioPlayerListener<keyof AudioPlayerEvents>\n >),\n );\n }\n (set as unknown as Set<AudioPlayerListener<K>>).add(listener);\n return this;\n }\n\n /** Removes an event listener. @returns `this` for chaining. */\n public off<K extends keyof AudioPlayerEvents>(\n event: K,\n listener: AudioPlayerListener<K>,\n ): this {\n (\n this.#listeners.get(event) as\n Set<AudioPlayerListener<K>> | undefined\n )?.delete(listener);\n return this;\n }\n\n /**\n * Handles a decoded audio chunk. The base implementation is a no-op;\n * subclasses override this to pipe audio to a UDP socket or other sink.\n * @param chunk A chunk of audio bytes read from the current stream.\n */\n protected onChunk(chunk: Uint8Array): void {\n void chunk;\n }\n\n /** Reads chunks from the reader while the player is in a playing state. */\n async #pump(): Promise<void> {\n const reader = this.#reader;\n if (!reader) return;\n while (true) {\n // Suspend without polling: park until resume()/stop() signals us.\n if (this.#state === \"paused\") {\n await new Promise<void>((resolve) => {\n this.#resumeSignal = resolve;\n });\n }\n // Abort if stopped/idle (e.g. resolved by stop()).\n if (this.#state !== \"playing\") return;\n const { done, value } = await reader.read();\n if (done) {\n if (this.#state === \"playing\" || this.#state === \"paused\") {\n this.#transition(\"idle\");\n this.#stream = undefined;\n this.#reader = undefined;\n this.#emit(\"finish\");\n }\n return;\n }\n // A stop() during the read wins — drop the chunk and exit.\n if (this.#state !== \"playing\" && this.#state !== \"paused\") return;\n // Deliver the chunk to the sink hook; loop back to honour pause state.\n this.onChunk(value);\n }\n }\n\n #transition(next: AudioPlayerState): void {\n if (this.#state === next) return;\n const from = this.#state;\n this.#state = next;\n this.#emit(\"stateChange\", from, next);\n }\n\n #emitError(err: unknown): void {\n this.#emit(\n \"error\",\n err instanceof Error ? err : new VoiceError(String(err)),\n );\n }\n\n #emit<K extends keyof AudioPlayerEvents>(\n event: K,\n ...args: AudioPlayerEvents[K]\n ): void {\n for (const listener of this.#listeners.get(event) ?? []) {\n try {\n void (listener as (...a: unknown[]) => unknown)(...args);\n } catch {\n /* Listener failures are isolated. */\n }\n }\n }\n}\n\n// ─── Audio Receive Pipeline ───────────────────────────────────────────────────\n\n/** Tracks incoming audio and maps SSRCs to readable streams per user. */\nexport class VoiceReceiver {\n /** The connection this receiver is attached to. */\n public readonly connection: VoiceConnection;\n readonly #ssrcMap = new Map<number, string>();\n /** Active stream controllers per user; a Set so multiple subscribers to the\n * same user all receive audio instead of the last one overwriting earlier ones. */\n readonly #streams = new Map<\n string,\n Set<ReadableStreamDefaultController<Uint8Array>>\n >();\n\n /** Creates a VoiceReceiver. @param connection Voice connection. */\n public constructor(connection: VoiceConnection) {\n this.connection = connection;\n }\n\n /** Maps an SSRC to a User ID (usually from gateway speaking events). */\n public mapSsrc(ssrc: number, userId: string): void {\n this.#ssrcMap.set(ssrc, userId);\n }\n\n /** Subscribes to incoming audio from a specific user. @param userId User identifier. @returns A binary readable stream of the audio. */\n public subscribe(userId: string): AudioStream {\n let controllerRef: ReadableStreamDefaultController<Uint8Array>;\n const stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n controllerRef = controller;\n let set = this.#streams.get(userId);\n if (!set) this.#streams.set(userId, (set = new Set()));\n set.add(controller);\n },\n cancel: () => {\n const set = this.#streams.get(userId);\n if (set) {\n set.delete(controllerRef);\n if (set.size === 0) this.#streams.delete(userId);\n }\n },\n });\n return new AudioStream(stream, { title: `User ${userId} Audio` });\n }\n\n /** Processes an incoming UDP packet, extracting SSRC and routing the audio\n * payload (RTP header stripped) to every subscriber for the mapped user. */\n public onPacket(packet: Uint8Array): void {\n if (packet.length < 12) return;\n\n const view = new DataView(\n packet.buffer,\n packet.byteOffset,\n packet.byteLength,\n );\n const ssrc = view.getUint32(8, false);\n\n const userId = this.#ssrcMap.get(ssrc);\n if (!userId) return;\n const controllers = this.#streams.get(userId);\n if (!controllers || controllers.size === 0) return;\n\n // Compute the RTP payload offset: 12-byte fixed header + CSRC list, plus\n // the extension header when the X bit is set.\n const csrcCount = packet[0] & 0x0f;\n let offset = 12 + csrcCount * 4;\n const hasExtension = (packet[0] & 0x10) !== 0;\n if (hasExtension) {\n if (packet.length < offset + 4) return;\n const extWords = view.getUint16(offset + 2, false);\n offset += 4 + extWords * 4;\n }\n if (packet.length < offset) return;\n const payload = packet.slice(offset);\n\n for (const controller of controllers) {\n try {\n controller.enqueue(payload);\n } catch {\n // Ignore errors on closed streams.\n }\n }\n }\n}\n\n// ─── Discord.js-familiar helpers (@discordjs/voice) ───────────────────────────\n\n/** Discord.js-familiar alias for {@link VoiceConnectionState}. Member values are Lunibee's. */\nexport { VoiceConnectionState as VoiceConnectionStatus };\n\n/** Active connections by guild, as tracked by {@link joinVoiceChannel}. */\nconst voiceConnections = new Map<string, VoiceConnection>();\n\n/**\n * Joins (or moves to) a voice channel, reusing the guild's tracked connection.\n * Mirrors `@discordjs/voice`'s `joinVoiceChannel`; transports are still attached\n * with {@link VoiceConnection.attachTransports}.\n * @param options Guild, channel and self mute/deaf flags.\n * @returns The guild's voice connection.\n * @throws {TypeError} If the guild or channel ID is empty.\n */\nexport function joinVoiceChannel(\n options: VoiceConnectionOptions & { channelId: string },\n): VoiceConnection {\n let connection = voiceConnections.get(options.guildId);\n if (!connection) {\n connection = new VoiceConnection(options);\n const created = connection;\n // VoiceConnection emits stateChange as (next, previous).\n created.on(\"stateChange\", (next) => {\n if (\n next === VoiceConnectionState.Destroyed &&\n voiceConnections.get(created.guildId) === created\n )\n voiceConnections.delete(created.guildId);\n });\n voiceConnections.set(options.guildId, connection);\n }\n connection.setChannel(options.channelId);\n connection.setSuppression(options);\n connection.connect();\n return connection;\n}\n\n/** Returns the connection tracked by {@link joinVoiceChannel} for a guild, if any. */\nexport function getVoiceConnection(\n guildId: string,\n): VoiceConnection | undefined {\n return voiceConnections.get(guildId);\n}\n\n/** Creates an {@link AudioPlayer}. Mirrors `@discordjs/voice`'s `createAudioPlayer`. */\nexport function createAudioPlayer(): AudioPlayer {\n return new AudioPlayer();\n}\n\n/** Creates an {@link AudioStream}. Mirrors `@discordjs/voice`'s `createAudioResource`. */\nexport function createAudioResource(\n stream: ReadableStream<Uint8Array>,\n metadata?: { title?: string; duration?: number },\n): AudioStream {\n return new AudioStream(stream, metadata);\n}\n\n/**\n * Resolves once a connection or player reaches `status`, or rejects after `timeoutMs`.\n * Mirrors `@discordjs/voice`'s `entersState`.\n * @returns The same target, for chaining.\n * @throws {VoiceError} If the state is not reached in time.\n */\nexport function entersState<T extends VoiceConnection | AudioPlayer>(\n target: T,\n status: T extends VoiceConnection ? VoiceConnectionState : AudioPlayerState,\n timeoutMs: number,\n): Promise<T> {\n if (target.state === status) return Promise.resolve(target);\n const emitter = target as unknown as {\n on(\n event: \"stateChange\",\n fn: (from: unknown, to: unknown) => void,\n ): unknown;\n off(\n event: \"stateChange\",\n fn: (from: unknown, to: unknown) => void,\n ): unknown;\n };\n // VoiceConnection emits (next, previous); AudioPlayer emits (from, to).\n const isConnection = target instanceof VoiceConnection;\n return new Promise<T>((resolve, reject) => {\n const onChange = (first: unknown, second: unknown): void => {\n if ((isConnection ? first : second) !== status) return;\n clearTimeout(timer);\n emitter.off(\"stateChange\", onChange);\n resolve(target);\n };\n const timer = setTimeout(() => {\n emitter.off(\"stateChange\", onChange);\n reject(\n new VoiceError(\n `Did not enter state \"${String(status)}\" within ${timeoutMs}ms.`,\n ),\n );\n }, timeoutMs);\n emitter.on(\"stateChange\", onChange);\n });\n}\n"
5
+ "/** Voice session lifecycle states. */\nexport enum VoiceConnectionState {\n Disconnected = \"disconnected\",\n Connecting = \"connecting\",\n Connected = \"connected\",\n Destroyed = \"destroyed\",\n}\n\n/** Voice protocol transport errors. */\nexport class VoiceError extends Error {\n /** Creates a voice error. @param message Error message. @param options Optional native error options. */\n public constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"VoiceError\";\n }\n}\n\n/** Options used to establish a voice session. */\nexport interface VoiceConnectionOptions {\n /** Guild containing the voice channel. */\n guildId: string;\n /** Voice channel to join. */\n channelId?: string;\n /** Whether the self user should be muted. */\n selfMute?: boolean;\n /** Whether the self user should be deafened. */\n selfDeaf?: boolean;\n}\n\n/** UDP transport supplied by the runtime-specific voice implementation. */\nexport interface VoiceUdpTransport {\n /** Sends an RTP/RTCP packet. @param packet Packet bytes. @param host Destination host. @param port Destination port. @returns Optional completion promise. */\n send(packet: Uint8Array, host: string, port: number): Promise<void> | void;\n /** Closes the UDP socket. @returns Nothing. */\n close(): void;\n /** Optional listener for incoming UDP packets. */\n onMessage?(listener: (packet: Uint8Array) => void): void;\n}\n\n/** Voice Gateway transport abstraction. */\nexport interface VoiceGatewayTransport {\n /** Opens the voice gateway. @param endpoint Voice gateway endpoint. @param sessionId Voice session identifier. @returns Promise fulfilled when connected. */\n connect(endpoint: string, sessionId: string): Promise<void>;\n /** Sends a voice gateway payload. @param payload Gateway payload. @returns Nothing. */\n send(payload: unknown): void;\n /** Closes the voice gateway. @param code Optional close code. @param reason Optional close reason. @returns Nothing. */\n close(code?: number, reason?: string): void;\n}\n\n/** Speaking flags used by the voice gateway. */\nexport const SpeakingFlags = {\n Microphone: 1,\n Soundshare: 2,\n Priority: 4,\n} as const;\n\n/** Voice session event payloads. */\nexport interface VoiceEvents {\n /** State transition event. */\n stateChange: [VoiceConnectionState, VoiceConnectionState];\n /** Speaking flag event. */\n speaking: [number, number];\n /** Voice transport error event. */\n error: [VoiceError];\n}\n\ntype VoiceListener = (...args: any[]) => unknown;\n\n/** Tracks a Discord voice session and exposes transport-independent lifecycle controls. */\nexport class VoiceConnection {\n /** Current voice connection state. */\n public state: VoiceConnectionState = VoiceConnectionState.Disconnected;\n /** Guild associated with this voice session. */\n public readonly guildId: string;\n /** Voice channel associated with this session. */\n public channelId?: string;\n /** Whether the client is self-muted. */\n public selfMute: boolean;\n /** Whether the client is self-deafened. */\n public selfDeaf: boolean;\n /** Active voice gateway transport, when attached. */\n public gateway?: VoiceGatewayTransport;\n /** Active UDP transport, when attached. */\n public udp?: VoiceUdpTransport;\n /** Registered lifecycle listeners. */\n readonly #listeners = new Map<keyof VoiceEvents, Set<VoiceListener>>();\n /** Receives incoming audio. */\n public readonly receiver: VoiceReceiver;\n\n /** Creates a voice connection state manager. @param options Guild ID or connection options. @throws {TypeError} If the guild ID is empty. */\n public constructor(options: string | VoiceConnectionOptions) {\n const resolved =\n typeof options === \"string\" ? { guildId: options } : options;\n if (!resolved.guildId?.trim()) {\n throw new TypeError(\n \"A guild ID is required for a voice connection.\",\n );\n }\n this.guildId = resolved.guildId;\n this.channelId = resolved.channelId;\n this.selfMute = resolved.selfMute ?? false;\n this.selfDeaf = resolved.selfDeaf ?? false;\n this.receiver = new VoiceReceiver(this);\n }\n\n /** Registers a voice lifecycle listener. @param event Event name. @param listener Listener callback. @returns This connection. @throws {TypeError} If listener is not callable. */\n public on<K extends keyof VoiceEvents>(\n event: K,\n listener: (...args: VoiceEvents[K]) => unknown,\n ): this {\n if (typeof listener !== \"function\")\n throw new TypeError(\"Voice listener must be a function.\");\n let listeners = this.#listeners.get(event);\n if (!listeners) this.#listeners.set(event, (listeners = new Set()));\n listeners.add(listener);\n return this;\n }\n\n /** Removes a voice lifecycle listener. @param event Event name. @param listener Listener callback. @returns This connection. */\n public off<K extends keyof VoiceEvents>(\n event: K,\n listener: (...args: VoiceEvents[K]) => unknown,\n ): this {\n this.#listeners.get(event)?.delete(listener);\n return this;\n }\n\n /** Attaches protocol transports, replacing and cleaning up the old pair when present. @param gateway Voice gateway transport. @param udp UDP transport. @returns This connection. @throws {VoiceError} If the connection is destroyed. @throws {TypeError} If a transport is missing. */\n public attachTransports(\n gateway: VoiceGatewayTransport,\n udp: VoiceUdpTransport,\n ): this {\n this.assertUsable();\n if (!gateway || !udp)\n throw new TypeError(\n \"Both voice gateway and UDP transports are required.\",\n );\n if (this.gateway || this.udp) this.#closeTransports();\n this.gateway = gateway;\n this.udp = udp;\n // Capture the transport this listener belongs to so that packets from a\n // later-replaced transport (whose listener cannot be unregistered — the\n // VoiceUdpTransport interface exposes no removal API) are ignored.\n const attachedUdp = udp;\n if (attachedUdp.onMessage) {\n attachedUdp.onMessage((packet) => {\n if (this.udp === attachedUdp) this.receiver.onPacket(packet);\n });\n }\n return this;\n }\n\n /** Begins the connection lifecycle. @returns Nothing. @throws {VoiceError} If the connection is destroyed. */\n public connect(): void {\n this.assertUsable();\n if (this.state === VoiceConnectionState.Connected) return;\n this.#transition(VoiceConnectionState.Connecting);\n if (this.state !== VoiceConnectionState.Connecting) return;\n this.#transition(VoiceConnectionState.Connected);\n }\n\n /** Disconnects the current voice session. @returns Nothing. */\n public disconnect(): void {\n if (this.state === VoiceConnectionState.Destroyed) return;\n this.#closeTransports();\n this.receiver.close();\n this.#transition(VoiceConnectionState.Disconnected);\n }\n\n /** Permanently destroys this voice connection. @returns Nothing. */\n public destroy(): void {\n if (this.state === VoiceConnectionState.Destroyed) return;\n this.#closeTransports();\n this.receiver.close();\n this.channelId = undefined;\n this.#transition(VoiceConnectionState.Destroyed);\n this.#listeners.clear();\n }\n\n /** Updates the voice channel and connection state. @param channelId Voice channel identifier. @returns Nothing. @throws {VoiceError} If the connection is destroyed. @throws {TypeError} If channelId is empty. */\n public setChannel(channelId: string): void {\n this.assertUsable();\n if (!channelId?.trim())\n throw new TypeError(\"A voice channel ID is required.\");\n this.channelId = channelId;\n }\n\n /** Updates self mute/deaf state. @param options Suppression flags. @returns Nothing. @throws {VoiceError} If the connection is destroyed. */\n public setSuppression(options: {\n selfMute?: boolean;\n selfDeaf?: boolean;\n }): void {\n this.assertUsable();\n if (options.selfMute !== undefined) this.selfMute = options.selfMute;\n if (options.selfDeaf !== undefined) this.selfDeaf = options.selfDeaf;\n }\n\n /** Publishes a speaking state through the attached voice gateway. @param flags Speaking flags. @returns Nothing. @throws {VoiceError} If the connection is destroyed or has no gateway transport. */\n public setSpeaking(flags: number = SpeakingFlags.Microphone): void {\n this.assertUsable();\n if (!this.gateway) {\n throw new VoiceError(\n \"A voice gateway transport is required to change speaking state.\",\n );\n }\n this.gateway.send({ op: 5, d: { speaking: flags, delay: 0, ssrc: 0 } });\n this.#emit(\"speaking\", flags, 0);\n }\n\n /** Ensures the connection is still usable. @returns Nothing. @throws {VoiceError} If destroyed. */\n #assertUsable(): void {\n if (this.state === VoiceConnectionState.Destroyed) {\n throw new VoiceError(\"Voice connection has been destroyed.\");\n }\n }\n\n /** Closes both active transports while isolating cleanup failures. @returns Nothing. */\n #closeTransports(): void {\n try {\n this.gateway?.close(1000, \"Voice connection replaced or closed\");\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof VoiceError\n ? error\n : new VoiceError(\"Voice gateway cleanup failed.\", {\n cause: error,\n }),\n );\n }\n try {\n this.udp?.close();\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof VoiceError\n ? error\n : new VoiceError(\"Voice UDP cleanup failed.\", {\n cause: error,\n }),\n );\n }\n this.gateway = undefined;\n this.udp = undefined;\n }\n\n /** Transitions to a new lifecycle state and emits the transition event. @param next Next state. @returns Nothing. */\n #transition(next: VoiceConnectionState): void {\n const previous = this.state;\n this.state = next;\n if (previous === next) return;\n for (const listener of this.#listeners.get(\"stateChange\") ?? []) {\n try {\n void listener(next, previous);\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof VoiceError\n ? error\n : new VoiceError(\"Voice event listener failed.\", {\n cause: error,\n }),\n );\n }\n if (this.state !== next && next !== VoiceConnectionState.Destroyed)\n return;\n }\n }\n\n /** Emits a voice event while isolating consumer failures. @param event Event name. @param args Event arguments. @returns Nothing. */\n #emit<K extends keyof VoiceEvents>(\n event: K,\n ...args: VoiceEvents[K]\n ): void {\n for (const listener of this.#listeners.get(event) ?? []) {\n try {\n void listener(...args);\n } catch {\n /* Consumer failures are isolated. */\n }\n }\n }\n\n /** Ensures the connection is still usable. @returns Nothing. @throws {VoiceError} If destroyed. */\n private assertUsable(): void {\n this.#assertUsable();\n }\n}\n\n// ─── Audio Pipeline ───────────────────────────────────────────────────────────\n\n/** Audio player lifecycle states. */\nexport type AudioPlayerState = \"idle\" | \"playing\" | \"paused\" | \"stopped\";\n\n/** An audio stream — wraps a `ReadableStream<Uint8Array>` with optional metadata. */\nexport class AudioStream {\n /** Underlying binary stream. */ public readonly stream: ReadableStream<Uint8Array>;\n /** Optional human-readable title for the stream. */ public readonly title?: string;\n /** Optional duration hint in seconds. */ public readonly duration?: number;\n\n /** Creates an audio stream.\n * @param stream Binary audio data stream.\n * @param metadata Optional metadata: title and duration in seconds.\n */\n public constructor(\n stream: ReadableStream<Uint8Array>,\n metadata?: { title?: string; duration?: number },\n ) {\n this.stream = stream;\n this.title = metadata?.title;\n this.duration = metadata?.duration;\n }\n}\n\ntype AudioPlayerListener<K extends keyof AudioPlayerEvents> = (\n ...args: AudioPlayerEvents[K]\n) => unknown;\n\ninterface AudioPlayerEvents {\n stateChange: [from: AudioPlayerState, to: AudioPlayerState];\n error: [error: Error];\n finish: [];\n}\n\n/**\n * Lightweight audio player with a simple state machine.\n *\n * States:\n * - `idle` — no stream loaded.\n * - `playing` — actively feeding audio data.\n * - `paused` — temporarily suspended; can be resumed.\n * - `stopped` — stream was stopped and discarded; load a new stream to play again.\n *\n * Events: `stateChange(from, to)`, `error(err)`, `finish()`.\n */\nexport class AudioPlayer {\n #state: AudioPlayerState = \"idle\";\n #stream?: AudioStream;\n #reader?: ReadableStreamDefaultReader<Uint8Array>;\n /** Resolves the pump's pause await when the player is resumed or stopped. */\n #resumeSignal?: () => void;\n readonly #listeners = new Map<\n string,\n Set<AudioPlayerListener<keyof AudioPlayerEvents>>\n >();\n\n /** Current player state. */\n public get state(): AudioPlayerState {\n return this.#state;\n }\n\n /** The audio stream currently loaded, if any. */\n public get current(): AudioStream | undefined {\n return this.#stream;\n }\n\n /**\n * Begins playing an audio stream.\n * If a stream is already playing, it is stopped first; the previous\n * stream's reader cancellation is awaited before the new stream starts so\n * the old and new pump loops cannot run concurrently.\n * @param audioStream Stream to play.\n */\n public async play(audioStream: AudioStream): Promise<void> {\n if (this.#state === \"playing\" || this.#state === \"paused\")\n await this.stop();\n this.#stream = audioStream;\n this.#reader = audioStream.stream.getReader();\n this.#transition(\"playing\");\n this.#pump().catch((err) => this.#emitError(err));\n }\n\n /** Pauses a currently-playing stream. No-op if not playing. */\n public pause(): void {\n if (this.#state !== \"playing\") return;\n this.#transition(\"paused\");\n }\n\n /** Resumes a paused stream. No-op if not paused. */\n public resume(): void {\n if (this.#state !== \"paused\") return;\n this.#transition(\"playing\");\n // Wake the existing pump loop rather than starting a second one.\n const signal = this.#resumeSignal;\n this.#resumeSignal = undefined;\n signal?.();\n }\n\n /**\n * Stops and discards the current stream. The returned promise resolves once\n * the underlying reader's cancellation has settled, so a subsequent `play`\n * never overlaps with the previous pump loop.\n */\n public async stop(): Promise<void> {\n if (this.#state === \"idle\" || this.#state === \"stopped\") return;\n const reader = this.#reader;\n this.#reader = undefined;\n this.#stream = undefined;\n this.#transition(\"stopped\");\n // Release a pump loop that is currently awaiting a resume.\n const signal = this.#resumeSignal;\n this.#resumeSignal = undefined;\n signal?.();\n if (reader) {\n try {\n await reader.cancel();\n } catch {\n /* Cancellation failures are non-fatal. */\n }\n }\n }\n\n /** Registers an event listener. @returns `this` for chaining. */\n public on<K extends keyof AudioPlayerEvents>(\n event: K,\n listener: AudioPlayerListener<K>,\n ): this {\n let set = this.#listeners.get(event) as\n Set<AudioPlayerListener<K>> | undefined;\n if (!set) {\n this.#listeners.set(\n event,\n (set = new Set() as unknown as Set<\n AudioPlayerListener<keyof AudioPlayerEvents>\n >),\n );\n }\n (set as unknown as Set<AudioPlayerListener<K>>).add(listener);\n return this;\n }\n\n /** Removes an event listener. @returns `this` for chaining. */\n public off<K extends keyof AudioPlayerEvents>(\n event: K,\n listener: AudioPlayerListener<K>,\n ): this {\n (\n this.#listeners.get(event) as\n Set<AudioPlayerListener<K>> | undefined\n )?.delete(listener);\n return this;\n }\n\n /**\n * Handles a decoded audio chunk. The base implementation is a no-op;\n * subclasses override this to pipe audio to a UDP socket or other sink.\n * @param chunk A chunk of audio bytes read from the current stream.\n */\n protected onChunk(chunk: Uint8Array): void {\n void chunk;\n }\n\n /** Reads chunks from the reader while the player is in a playing state. */\n async #pump(): Promise<void> {\n const reader = this.#reader;\n if (!reader) return;\n while (true) {\n // Suspend without polling: park until resume()/stop() signals us.\n if (this.#state === \"paused\") {\n await new Promise<void>((resolve) => {\n this.#resumeSignal = resolve;\n });\n }\n // Abort if stopped/idle (e.g. resolved by stop()).\n if (this.#state !== \"playing\") return;\n const { done, value } = await reader.read();\n if (done) {\n if (this.#state === \"playing\" || this.#state === \"paused\") {\n this.#transition(\"idle\");\n this.#stream = undefined;\n this.#reader = undefined;\n this.#emit(\"finish\");\n }\n return;\n }\n // A stop() during the read wins — drop the chunk and exit.\n if (this.#state !== \"playing\" && this.#state !== \"paused\") return;\n // Deliver the chunk to the sink hook; loop back to honour pause state.\n this.onChunk(value);\n }\n }\n\n #transition(next: AudioPlayerState): void {\n if (this.#state === next) return;\n const from = this.#state;\n this.#state = next;\n this.#emit(\"stateChange\", from, next);\n }\n\n #emitError(err: unknown): void {\n this.#emit(\n \"error\",\n err instanceof Error ? err : new VoiceError(String(err)),\n );\n }\n\n #emit<K extends keyof AudioPlayerEvents>(\n event: K,\n ...args: AudioPlayerEvents[K]\n ): void {\n for (const listener of this.#listeners.get(event) ?? []) {\n try {\n void (listener as (...a: unknown[]) => unknown)(...args);\n } catch {\n /* Listener failures are isolated. */\n }\n }\n }\n}\n\n// ─── Audio Receive Pipeline ───────────────────────────────────────────────────\n\n/** Tracks incoming audio and maps SSRCs to readable streams per user. */\nexport class VoiceReceiver {\n /** The connection this receiver is attached to. */\n public readonly connection: VoiceConnection;\n readonly #ssrcMap = new Map<number, string>();\n /** Active stream controllers per user; a Set so multiple subscribers to the\n * same user all receive audio instead of the last one overwriting earlier ones. */\n readonly #streams = new Map<\n string,\n Set<ReadableStreamDefaultController<Uint8Array>>\n >();\n\n /** Creates a VoiceReceiver. @param connection Voice connection. */\n public constructor(connection: VoiceConnection) {\n this.connection = connection;\n }\n\n /** Maps an SSRC to a User ID (usually from gateway speaking events). */\n public mapSsrc(ssrc: number, userId: string): void {\n this.#ssrcMap.set(ssrc, userId);\n }\n\n /** Subscribes to incoming audio from a specific user. @param userId User identifier. @returns A binary readable stream of the audio. */\n public subscribe(userId: string): AudioStream {\n // A destroyed connection never closes its receiver again, so a late\n // subscriber would wait forever.\n if (this.connection.state === VoiceConnectionState.Destroyed)\n throw new VoiceError(\"Voice connection has been destroyed.\");\n let controllerRef: ReadableStreamDefaultController<Uint8Array>;\n const stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n controllerRef = controller;\n let set = this.#streams.get(userId);\n if (!set) this.#streams.set(userId, (set = new Set()));\n set.add(controller);\n },\n cancel: () => {\n const set = this.#streams.get(userId);\n if (set) {\n set.delete(controllerRef);\n if (set.size === 0) this.#streams.delete(userId);\n }\n },\n });\n return new AudioStream(stream, { title: `User ${userId} Audio` });\n }\n\n /** Ends every subscriber stream and forgets SSRC mappings. Called when the\n * connection disconnects or is destroyed, so readers do not wait forever. */\n public close(): void {\n for (const controllers of this.#streams.values())\n for (const controller of controllers) {\n try {\n controller.close();\n } catch {\n // Already closed or errored by the consumer.\n }\n }\n this.#streams.clear();\n this.#ssrcMap.clear();\n }\n\n /** Processes an incoming UDP packet, extracting SSRC and routing the audio\n * payload (RTP header stripped) to every subscriber for the mapped user. */\n public onPacket(packet: Uint8Array): void {\n if (packet.length < 12) return;\n\n const view = new DataView(\n packet.buffer,\n packet.byteOffset,\n packet.byteLength,\n );\n const ssrc = view.getUint32(8, false);\n\n const userId = this.#ssrcMap.get(ssrc);\n if (!userId) return;\n const controllers = this.#streams.get(userId);\n if (!controllers || controllers.size === 0) return;\n\n // Compute the RTP payload offset: 12-byte fixed header + CSRC list, plus\n // the extension header when the X bit is set.\n const csrcCount = packet[0] & 0x0f;\n let offset = 12 + csrcCount * 4;\n const hasExtension = (packet[0] & 0x10) !== 0;\n if (hasExtension) {\n if (packet.length < offset + 4) return;\n const extWords = view.getUint16(offset + 2, false);\n offset += 4 + extWords * 4;\n }\n if (packet.length < offset) return;\n const payload = packet.slice(offset);\n\n for (const controller of controllers) {\n try {\n controller.enqueue(payload);\n } catch {\n // Ignore errors on closed streams.\n }\n }\n }\n}\n\n// ─── Discord.js-familiar helpers (@discordjs/voice) ───────────────────────────\n\n/** Discord.js-familiar alias for {@link VoiceConnectionState}. Member values are Lunibee's. */\nexport { VoiceConnectionState as VoiceConnectionStatus };\n\n/** Active connections by guild, as tracked by {@link joinVoiceChannel}. */\nconst voiceConnections = new Map<string, VoiceConnection>();\n\n/**\n * Joins (or moves to) a voice channel, reusing the guild's tracked connection.\n * Mirrors `@discordjs/voice`'s `joinVoiceChannel`; transports are still attached\n * with {@link VoiceConnection.attachTransports}.\n * @param options Guild, channel and self mute/deaf flags.\n * @returns The guild's voice connection.\n * @throws {TypeError} If the guild or channel ID is empty.\n */\nexport function joinVoiceChannel(\n options: VoiceConnectionOptions & { channelId: string },\n): VoiceConnection {\n let connection = voiceConnections.get(options.guildId);\n if (!connection) {\n connection = new VoiceConnection(options);\n const created = connection;\n // VoiceConnection emits stateChange as (next, previous).\n created.on(\"stateChange\", (next) => {\n if (\n next === VoiceConnectionState.Destroyed &&\n voiceConnections.get(created.guildId) === created\n )\n voiceConnections.delete(created.guildId);\n });\n voiceConnections.set(options.guildId, connection);\n }\n connection.setChannel(options.channelId);\n connection.setSuppression(options);\n connection.connect();\n return connection;\n}\n\n/** Returns the connection tracked by {@link joinVoiceChannel} for a guild, if any. */\nexport function getVoiceConnection(\n guildId: string,\n): VoiceConnection | undefined {\n return voiceConnections.get(guildId);\n}\n\n/** Creates an {@link AudioPlayer}. Mirrors `@discordjs/voice`'s `createAudioPlayer`. */\nexport function createAudioPlayer(): AudioPlayer {\n return new AudioPlayer();\n}\n\n/** Creates an {@link AudioStream}. Mirrors `@discordjs/voice`'s `createAudioResource`. */\nexport function createAudioResource(\n stream: ReadableStream<Uint8Array>,\n metadata?: { title?: string; duration?: number },\n): AudioStream {\n return new AudioStream(stream, metadata);\n}\n\n/**\n * Resolves once a connection or player reaches `status`, or rejects after `timeoutMs`.\n * Mirrors `@discordjs/voice`'s `entersState`.\n * @returns The same target, for chaining.\n * @throws {VoiceError} If the state is not reached in time.\n */\nexport function entersState<T extends VoiceConnection | AudioPlayer>(\n target: T,\n status: T extends VoiceConnection ? VoiceConnectionState : AudioPlayerState,\n timeoutMs: number,\n): Promise<T> {\n if (target.state === status) return Promise.resolve(target);\n const emitter = target as unknown as {\n on(\n event: \"stateChange\",\n fn: (from: unknown, to: unknown) => void,\n ): unknown;\n off(\n event: \"stateChange\",\n fn: (from: unknown, to: unknown) => void,\n ): unknown;\n };\n // VoiceConnection emits (next, previous); AudioPlayer emits (from, to).\n const isConnection = target instanceof VoiceConnection;\n return new Promise<T>((resolve, reject) => {\n const onChange = (first: unknown, second: unknown): void => {\n if ((isConnection ? first : second) !== status) return;\n clearTimeout(timer);\n emitter.off(\"stateChange\", onChange);\n resolve(target);\n };\n const timer = setTimeout(() => {\n emitter.off(\"stateChange\", onChange);\n reject(\n new VoiceError(\n `Did not enter state \"${String(status)}\" within ${timeoutMs}ms.`,\n ),\n );\n }, timeoutMs);\n emitter.on(\"stateChange\", onChange);\n });\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "C61FD04CD1E9BF4D64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "9E96BE25F2F4C69B64756E2164756E21",
9
9
  "names": []
10
10
  }
package/dist/ws/index.js CHANGED
@@ -34,6 +34,8 @@ class GatewaySession {
34
34
  return false;
35
35
  if (!Number.isInteger(sequence))
36
36
  return false;
37
+ if (this.#sequence !== null && sequence < this.#sequence)
38
+ return false;
37
39
  this.#sequence = sequence;
38
40
  return true;
39
41
  }
@@ -132,8 +134,11 @@ class GatewayHeartbeat {
132
134
  this.#clearBeatTimers();
133
135
  this.#interval = interval;
134
136
  this.#acknowledged = true;
135
- this.#initialTimer = setTimeout(() => this.sendHeartbeat(), Math.random() * interval);
136
- this.#timer = setInterval(() => this.sendHeartbeat(), interval);
137
+ this.#initialTimer = setTimeout(() => {
138
+ this.#initialTimer = undefined;
139
+ this.sendHeartbeat();
140
+ this.#timer = setInterval(() => this.sendHeartbeat(), interval);
141
+ }, Math.random() * interval);
137
142
  }
138
143
  stop() {
139
144
  this.#clearBeatTimers();
@@ -159,6 +164,8 @@ class GatewayHeartbeat {
159
164
  }, this.#options.ackTimeout);
160
165
  }
161
166
  acknowledge() {
167
+ if (this.#acknowledged)
168
+ return;
162
169
  this.#acknowledged = true;
163
170
  this.#latency = this.#now() - this.#sentAt;
164
171
  this.#clearAckTimer();
@@ -816,6 +823,7 @@ class SendBudget {
816
823
  }
817
824
 
818
825
  // packages/ws/src/index.ts
826
+ var RESUMABLE_CLOSE_CODE = 4900;
819
827
  var DEFAULT_GATEWAY_URL = "wss://gateway.discord.gg/?v=10&encoding=json";
820
828
 
821
829
  class Gateway {
@@ -901,7 +909,7 @@ class Gateway {
901
909
  } else {
902
910
  this.#emitError(new GatewayError(`Gateway heartbeat acknowledgement timed out after ${timeout.elapsedMs}ms.`));
903
911
  }
904
- this.#transport.close(1001, timeout.type === "zombie" ? "Zombie Gateway connection" : "Heartbeat timeout");
912
+ this.#transport.close(RESUMABLE_CLOSE_CODE, timeout.type === "zombie" ? "Zombie Gateway connection" : "Heartbeat timeout");
905
913
  }
906
914
  connect(url = DEFAULT_GATEWAY_URL) {
907
915
  if (this.state === "CLOSED" /* Closed */)
@@ -1037,7 +1045,7 @@ class Gateway {
1037
1045
  this.#emit("heartbeatAck", action.data);
1038
1046
  return;
1039
1047
  case "reconnect":
1040
- this.#transport.close(1001, "Server requested reconnect");
1048
+ this.#transport.close(RESUMABLE_CLOSE_CODE, "Server requested reconnect");
1041
1049
  return;
1042
1050
  case "invalid-session": {
1043
1051
  if (!action.resumable)
@@ -1184,4 +1192,4 @@ export {
1184
1192
  resumePayload
1185
1193
  };
1186
1194
 
1187
- //# debugId=09C4F2BCB61445DA64756E2164756E21
1195
+ //# debugId=DF17709D7B996C6F64756E2164756E21