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.
- package/dist/builders/attachment.d.ts +1 -0
- package/dist/builders/clone.d.ts +10 -0
- package/dist/builders/commands.d.ts +1 -1
- package/dist/builders/components.d.ts +24 -5
- package/dist/builders/index.js +64 -17
- package/dist/builders/index.js.map +10 -9
- package/dist/collection/index.d.ts +1 -0
- package/dist/collection/index.js +297 -19
- package/dist/collection/index.js.map +5 -4
- package/dist/collection/redis.d.ts +98 -0
- package/dist/core/collector.d.ts +8 -0
- package/dist/core/events.d.ts +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +6242 -4733
- package/dist/core/index.js.map +47 -40
- package/dist/core/permissions.d.ts +3 -402
- package/dist/core/redis-cache.d.ts +85 -0
- package/dist/formatters/index.d.ts +5 -1
- package/dist/formatters/index.js +7 -3
- package/dist/formatters/index.js.map +3 -3
- package/dist/handlers/index.js +11 -2
- package/dist/handlers/index.js.map +3 -3
- package/dist/index.js +8688 -6136
- package/dist/index.js.map +67 -59
- package/dist/managers/application.d.ts +10 -6
- package/dist/managers/base.d.ts +11 -1
- package/dist/managers/guild.d.ts +3 -1
- package/dist/managers/index.d.ts +6 -1
- package/dist/managers/index.js +383 -109
- package/dist/managers/index.js.map +20 -23
- package/dist/managers/member.d.ts +3 -1
- package/dist/managers/message.d.ts +13 -4
- package/dist/managers/thread.d.ts +3 -3
- package/dist/rest/decoder.d.ts +2 -0
- package/dist/rest/errors.d.ts +5 -2
- package/dist/rest/headers.d.ts +31 -0
- package/dist/rest/identity.d.ts +31 -0
- package/dist/rest/index.d.ts +97 -6
- package/dist/rest/index.js +1355 -220
- package/dist/rest/index.js.map +16 -13
- package/dist/rest/limiter.d.ts +75 -10
- package/dist/rest/redis.d.ts +103 -22
- package/dist/rest/scheduler.d.ts +13 -0
- package/dist/rest/store.d.ts +135 -12
- package/dist/rest/telemetry.d.ts +151 -0
- package/dist/rest/webhook.d.ts +18 -6
- package/dist/sharding/bus.d.ts +4 -2
- package/dist/sharding/cluster.d.ts +29 -1
- package/dist/sharding/health.d.ts +1 -1
- package/dist/sharding/host.d.ts +13 -2
- package/dist/sharding/identify.d.ts +2 -0
- package/dist/sharding/index.d.ts +8 -0
- package/dist/sharding/index.js +579 -65
- package/dist/sharding/index.js.map +17 -15
- package/dist/sharding/leader.d.ts +34 -0
- package/dist/sharding/remote.d.ts +36 -2
- package/dist/sharding/supervisor.d.ts +1 -1
- package/dist/sharding/thread.d.ts +30 -0
- package/dist/sharding/transport.d.ts +6 -5
- package/dist/sharding/worker.d.ts +11 -1
- package/dist/structures/base.d.ts +16 -5
- package/dist/structures/index.d.ts +1 -1
- package/dist/structures/index.js +57 -91
- package/dist/structures/index.js.map +10 -18
- package/dist/structures/options.d.ts +3 -2
- package/dist/structures/permissions.d.ts +409 -0
- package/dist/structures/resources.d.ts +4 -1
- package/dist/types/gateway.d.ts +4 -0
- package/dist/types/index.d.ts +14 -4
- package/dist/types/index.js.map +3 -3
- package/dist/utils/args.d.ts +3 -1
- package/dist/utils/index.d.ts +6 -1
- package/dist/utils/index.js +17 -10
- package/dist/utils/index.js.map +4 -4
- package/dist/voice/index.d.ts +7 -1
- package/dist/voice/index.js +33 -10
- package/dist/voice/index.js.map +3 -3
- package/dist/ws/index.d.ts +2 -0
- package/dist/ws/index.js +32 -4
- package/dist/ws/index.js.map +5 -5
- package/package.json +21 -17
|
@@ -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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
}
|
package/dist/builders/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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((
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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("
|
|
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=
|
|
1302
|
+
//# debugId=53A00A51A367F67A64756E2164756E21
|