lunibee 0.3.1 → 0.3.3

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 (81) hide show
  1. package/dist/builders/attachment.d.ts +1 -0
  2. package/dist/builders/clone.d.ts +10 -0
  3. package/dist/builders/commands.d.ts +1 -1
  4. package/dist/builders/components.d.ts +24 -5
  5. package/dist/builders/index.js +64 -17
  6. package/dist/builders/index.js.map +10 -9
  7. package/dist/collection/index.d.ts +1 -0
  8. package/dist/collection/index.js +297 -19
  9. package/dist/collection/index.js.map +5 -4
  10. package/dist/collection/redis.d.ts +98 -0
  11. package/dist/core/collector.d.ts +8 -0
  12. package/dist/core/events.d.ts +1 -0
  13. package/dist/core/index.d.ts +3 -0
  14. package/dist/core/index.js +6242 -4733
  15. package/dist/core/index.js.map +47 -40
  16. package/dist/core/permissions.d.ts +3 -402
  17. package/dist/core/redis-cache.d.ts +85 -0
  18. package/dist/formatters/index.d.ts +5 -1
  19. package/dist/formatters/index.js +7 -3
  20. package/dist/formatters/index.js.map +3 -3
  21. package/dist/handlers/index.js +11 -2
  22. package/dist/handlers/index.js.map +3 -3
  23. package/dist/index.js +8688 -6136
  24. package/dist/index.js.map +67 -59
  25. package/dist/managers/application.d.ts +10 -6
  26. package/dist/managers/base.d.ts +11 -1
  27. package/dist/managers/guild.d.ts +3 -1
  28. package/dist/managers/index.d.ts +6 -1
  29. package/dist/managers/index.js +383 -109
  30. package/dist/managers/index.js.map +20 -23
  31. package/dist/managers/member.d.ts +3 -1
  32. package/dist/managers/message.d.ts +13 -4
  33. package/dist/managers/thread.d.ts +3 -3
  34. package/dist/rest/decoder.d.ts +2 -0
  35. package/dist/rest/errors.d.ts +5 -2
  36. package/dist/rest/headers.d.ts +31 -0
  37. package/dist/rest/identity.d.ts +31 -0
  38. package/dist/rest/index.d.ts +97 -6
  39. package/dist/rest/index.js +1355 -220
  40. package/dist/rest/index.js.map +16 -13
  41. package/dist/rest/limiter.d.ts +75 -10
  42. package/dist/rest/redis.d.ts +103 -22
  43. package/dist/rest/scheduler.d.ts +13 -0
  44. package/dist/rest/store.d.ts +135 -12
  45. package/dist/rest/telemetry.d.ts +151 -0
  46. package/dist/rest/webhook.d.ts +18 -6
  47. package/dist/sharding/bus.d.ts +4 -2
  48. package/dist/sharding/cluster.d.ts +29 -1
  49. package/dist/sharding/health.d.ts +1 -1
  50. package/dist/sharding/host.d.ts +13 -2
  51. package/dist/sharding/identify.d.ts +2 -0
  52. package/dist/sharding/index.d.ts +8 -0
  53. package/dist/sharding/index.js +579 -65
  54. package/dist/sharding/index.js.map +17 -15
  55. package/dist/sharding/leader.d.ts +34 -0
  56. package/dist/sharding/remote.d.ts +36 -2
  57. package/dist/sharding/supervisor.d.ts +1 -1
  58. package/dist/sharding/thread.d.ts +30 -0
  59. package/dist/sharding/transport.d.ts +6 -5
  60. package/dist/sharding/worker.d.ts +11 -1
  61. package/dist/structures/base.d.ts +16 -5
  62. package/dist/structures/index.d.ts +1 -1
  63. package/dist/structures/index.js +57 -91
  64. package/dist/structures/index.js.map +10 -18
  65. package/dist/structures/options.d.ts +3 -2
  66. package/dist/structures/permissions.d.ts +409 -0
  67. package/dist/structures/resources.d.ts +4 -1
  68. package/dist/types/gateway.d.ts +4 -0
  69. package/dist/types/index.d.ts +14 -4
  70. package/dist/types/index.js.map +3 -3
  71. package/dist/utils/args.d.ts +3 -1
  72. package/dist/utils/index.d.ts +6 -1
  73. package/dist/utils/index.js +17 -10
  74. package/dist/utils/index.js.map +4 -4
  75. package/dist/voice/index.d.ts +7 -1
  76. package/dist/voice/index.js +33 -10
  77. package/dist/voice/index.js.map +3 -3
  78. package/dist/ws/index.d.ts +2 -0
  79. package/dist/ws/index.js +32 -4
  80. package/dist/ws/index.js.map +5 -5
  81. package/package.json +21 -17
