privage.js 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +503 -0
  3. package/dist/Client.d.ts +192 -0
  4. package/dist/Client.js +904 -0
  5. package/dist/attachments.d.ts +41 -0
  6. package/dist/attachments.js +63 -0
  7. package/dist/collectors.d.ts +41 -0
  8. package/dist/collectors.js +52 -0
  9. package/dist/commands.d.ts +108 -0
  10. package/dist/commands.js +216 -0
  11. package/dist/compat.d.ts +72 -0
  12. package/dist/compat.js +75 -0
  13. package/dist/components.d.ts +137 -0
  14. package/dist/components.js +226 -0
  15. package/dist/embeds.d.ts +97 -0
  16. package/dist/embeds.js +125 -0
  17. package/dist/errors.d.ts +32 -0
  18. package/dist/errors.js +48 -0
  19. package/dist/formatters.d.ts +16 -0
  20. package/dist/formatters.js +43 -0
  21. package/dist/index.d.ts +33 -0
  22. package/dist/index.js +101 -0
  23. package/dist/intents.d.ts +28 -0
  24. package/dist/intents.js +33 -0
  25. package/dist/modals.d.ts +96 -0
  26. package/dist/modals.js +162 -0
  27. package/dist/rest.d.ts +208 -0
  28. package/dist/rest.js +356 -0
  29. package/dist/structures/Channel.d.ts +25 -0
  30. package/dist/structures/Channel.js +34 -0
  31. package/dist/structures/Interaction.d.ts +149 -0
  32. package/dist/structures/Interaction.js +170 -0
  33. package/dist/structures/Member.d.ts +35 -0
  34. package/dist/structures/Member.js +55 -0
  35. package/dist/structures/Message.d.ts +181 -0
  36. package/dist/structures/Message.js +191 -0
  37. package/dist/structures/Role.d.ts +17 -0
  38. package/dist/structures/Role.js +20 -0
  39. package/dist/structures/Server.d.ts +29 -0
  40. package/dist/structures/Server.js +36 -0
  41. package/dist/structures/ServerChannels.d.ts +24 -0
  42. package/dist/structures/ServerChannels.js +50 -0
  43. package/dist/types.d.ts +195 -0
  44. package/dist/types.js +2 -0
  45. package/package.json +49 -0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Message components (buttons + select menus) + fluent builders. Bot-only,