@@ -1,3 +1,4 @@
1
+ /** Options for `CreateAttachment`: file name, alt-text description, the contents or a file path, and an optional root folder the path must stay inside. */
1
2
  export interface AttachmentData {
2
3
  name: string;
3
4
  description?: string;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A deep copy of builder data, for `toJSON()`.
3
+ *
4
+ * Builder payloads are plain JSON: objects, arrays and primitives. Copying
5
+ * those directly is several times faster than `structuredClone` on the small
6
+ * payloads builders hold. Anything else (a Date, a Map, a class instance) and
7
+ * anything nested deeper than 64 levels (a cycle) goes to `structuredClone`,
8
+ * so the result is what `structuredClone` would give.
9
+ */
10
+ export declare function cloneData<T>(value: T, depth?: number): T;
@@ -69,7 +69,7 @@ export declare class CreateCommandOption {
69
69
  setDescription(description: string): this;
70
70
  /** Makes the option required or optional. */
71
71
  setRequired(required?: boolean): this;
72
- /** Sets autocomplete. */
72
+ /** Sets autocomplete; options with choices cannot use it. */
73
73
  setAutocomplete(enabled?: boolean): this;
74
74
  /** Serializes the option payload. */
75
75
  toJSON(): Record<string, unknown>;
@@ -6,11 +6,13 @@ export declare const TextInputType: {
6
6
  };
7
7
  /** One {@link TextInputType} value, usable as a type. */
8
8
  export type TextInputType = (typeof TextInputType)[keyof typeof TextInputType];
9
+ /** An emoji on a button or select option: a custom emoji `id`, or a Unicode `name`. */
9
10
  export interface APIComponentEmoji {
10
11
  id?: string | null;
11
12
  name?: string | null;
12
13
  animated?: boolean;
13
14
  }
15
+ /** A button as a builder sends it. Exported from `lunibee` as `APIButtonPayload`. */
14
16
  export interface APIButtonComponent {
15
17
  type: typeof ComponentEnum.Button;
16
18
  style: 1 | 2 | 3 | 4 | 5 | 6;
@@ -22,6 +24,7 @@ export interface APIButtonComponent {
22
24
  sku_id?: string;
23
25
  disabled?: boolean;
24
26
  }
27
+ /** One option of a string select menu. */
25
28
  export interface APISelectOption {
26
29
  label: string;
27
30
  value: string;
@@ -29,6 +32,7 @@ export interface APISelectOption {
29
32
  emoji?: APIComponentEmoji;
30
33
  default?: boolean;
31
34
  }
35
+ /** A string select menu (component type 3) as Discord receives it. */
32
36
  export interface APIStringSelectComponent {
33
37
  type: typeof ComponentEnum.StringSelect;
34
38
  custom_id?: string;
@@ -39,6 +43,7 @@ export interface APIStringSelectComponent {
39
43
  disabled?: boolean;
40
44
  options?: APISelectOption[];
41
45
  }
46
+ /** A user, role, mentionable or channel select menu as Discord receives it. */
42
47
  export interface APIEntitySelectComponent {
43
48
  type: typeof ComponentEnum.UserSelect | typeof ComponentEnum.RoleSelect | typeof ComponentEnum.MentionableSelect | typeof ComponentEnum.ChannelSelect;
44
49
  custom_id?: string;
@@ -49,6 +54,7 @@ export interface APIEntitySelectComponent {
49
54
  disabled?: boolean;
50
55
  default_values?: APISelectDefaultValue[];
51
56
  }
57
+ /** A modal text input as a builder sends it. Exported from `lunibee` as `APITextInputPayload`. */
52
58
  export interface APITextInputComponent {
53
59
  type: typeof ComponentEnum.TextInput;
54
60
  style: typeof TextInputType.Short | typeof TextInputType.Paragraph;
@@ -60,33 +66,40 @@ export interface APITextInputComponent {
60
66
  required?: boolean;
61
67
  value?: string;
62
68
  }
69
+ /** A modal (callback type 9 data): title, custom ID and its components. */
63
70
  export interface APIModalComponent {
64
71
  type: 9;
65
72
  custom_id: string;
66
73
  title: string;
67
74
  components: APIActionRowComponent[];
68
75
  }
76
+ /** Any component an action row can hold: a button, a select menu or a text input. */
69
77
  export type APIActionRowChild = APIButtonComponent | APIStringSelectComponent | APIEntitySelectComponent | APITextInputComponent;
78
+ /** An action row as a builder sends it: up to five buttons, or one select menu or text input. Exported from `lunibee` as `APIActionRowPayload`. */
70
79
  export interface APIActionRowComponent {
71
80
  type: typeof ComponentEnum.ActionRow;
72
81
  components: APIActionRowChild[];
73
82
  }
83
+ /** A Components V2 section: up to three text displays with an accessory. */
74
84
  export interface APISectionComponent {
75
85
  type: typeof ComponentEnum.Section;
76
86
  components?: APIComponent[];
77
87
  accessory?: APIComponent;
78
88
  }
89
+ /** A Components V2 text display (Markdown content). */
79
90
  export interface APITextDisplayComponent {
80
91
  type: typeof ComponentEnum.TextDisplay;
81
92
  content: string;
82
93
  }
94
+ /** A Components V2 media gallery; each item is `{ media: { url }, description?, spoiler? }`. */
83
95
  export interface APIMediaGalleryComponent {
84
96
  type: typeof ComponentEnum.MediaGallery;
85
97
  items: {
86
98
  media: {
87
99
  url: string;
88
- description?: string;
89
100
  };
101
+ description?: string;
102
+ spoiler?: boolean;
90
103
  }[];
91
104
  }
92
105
  /** Default values accepted by an auto-populated entity select. */
@@ -94,6 +107,7 @@ export interface APISelectDefaultValue {
94
107
  id: string;
95
108
  type: "user" | "role" | "channel";
96
109
  }
110
+ /** A Components V2 file display for an uploaded attachment. */
97
111
  export interface APIFileComponent {
98
112
  type: typeof ComponentEnum.File;
99
113
  file: {
@@ -101,30 +115,34 @@ export interface APIFileComponent {
101
115
  };
102
116
  spoiler?: boolean;
103
117
  }
118
+ /** A Components V2 separator, with optional divider line and spacing. */
104
119
  export interface APISeparatorComponent {
105
120
  type: typeof ComponentEnum.Separator;
106
121
  spacing?: 1 | 2;
107
122
  divider?: boolean;
108
123
  }
124
+ /** A Components V2 thumbnail: `{ type: 11, media: { url }, description?, spoiler? }`. */
109
125
  export interface APIThumbnailComponent {
110
126
  type: typeof ComponentEnum.Thumbnail;
111
- url: string;
112
- proxy_url?: string;
113
- width?: number;
114
- height?: number;
127
+ media: {
128
+ url: string;
129
+ };
115
130
  description?: string;
116
131
  spoiler?: boolean;
117
132
  }
133
+ /** A Components V2 content inventory entry. */
118
134
  export interface APIContentInventoryEntryComponent {
119
135
  type: typeof ComponentEnum.ContentInventoryEntry;
120
136
  id: string;
121
137
  }
138
+ /** A Components V2 container: a box around other components, with an optional accent colour. */
122
139
  export interface APIContainerComponent {
123
140
  type: typeof ComponentEnum.Container;
124
141
  components: APIComponent[];
125
142
  accent_color?: number;
126
143
  spoiler?: boolean;
127
144
  }
145
+ /** Any top-level message component: an action row or a Components V2 layout component. */
128
146
  export type APIComponent = APIActionRowComponent | APIButtonComponent | APIStringSelectComponent | APIEntitySelectComponent | APITextInputComponent | APISectionComponent | APITextDisplayComponent | APIThumbnailComponent | APIMediaGalleryComponent | APIFileComponent | APISeparatorComponent | APIContentInventoryEntryComponent | APIContainerComponent;
129
147
  export declare class CreateActionRow<T extends {
130
148
  toJSON(): APIActionRowChild;
@@ -225,6 +243,7 @@ export declare class CreateMediaGallery {
225
243
  addItems(...items: {
226
244
  url: string;
227
245
  description?: string;
246
+ spoiler?: boolean;
228
247
  }[]): this;
229
248
  toJSON(): APIMediaGalleryComponent;
230
249
  }
@@ -1,4 +1,26 @@
1
1
  // @bun
2
+ // packages/builders/src/clone.ts
3
+ function cloneData(value, depth = 0) {
4
+ if (value === null || typeof value !== "object")
5
+ return value;
6
+ if (depth > 64)
7
+ return structuredClone(value);
8
+ if (Array.isArray(value)) {
9
+ const out = new Array(value.length);
10
+ for (let i = 0;i < value.length; i++)
11
+ out[i] = cloneData(value[i], depth + 1);
12
+ return out;
13
+ }
14
+ const proto = Object.getPrototypeOf(value);
15
+ if (proto !== Object.prototype && proto !== null)
16
+ return structuredClone(value);
17
+ const from = value;
18
+ const out = {};
19
+ for (const key of Object.keys(from))
20
+ out[key] = cloneData(from[key], depth + 1);
21
+ return out;
22
+ }
23
+
2
24
  // packages/builders/src/embed.ts
3
25
  class CreateEmbed {
4
26
  #data = {};
@@ -94,7 +116,11 @@ class CreateEmbed {
94
116
  const current = [...this.#data.fields ?? []];
95
117
  if (!Number.isInteger(index) || !Number.isInteger(deleteCount) || index < 0 || deleteCount < 0)
96
118
  throw new RangeError("Field index and delete count must be non-negative integers.");
97
- current.splice(index, deleteCount, ...fields);
119
+ for (const field of fields) {
120
+ validate(field.name, 256, "Embed field name");
121
+ validate(field.value, 1024, "Embed field value");
122
+ }
123
+ current.splice(index, deleteCount, ...fields.map((f) => ({ ...f })));
98
124
  if (current.length > 25)
99
125
  throw new RangeError("An embed cannot contain more than 25 fields.");
100
126
  this.#data.fields = current;
@@ -120,7 +146,7 @@ class CreateEmbed {
120
146
  const total = (data.title?.length ?? 0) + (data.description?.length ?? 0) + (data.footer?.text.length ?? 0) + (data.author?.name.length ?? 0) + (data.fields ?? []).reduce((sum, field) => sum + field.name.length + field.value.length, 0);
121
147
  if (total > 6000)
122
148
  throw new RangeError(`An embed cannot contain more than 6000 characters in total (this one has ${total}).`);
123
- return structuredClone(data);
149
+ return cloneData(data);
124
150
  }
125
151
  }
126
152
  function validate(value, max, field) {
@@ -129,11 +155,15 @@ function validate(value, max, field) {
129
155
  return value;
130
156
  }
131
157
  function url(value, field) {
158
+ let parsed;
132
159
  try {
133
- return new URL(value).toString();
160
+ parsed = new URL(value);
134
161
  } catch (error) {
135
162
  throw new TypeError(`${field} must be a valid URL.`, { cause: error });
136
163
  }
164
+ if (!["http:", "https:", "attachment:"].includes(parsed.protocol))
165
+ throw new TypeError(`${field} must use http, https or attachment.`);
166
+ return parsed.toString();
137
167
  }
138
168
  // packages/types/src/channel-kind.ts
139
169
  var ChannelKinds = {
@@ -327,7 +357,7 @@ class CreateStringSelect extends CreateSelectMenu {
327
357
  throw new RangeError("A string select needs at least one option.");
328
358
  if ((this.#data.max_values ?? 1) > count)
329
359
  throw new RangeError("max_values cannot exceed the number of options.");
330
- return structuredClone(this.#data);
360
+ return cloneData(this.#data);
331
361
  }
332
362
  }
333
363
 
@@ -375,6 +405,8 @@ class CreateButton {
375
405
  cause: error
376
406
  });
377
407
  }
408
+ if (!["http:", "https:", "discord:"].includes(url.protocol))
409
+ throw new TypeError("Button URL must use http, https or discord.");
378
410
  this.setStyle(ButtonType.Link);
379
411
  this.#data.url = url.toString();
380
412
  return this;
@@ -413,7 +445,7 @@ class CreateButton {
413
445
  }
414
446
  if (data.style !== ButtonType.Premium && !data.label && !data.emoji)
415
447
  throw new TypeError("Buttons require a label or an emoji.");
416
- return structuredClone(data);
448
+ return cloneData(data);
417
449
  }
418
450
  }
419
451
 
@@ -432,7 +464,7 @@ class CreateEntitySelect extends CreateSelectMenu {
432
464
  }
433
465
  toJSON() {
434
466
  assertSelect(this.#data);
435
- return structuredClone(this.#data);
467
+ return cloneData(this.#data);
436
468
  }
437
469
  }
438
470
 
@@ -524,7 +556,7 @@ class CreateTextInput {
524
556
  throw new TypeError("Text inputs require a custom ID.");
525
557
  if (data.min_length !== undefined && data.max_length !== undefined && data.min_length > data.max_length)
526
558
  throw new RangeError("min_length cannot exceed max_length.");
527
- return structuredClone(data);
559
+ return cloneData(data);
528
560
  }
529
561
  }
530
562
 
@@ -608,13 +640,17 @@ class CreateMediaGallery {
608
640
  if (this.#items.length + items.length > 10) {
609
641
  throw new RangeError("MediaGallery can only contain up to 10 items.");
610
642
  }
611
- this.#items.push(...items.map((item) => ({ media: item })));
643
+ this.#items.push(...items.map(({ url, description, spoiler }) => ({
644
+ media: { url },
645
+ ...description !== undefined ? { description } : {},
646
+ ...spoiler !== undefined ? { spoiler } : {}
647
+ })));
612
648
  return this;
613
649
  }
614
650
  toJSON() {
615
651
  return {
616
652
  type: ComponentEnum.MediaGallery,
617
- items: structuredClone(this.#items)
653
+ items: cloneData(this.#items)
618
654
  };
619
655
  }
620
656
  }
@@ -688,7 +724,7 @@ class CreateThumbnail {
688
724
  toJSON() {
689
725
  const data = {
690
726
  type: ComponentEnum.Thumbnail,
691
- url: this.#url
727
+ media: { url: this.#url }
692
728
  };
693
729
  if (this.#description !== undefined)
694
730
  data.description = this.#description;
@@ -853,7 +889,7 @@ class CreateSlashCommand {
853
889
  return this.addOption(configure(new CreateSubcommandGroup));
854
890
  }
855
891
  toJSON() {
856
- return structuredClone(this.#data);
892
+ return cloneData(this.#data);
857
893
  }
858
894
  addOption(option) {
859
895
  this.#data.options = appendOption(this.#data.options, option.toJSON(), "application command");
@@ -881,11 +917,13 @@ class CreateCommandOption {
881
917
  return this;
882
918
  }
883
919
  setAutocomplete(enabled = true) {
920
+ if (enabled && Array.isArray(this.data.choices) && this.data.choices.length)
921
+ throw new RangeError("Autocomplete options cannot define choices.");
884
922
  this.data.autocomplete = enabled;
885
923
  return this;
886
924
  }
887
925
  toJSON() {
888
- return structuredClone(this.data);
926
+ return cloneData(this.data);
889
927
  }
890
928
  }
891
929
 
@@ -921,6 +959,9 @@ class CreateNumericOption extends CreateCommandOption {
921
959
  }
922
960
  setMinValue(value) {
923
961
  validateNumberRange(value, -MAX_SAFE, MAX_SAFE, "min_value", this.#integer);
962
+ const max = this.data.max_value;
963
+ if (max !== undefined && value > max)
964
+ throw new RangeError("min_value cannot exceed max_value.");
924
965
  this.data.min_value = value;
925
966
  return this;
926
967
  }
@@ -1075,8 +1116,12 @@ function appendChoices(data, choices) {
1075
1116
  throw new RangeError("Autocomplete options cannot define choices.");
1076
1117
  if (current.length + choices.length > 25)
1077
1118
  throw new RangeError("An option cannot contain more than 25 choices.");
1078
- for (const choice of choices)
1119
+ for (const choice of choices) {
1079
1120
  validateText2(choice.name, "Choice name", 100);
1121
+ const value = choice.value;
1122
+ if (typeof value === "string" ? value.length === 0 || value.length > 100 : typeof value !== "number" || !Number.isFinite(value))
1123
+ throw new TypeError("Choice value must be a string of 1-100 characters or a finite number.");
1124
+ }
1080
1125
  data.choices = [...current, ...choices.map((choice) => ({ ...choice }))];
1081
1126
  }
1082
1127
  function validateName(value, field) {
@@ -1131,7 +1176,7 @@ class CreateContextMenuCommand {
1131
1176
  return this;
1132
1177
  }
1133
1178
  toJSON() {
1134
- return structuredClone(this.data);
1179
+ return cloneData(this.data);
1135
1180
  }
1136
1181
  }
1137
1182
 
@@ -1198,12 +1243,14 @@ class CreateAttachment {
1198
1243
  }
1199
1244
  }
1200
1245
  async function insideRoot(root, file) {
1201
- const base = await realpath(root);
1246
+ const base = await realpath(root).catch(() => {
1247
+ throw new Error("Attachment root folder was not found.");
1248
+ });
1202
1249
  const target = await realpath(resolve(base, file)).catch(() => {
1203
1250
  throw new Error("Attachment file was not found inside its root.");
1204
1251
  });
1205
1252
  const rel = relative(base, target);
1206
- if (rel === "" || rel.startsWith("..") || isAbsolute(rel))
1253
+ if (rel === "" || rel === ".." || rel.startsWith("../") || rel.startsWith("..\\") || isAbsolute(rel))
1207
1254
  throw new Error("Attachment file is outside its root folder.");
1208
1255
  return target;
1209
1256
  }
@@ -1252,4 +1299,4 @@ export {
1252
1299
  componentsV2Message
1253
1300
  };
1254
1301
 
1255
- //# debugId=9EB45384514981FA64756E2164756E21
1302
+ //# debugId=53A00A51A367F67A64756E2164756E21