3
+ * like embeds — but the server REJECTS invalid components (400) instead of
4
+ * dropping them, and these builders throw the same complaints at build time.
5
+ *
6
+ * Row grammar (interactions v2): a row holds EITHER ≤5 buttons OR exactly
7
+ * one select. Wire shape:
8
+ * components: [
9
+ * { type: 'row', components: [
10
+ * { type: 'button', style: 'primary', label: 'Claim', custom_id: 'ticket:claim:42' },
11
+ * { type: 'button', style: 'link', label: 'Logs', url: 'https://...' },
12
+ * ]},
13
+ * { type: 'row', components: [
14
+ * { type: 'select', custom_id: 'ticket:severity', placeholder: 'Severity',
15
+ * options: [{ label: 'Low', value: 'low' }] },
16
+ * ]},
17
+ * ]
18
+ */
19
+ export type ButtonStyle = 'primary' | 'secondary' | 'success' | 'danger' | 'link';
20
+ /** discord.js-style constants for {@link ButtonStyle} — `setStyle(ButtonStyle.Primary)` ports verbatim. */
21
+ export declare const ButtonStyle: {
22
+ readonly Primary: "primary";
23
+ readonly Secondary: "secondary";
24
+ readonly Success: "success";
25
+ readonly Danger: "danger";
26
+ readonly Link: "link";
27
+ };
28
+ /** Server-enforced component limits (mirrored client-side). */
29
+ export declare const ComponentLimits: {
30
+ readonly RowsPerMessage: 5;
31
+ readonly ButtonsPerRow: 5;
32
+ /** Interactive components per message (a select counts as 1). */
33
+ readonly ButtonsPerMessage: 25;
34
+ readonly Label: 80;
35
+ readonly CustomId: 100;
36
+ readonly Emoji: 32;
37
+ readonly SelectPlaceholder: 150;
38
+ /** Options per select menu. */
39
+ readonly SelectOptions: 25;
40
+ /** Ceiling for `min_values`/`max_values`. */
41
+ readonly SelectValues: 25;
42
+ readonly OptionLabel: 100;
43
+ readonly OptionValue: 100;
44
+ readonly OptionDescription: 100;
45
+ readonly OptionEmoji: 8;
46
+ };
47
+ export interface Button {
48
+ type: 'button';
49
+ style: ButtonStyle;
50
+ /** ≤80 chars. Optional only for emoji-only buttons. */
51
+ label?: string;
52
+ /** Unicode emoji shown before the label (≤32 chars). */
53
+ emoji?: string;
54
+ /** Required for non-link styles (≤100 chars, unique per message). Round-trips in the interaction event. */
55
+ custom_id?: string;
56
+ /** Required for (and exclusive to) link-style buttons. */
57
+ url?: string;
58
+ disabled?: boolean;
59
+ }
60
+ /** One option in a select menu. */
61
+ export interface SelectOption {
62
+ /** ≤100 chars. */
63
+ label: string;
64
+ /** ≤100 chars, unique within the select. Round-trips in `interaction.values`. */
65
+ value: string;
66
+ /** Muted second line under the label (≤100 chars). */
67
+ description?: string;
68
+ /** Unicode emoji shown before the label (≤8 chars). */
69
+ emoji?: string;
70
+ /** Pre-selected when the menu renders. */
71
+ default?: boolean;
72
+ }
73
+ export interface SelectMenu {
74
+ type: 'select';
75
+ /** ≤100 chars, unique per message across ALL components. */
76
+ custom_id: string;
77
+ /** ≤150 chars. */
78
+ placeholder?: string;
79
+ /** 0–25 (server default 1). */
80
+ min_values?: number;
81
+ /** 1–25, ≥ min_values (server default 1). */
82
+ max_values?: number;
83
+ disabled?: boolean;
84
+ /** 1–25 options. */
85
+ options: SelectOption[];
86
+ }
87
+ export interface ActionRow {
88
+ type: 'row';
89
+ /** Either ≤5 buttons or exactly one select — never both. */
90
+ components: (Button | SelectMenu)[];
91
+ }
92
+ export declare class ButtonBuilder {
93
+ private readonly data;
94
+ setStyle(style: ButtonStyle): this;
95
+ /** ≤80 chars. */
96
+ setLabel(label: string): this;
97
+ /** Unicode emoji (≤32 chars) — allows emoji-only buttons with no label. */
98
+ setEmoji(emoji: string): this;
99
+ /** Your opaque click identifier (≤100 chars) — comes back on the `interaction` event. Non-link styles only. */
100
+ setCustomId(customId: string): this;
101
+ /** Link-style buttons only — opens the URL, no interaction event. */
102
+ setURL(url: string): this;
103
+ setDisabled(disabled?: boolean): this;
104
+ toJSON(): Button;
105
+ }
106
+ /**
107
+ * Fluent select-menu builder. Mirrors the server's select grammar — throws
108
+ * at build time where the server would 400.
109
+ */
110
+ export declare class SelectMenuBuilder {
111
+ private readonly data;
112
+ private readonly options;
113
+ /** Your opaque identifier (≤100 chars, unique per message) — comes back on the `interaction` event. */
114
+ setCustomId(customId: string): this;
115
+ /** Hint text shown while nothing is selected (≤150 chars). */
116
+ setPlaceholder(placeholder: string): this;
117
+ /** Minimum selections (0–25; server default 1). */
118
+ setMinValues(min: number): this;
119
+ /** Maximum selections (1–25, ≥ min_values; server default 1). */
120
+ setMaxValues(max: number): this;
121
+ setDisabled(disabled?: boolean): this;
122
+ /** Append options (≤25 total; label/value ≤100, values unique). */
123
+ addOptions(...options: SelectOption[]): this;
124
+ toJSON(): SelectMenu;
125
+ }
126
+ /**
127
+ * A row holds EITHER up to 5 buttons OR exactly one select — the builder
128
+ * enforces the same exclusivity the server does.
129
+ */
130
+ export declare class ActionRowBuilder {
131
+ private readonly buttons;
132
+ private select;
133
+ addButtons(...buttons: (Button | ButtonBuilder)[]): this;
134
+ /** Put a select menu in the row — it must be the row's only component. */
135
+ addSelect(select: SelectMenu | SelectMenuBuilder): this;
136
+ toJSON(): ActionRow;
137
+ }
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ /**
3
+ * Message components (buttons + select menus) + fluent builders. Bot-only,
4
+ * like embeds — but the server REJECTS invalid components (400) instead of
5
+ * dropping them, and these builders throw the same complaints at build time.
6
+ *
7
+ * Row grammar (interactions v2): a row holds EITHER ≤5 buttons OR exactly
8
+ * one select. Wire shape:
9
+ * components: [
10
+ * { type: 'row', components: [
11
+ * { type: 'button', style: 'primary', label: 'Claim', custom_id: 'ticket:claim:42' },
12
+ * { type: 'button', style: 'link', label: 'Logs', url: 'https://...' },
13
+ * ]},
14
+ * { type: 'row', components: [
15
+ * { type: 'select', custom_id: 'ticket:severity', placeholder: 'Severity',
16
+ * options: [{ label: 'Low', value: 'low' }] },
17
+ * ]},
18
+ * ]
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.ActionRowBuilder = exports.SelectMenuBuilder = exports.ButtonBuilder = exports.ComponentLimits = exports.ButtonStyle = void 0;
22
+ /** discord.js-style constants for {@link ButtonStyle} — `setStyle(ButtonStyle.Primary)` ports verbatim. */
23
+ exports.ButtonStyle = {
24
+ Primary: 'primary',
25
+ Secondary: 'secondary',
26
+ Success: 'success',
27
+ Danger: 'danger',
28
+ Link: 'link',
29
+ };
30
+ /** Server-enforced component limits (mirrored client-side). */
31
+ exports.ComponentLimits = {
32
+ RowsPerMessage: 5,
33
+ ButtonsPerRow: 5,
34
+ /** Interactive components per message (a select counts as 1). */
35
+ ButtonsPerMessage: 25,
36
+ Label: 80,
37
+ CustomId: 100,
38
+ Emoji: 32,
39
+ SelectPlaceholder: 150,
40
+ /** Options per select menu. */
41
+ SelectOptions: 25,
42
+ /** Ceiling for `min_values`/`max_values`. */
43
+ SelectValues: 25,
44
+ OptionLabel: 100,
45
+ OptionValue: 100,
46
+ OptionDescription: 100,
47
+ OptionEmoji: 8,
48
+ };
49
+ function assertLength(what, value, max, kind = 'Button') {
50
+ if (value.length === 0 || value.length > max) {
51
+ throw new RangeError(`${kind} ${what} must be 1–${max} characters (got ${value.length}).`);
52
+ }
53
+ return value;
54
+ }
55
+ class ButtonBuilder {
56
+ constructor() {
57
+ this.data = { type: 'button' };
58
+ }
59
+ setStyle(style) {
60
+ this.data.style = style;
61
+ return this;
62
+ }
63
+ /** ≤80 chars. */
64
+ setLabel(label) {
65
+ this.data.label = assertLength('label', label, exports.ComponentLimits.Label);
66
+ return this;
67
+ }
68
+ /** Unicode emoji (≤32 chars) — allows emoji-only buttons with no label. */
69
+ setEmoji(emoji) {
70
+ this.data.emoji = assertLength('emoji', emoji, exports.ComponentLimits.Emoji);
71
+ return this;
72
+ }
73
+ /** Your opaque click identifier (≤100 chars) — comes back on the `interaction` event. Non-link styles only. */
74
+ setCustomId(customId) {
75
+ this.data.custom_id = assertLength('custom_id', customId, exports.ComponentLimits.CustomId);
76
+ return this;
77
+ }
78
+ /** Link-style buttons only — opens the URL, no interaction event. */
79
+ setURL(url) {
80
+ this.data.url = url;
81
+ return this;
82
+ }
83
+ setDisabled(disabled = true) {
84
+ this.data.disabled = disabled;
85
+ return this;
86
+ }
87
+ toJSON() {
88
+ const d = this.data;
89
+ if (!d.style)
90
+ throw new RangeError('Button style is required — call setStyle().');
91
+ if (d.label === undefined && d.emoji === undefined) {
92
+ throw new RangeError('Buttons need a label (or an emoji for emoji-only buttons).');
93
+ }
94
+ if (d.style === 'link') {
95
+ if (d.custom_id !== undefined)
96
+ throw new RangeError('Link buttons must not carry a custom_id.');
97
+ if (!d.url)
98
+ throw new RangeError('Link buttons require a URL — call setURL().');
99
+ }
100
+ else {
101
+ if (d.url !== undefined)
102
+ throw new RangeError('Only link buttons may carry a URL.');
103
+ if (!d.custom_id)
104
+ throw new RangeError('Non-link buttons require a custom_id — call setCustomId().');
105
+ }
106
+ return { ...d };
107
+ }
108
+ }
109
+ exports.ButtonBuilder = ButtonBuilder;
110
+ /**
111
+ * Fluent select-menu builder. Mirrors the server's select grammar — throws
112
+ * at build time where the server would 400.
113
+ */
114
+ class SelectMenuBuilder {
115
+ constructor() {
116
+ this.data = { type: 'select' };
117
+ this.options = [];
118
+ }
119
+ /** Your opaque identifier (≤100 chars, unique per message) — comes back on the `interaction` event. */
120
+ setCustomId(customId) {
121
+ this.data.custom_id = assertLength('custom_id', customId, exports.ComponentLimits.CustomId, 'Select');
122
+ return this;
123
+ }
124
+ /** Hint text shown while nothing is selected (≤150 chars). */
125
+ setPlaceholder(placeholder) {
126
+ this.data.placeholder = assertLength('placeholder', placeholder, exports.ComponentLimits.SelectPlaceholder, 'Select');
127
+ return this;
128
+ }
129
+ /** Minimum selections (0–25; server default 1). */
130
+ setMinValues(min) {
131
+ if (!Number.isInteger(min) || min < 0 || min > exports.ComponentLimits.SelectValues) {
132
+ throw new RangeError(`Select min_values must be an integer between 0 and ${exports.ComponentLimits.SelectValues} (got ${min}).`);
133
+ }
134
+ this.data.min_values = min;
135
+ return this;
136
+ }
137
+ /** Maximum selections (1–25, ≥ min_values; server default 1). */
138
+ setMaxValues(max) {
139
+ if (!Number.isInteger(max) || max < 1 || max > exports.ComponentLimits.SelectValues) {
140
+ throw new RangeError(`Select max_values must be an integer between 1 and ${exports.ComponentLimits.SelectValues} (got ${max}).`);
141
+ }
142
+ this.data.max_values = max;
143
+ return this;
144
+ }
145
+ setDisabled(disabled = true) {
146
+ this.data.disabled = disabled;
147
+ return this;
148
+ }
149
+ /** Append options (≤25 total; label/value ≤100, values unique). */
150
+ addOptions(...options) {
151
+ for (const option of options) {
152
+ if (this.options.length >= exports.ComponentLimits.SelectOptions) {
153
+ throw new RangeError(`Selects hold at most ${exports.ComponentLimits.SelectOptions} options.`);
154
+ }
155
+ assertLength('option label', option.label, exports.ComponentLimits.OptionLabel, 'Select');
156
+ assertLength('option value', option.value, exports.ComponentLimits.OptionValue, 'Select');
157
+ if (option.description !== undefined)
158
+ assertLength('option description', option.description, exports.ComponentLimits.OptionDescription, 'Select');
159
+ if (option.emoji !== undefined)
160
+ assertLength('option emoji', option.emoji, exports.ComponentLimits.OptionEmoji, 'Select');
161
+ if (this.options.some((o) => o.value === option.value)) {
162
+ throw new RangeError(`Select option value "${option.value}" is duplicated — values must be unique within the select.`);
163
+ }
164
+ this.options.push({ ...option });
165
+ }
166
+ return this;
167
+ }
168
+ toJSON() {
169
+ const d = this.data;
170
+ if (!d.custom_id)
171
+ throw new RangeError('Selects require a custom_id — call setCustomId().');
172
+ if (this.options.length === 0)
173
+ throw new RangeError('Select has no options — add at least one with addOptions().');
174
+ const min = d.min_values ?? 1;
175
+ const max = d.max_values ?? 1;
176
+ if (max < min) {
177
+ throw new RangeError(`Select max_values (${max}) must be greater than or equal to min_values (${min}).`);
178
+ }
179
+ return { ...d, options: this.options.map((o) => ({ ...o })) };
180
+ }
181
+ }
182
+ exports.SelectMenuBuilder = SelectMenuBuilder;
183
+ /**
184
+ * A row holds EITHER up to 5 buttons OR exactly one select — the builder
185
+ * enforces the same exclusivity the server does.
186
+ */
187
+ class ActionRowBuilder {
188
+ constructor() {
189
+ this.buttons = [];
190
+ this.select = null;
191
+ }
192
+ addButtons(...buttons) {
193
+ if (this.select) {
194
+ throw new RangeError('A row holds either buttons or a single select — this row already has a select.');
195
+ }
196
+ for (const button of buttons) {
197
+ const resolved = button instanceof ButtonBuilder ? button.toJSON() : button;
198
+ if (this.buttons.length >= exports.ComponentLimits.ButtonsPerRow) {
199
+ throw new RangeError(`Action rows hold at most ${exports.ComponentLimits.ButtonsPerRow} buttons.`);
200
+ }
201
+ this.buttons.push(resolved);
202
+ }
203
+ return this;
204
+ }
205
+ /** Put a select menu in the row — it must be the row's only component. */
206
+ addSelect(select) {
207
+ if (this.buttons.length > 0) {
208
+ throw new RangeError('A row holds either buttons or a single select — this row already has buttons.');
209
+ }
210
+ if (this.select) {
211
+ throw new RangeError('A row holds exactly one select.');
212
+ }
213
+ this.select = select instanceof SelectMenuBuilder ? select.toJSON() : select;
214
+ return this;
215
+ }
216
+ toJSON() {
217
+ if (this.select) {
218
+ return { type: 'row', components: [{ ...this.select, options: this.select.options.map((o) => ({ ...o })) }] };
219
+ }
220
+ if (this.buttons.length === 0) {
221
+ throw new RangeError('Action row is empty — add at least one button or a select.');
222
+ }
223
+ return { type: 'row', components: this.buttons.map((b) => ({ ...b })) };
224
+ }
225
+ }
226
+ exports.ActionRowBuilder = ActionRowBuilder;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Typed embeds + a fluent EmbedBuilder (discord.js-style). Embeds are
3
+ * bot-only. The wire shape below is exactly what the backend's embed
4
+ * validator accepts; limits mirror the server's, but the builder throws at
5
+ * build time where the server would silently truncate or drop.
6
+ */
7
+ /** Server-enforced embed limits (mirrored client-side; see embedValidator). */
8
+ export declare const EmbedLimits: {
9
+ /** Max embeds per message. */
10
+ readonly EmbedsPerMessage: 10;
11
+ readonly Title: 256;
12
+ readonly Description: 4096;
13
+ readonly AuthorName: 256;
14
+ readonly FieldName: 256;
15
+ readonly FieldValue: 1024;
16
+ readonly Fields: 25;
17
+ readonly FooterText: 2048;
18
+ };
19
+ export interface EmbedField {
20
+ name: string;
21
+ value: string;
22
+ inline?: boolean;
23
+ }
24
+ export interface EmbedAuthor {
25
+ name: string;
26
+ /** Small icon next to the author name. */
27
+ icon_url?: string;
28
+ url?: string;
29
+ }
30
+ export interface EmbedFooter {
31
+ text: string;
32
+ icon_url?: string;
33
+ }
34
+ /** The wire embed shape the backend accepts. */
35
+ export interface Embed {
36
+ title?: string;
37
+ description?: string;
38
+ url?: string;
39
+ /** `0xRRGGBB` number or a hex string like `'#35b9e8'` — both accepted. */
40
+ color?: number | string;
41
+ author?: EmbedAuthor;
42
+ footer?: EmbedFooter;
43
+ fields?: EmbedField[];
44
+ image?: {
45
+ url: string;
46
+ };
47
+ thumbnail?: {
48
+ url: string;
49
+ };
50
+ /** ISO timestamp shown in the footer area. */
51
+ timestamp?: string;
52
+ }
53
+ /**
54
+ * Fluent embed builder. `JSON.stringify` picks up `toJSON()` automatically,
55
+ * so builders can be passed straight into `embeds: [...]` without unwrapping.
56
+ *
57
+ * Note: the client renders a single image slot — if both are set,
58
+ * `thumbnail` wins over `image`.
59
+ */
60
+ export declare class EmbedBuilder {
61
+ private readonly data;
62
+ /** ≤256 chars. */
63
+ setTitle(title: string): this;
64
+ /** ≤4096 chars. */
65
+ setDescription(description: string): this;
66
+ /** Makes the title a link. */
67
+ setURL(url: string): this;
68
+ /** `0xRRGGBB` number (e.g. `0x35b9e8`), hex string (`'#35b9e8'`), or `[r, g, b]` tuple (discord.js-style). */
69
+ setColor(color: number | string | [number, number, number]): this;
70
+ /** Author line above the title. `name` ≤256 chars. (`iconURL` accepted as a discord.js-style alias.) */
71
+ setAuthor(author: {
72
+ name: string;
73
+ iconUrl?: string;
74
+ iconURL?: string;
75
+ url?: string;
76
+ }): this;
77
+ /** Footer line. `text` ≤2048 chars. (`iconURL` accepted as a discord.js-style alias.) */
78
+ setFooter(footer: {
79
+ text: string;
80
+ iconUrl?: string;
81
+ iconURL?: string;
82
+ }): this;
83
+ /** Append fields (≤25 total; name ≤256, value ≤1024 each). Accepts spread or a single array (discord.js-style). */
84
+ addFields(...fields: (EmbedField | EmbedField[])[]): this;
85
+ /** Replace all fields. Accepts spread or a single array. */
86
+ setFields(...fields: (EmbedField | EmbedField[])[]): this;
87
+ setImage(url: string): this;
88
+ /** Renders in the same single image slot; wins over `setImage`. */
89
+ setThumbnail(url: string): this;
90
+ /** Defaults to now. */
91
+ setTimestamp(timestamp?: Date | number | string): this;
92
+ /**
93
+ * The plain wire object. Throws if the embed is empty (the server would
94
+ * silently drop it — fail loud instead).
95
+ */
96
+ toJSON(): Embed;
97
+ }
package/dist/embeds.js ADDED
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ /**
3
+ * Typed embeds + a fluent EmbedBuilder (discord.js-style). Embeds are
4
+ * bot-only. The wire shape below is exactly what the backend's embed
5
+ * validator accepts; limits mirror the server's, but the builder throws at
6
+ * build time where the server would silently truncate or drop.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.EmbedBuilder = exports.EmbedLimits = void 0;
10
+ /** Server-enforced embed limits (mirrored client-side; see embedValidator). */
11
+ exports.EmbedLimits = {
12
+ /** Max embeds per message. */
13
+ EmbedsPerMessage: 10,
14
+ Title: 256,
15
+ Description: 4096,
16
+ AuthorName: 256,
17
+ FieldName: 256,
18
+ FieldValue: 1024,
19
+ Fields: 25,
20
+ FooterText: 2048,
21
+ };
22
+ function assertLength(what, value, max) {
23
+ if (value.length > max) {
24
+ throw new RangeError(`Embed ${what} exceeds ${max} characters (got ${value.length}).`);
25
+ }
26
+ return value;
27
+ }
28
+ /**
29
+ * Fluent embed builder. `JSON.stringify` picks up `toJSON()` automatically,
30
+ * so builders can be passed straight into `embeds: [...]` without unwrapping.
31
+ *
32
+ * Note: the client renders a single image slot — if both are set,
33
+ * `thumbnail` wins over `image`.
34
+ */
35
+ class EmbedBuilder {
36
+ constructor() {
37
+ this.data = {};
38
+ }
39
+ /** ≤256 chars. */
40
+ setTitle(title) {
41
+ this.data.title = assertLength('title', title, exports.EmbedLimits.Title);
42
+ return this;
43
+ }
44
+ /** ≤4096 chars. */
45
+ setDescription(description) {
46
+ this.data.description = assertLength('description', description, exports.EmbedLimits.Description);
47
+ return this;
48
+ }
49
+ /** Makes the title a link. */
50
+ setURL(url) {
51
+ this.data.url = url;
52
+ return this;
53
+ }
54
+ /** `0xRRGGBB` number (e.g. `0x35b9e8`), hex string (`'#35b9e8'`), or `[r, g, b]` tuple (discord.js-style). */
55
+ setColor(color) {
56
+ this.data.color = Array.isArray(color)
57
+ ? ((color[0] & 0xff) << 16) | ((color[1] & 0xff) << 8) | (color[2] & 0xff)
58
+ : color;
59
+ return this;
60
+ }
61
+ /** Author line above the title. `name` ≤256 chars. (`iconURL` accepted as a discord.js-style alias.) */
62
+ setAuthor(author) {
63
+ const icon = author.iconUrl ?? author.iconURL;
64
+ this.data.author = {
65
+ name: assertLength('author name', author.name, exports.EmbedLimits.AuthorName),
66
+ ...(icon ? { icon_url: icon } : {}),
67
+ ...(author.url ? { url: author.url } : {}),
68
+ };
69
+ return this;
70
+ }
71
+ /** Footer line. `text` ≤2048 chars. (`iconURL` accepted as a discord.js-style alias.) */
72
+ setFooter(footer) {
73
+ const icon = footer.iconUrl ?? footer.iconURL;
74
+ this.data.footer = {
75
+ text: assertLength('footer text', footer.text, exports.EmbedLimits.FooterText),
76
+ ...(icon ? { icon_url: icon } : {}),
77
+ };
78
+ return this;
79
+ }
80
+ /** Append fields (≤25 total; name ≤256, value ≤1024 each). Accepts spread or a single array (discord.js-style). */
81
+ addFields(...fields) {
82
+ const flat = fields.flat();
83
+ const next = [...(this.data.fields ?? []), ...flat];
84
+ if (next.length > exports.EmbedLimits.Fields) {
85
+ throw new RangeError(`Embeds support at most ${exports.EmbedLimits.Fields} fields (got ${next.length}).`);
86
+ }
87
+ this.data.fields = next.map((f) => ({
88
+ name: assertLength('field name', f.name, exports.EmbedLimits.FieldName),
89
+ value: assertLength('field value', f.value, exports.EmbedLimits.FieldValue),
90
+ inline: !!f.inline,
91
+ }));
92
+ return this;
93
+ }
94
+ /** Replace all fields. Accepts spread or a single array. */
95
+ setFields(...fields) {
96
+ this.data.fields = undefined;
97
+ return fields.length ? this.addFields(...fields) : this;
98
+ }
99
+ setImage(url) {
100
+ this.data.image = { url };
101
+ return this;
102
+ }
103
+ /** Renders in the same single image slot; wins over `setImage`. */
104
+ setThumbnail(url) {
105
+ this.data.thumbnail = { url };
106
+ return this;
107
+ }
108
+ /** Defaults to now. */
109
+ setTimestamp(timestamp = new Date()) {
110
+ this.data.timestamp = new Date(timestamp).toISOString();
111
+ return this;
112
+ }
113
+ /**
114
+ * The plain wire object. Throws if the embed is empty (the server would
115
+ * silently drop it — fail loud instead).
116
+ */
117
+ toJSON() {
118
+ const d = this.data;
119
+ if (!d.title && !d.description && !d.fields?.length && !d.image?.url && !d.thumbnail?.url) {
120
+ throw new RangeError('Embed is empty — set at least a title, description, field, or image before sending.');
121
+ }
122
+ return { ...d, ...(d.fields ? { fields: d.fields.map((f) => ({ ...f })) } : {}) };
123
+ }
124
+ }
125
+ exports.EmbedBuilder = EmbedBuilder;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Typed errors surfaced by the SDK. Bot authors branch on these instead of
3
+ * inspecting HTTP status codes. See the protocol spec §12 "Error taxonomy".
4
+ */
5
+ export declare class PrivageError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ /** 401/403 auth — bad or rotated token, or a non-bot account. Terminal: `login()` rejects and does not retry. */
9
+ export declare class PrivageAuthError extends PrivageError {
10
+ }
11
+ /** 403 on an action — missing channel permission, not a member, or the bot was kicked. Thrown from `reply()`/`send()`. */
12
+ export declare class PrivagePermissionError extends PrivageError {
13
+ }
14
+ /**
15
+ * Network-level failure. Two paths: emitted as `connectionError` for WS
16
+ * transport failures/heartbeat timeouts (recoverable — the SDK
17
+ * auto-reconnects), AND thrown from any REST-backed call whose fetch fails
18
+ * or times out (not retried — the caller decides).
19
+ */
20
+ export declare class PrivageConnectionError extends PrivageError {
21
+ }
22
+ /** 429 + slowmode + mention limits. `retryAfter` is milliseconds. */
23
+ export declare class PrivageRateLimitError extends PrivageError {
24
+ readonly retryAfter: number;
25
+ constructor(message: string, retryAfter: number);
26
+ }
27
+ /** Any other non-2xx REST response. */
28
+ export declare class PrivageHTTPError extends PrivageError {
29
+ readonly status: number;
30
+ readonly body: unknown;
31
+ constructor(message: string, status: number, body: unknown);
32
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /**
3
+ * Typed errors surfaced by the SDK. Bot authors branch on these instead of
4
+ * inspecting HTTP status codes. See the protocol spec §12 "Error taxonomy".
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.PrivageHTTPError = exports.PrivageRateLimitError = exports.PrivageConnectionError = exports.PrivagePermissionError = exports.PrivageAuthError = exports.PrivageError = void 0;
8
+ class PrivageError extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = new.target.name;
12
+ }
13
+ }
14
+ exports.PrivageError = PrivageError;
15
+ /** 401/403 auth — bad or rotated token, or a non-bot account. Terminal: `login()` rejects and does not retry. */
16
+ class PrivageAuthError extends PrivageError {
17
+ }
18
+ exports.PrivageAuthError = PrivageAuthError;
19
+ /** 403 on an action — missing channel permission, not a member, or the bot was kicked. Thrown from `reply()`/`send()`. */
20
+ class PrivagePermissionError extends PrivageError {
21
+ }
22
+ exports.PrivagePermissionError = PrivagePermissionError;
23
+ /**
24
+ * Network-level failure. Two paths: emitted as `connectionError` for WS
25
+ * transport failures/heartbeat timeouts (recoverable — the SDK
26
+ * auto-reconnects), AND thrown from any REST-backed call whose fetch fails
27
+ * or times out (not retried — the caller decides).
28
+ */
29
+ class PrivageConnectionError extends PrivageError {
30
+ }
31
+ exports.PrivageConnectionError = PrivageConnectionError;
32
+ /** 429 + slowmode + mention limits. `retryAfter` is milliseconds. */
33
+ class PrivageRateLimitError extends PrivageError {
34
+ constructor(message, retryAfter) {
35
+ super(message);
36
+ this.retryAfter = retryAfter;
37
+ }
38
+ }
39
+ exports.PrivageRateLimitError = PrivageRateLimitError;
40
+ /** Any other non-2xx REST response. */
41
+ class PrivageHTTPError extends PrivageError {
42
+ constructor(message, status, body) {
43
+ super(message);
44
+ this.status = status;
45
+ this.body = body;
46
+ }
47
+ }
48
+ exports.PrivageHTTPError = PrivageHTTPError;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Message formatting helpers. Mention syntax matches the wire format the
3
+ * backend parses and the client renders: `<@userId>`, `<@&roleId>`,
4
+ * `<#channelId>`, plus the `@everyone` / `@here` broadcasts (rate-limited
5
+ * server-side). The rest is standard markdown.
6
+ */
7
+ export declare function userMention(userId: string): string;
8
+ export declare function roleMention(roleId: string): string;
9
+ export declare function channelMention(channelId: string): string;
10
+ export declare const everyone = "@everyone";
11
+ export declare const here = "@here";
12
+ export declare function bold(text: string): string;
13
+ export declare function italic(text: string): string;
14
+ export declare function strikethrough(text: string): string;
15
+ export declare function inlineCode(text: string): string;
16
+ export declare function codeBlock(content: string, language?: string): string;