abseil 0.4.0 → 0.5.0-canary.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.
- package/README.md +34 -25
- package/dist/index.d.ts +93 -75
- package/dist/index.js +110 -146
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
# Abseil
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Validate and traverse Discord [Components V2](https://docs.discord.com/developers/components/overview) trees.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The `abseil` function validates that the input components array matches a schema. It returns the validated array and functions to interact with it.
|
|
6
6
|
|
|
7
|
-
| function
|
|
8
|
-
|
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
| next | Jump to the next neighbouring node of a type in an array |
|
|
14
|
-
| parent | Access the parent of a node |
|
|
15
|
-
| previous | Return to the previous node |
|
|
16
|
-
| sibling | Access the next node in an array |
|
|
17
|
-
| update | Shallow merge with the current component in place |
|
|
18
|
-
| value | The current component |
|
|
7
|
+
| function | description |
|
|
8
|
+
| -------- | -------------------------------------- |
|
|
9
|
+
| parent | Discover the parent node. |
|
|
10
|
+
| remove | Destroy a node. |
|
|
11
|
+
| search | Search the layout for a specific node. |
|
|
12
|
+
| sibling | Discover a neighbouring node. |
|
|
19
13
|
|
|
20
14
|
## Example
|
|
21
15
|
|
|
@@ -45,26 +39,41 @@ const message = {
|
|
|
45
39
|
</details>
|
|
46
40
|
|
|
47
41
|
```ts
|
|
48
|
-
import
|
|
42
|
+
import {
|
|
43
|
+
$ActionRow,
|
|
44
|
+
$Button,
|
|
45
|
+
$Container,
|
|
46
|
+
$Section,
|
|
47
|
+
$Separator,
|
|
48
|
+
$TextDisplay,
|
|
49
|
+
abseil,
|
|
50
|
+
} from "abseil";
|
|
49
51
|
import { editMessage } from "dressed";
|
|
50
52
|
|
|
51
|
-
const
|
|
53
|
+
const tree = abseil(message.components, [
|
|
54
|
+
$Container(
|
|
55
|
+
$Section([$TextDisplay], $Button).or($TextDisplay),
|
|
56
|
+
$TextDisplay,
|
|
57
|
+
$ActionRow($Button, $Button),
|
|
58
|
+
$Separator,
|
|
59
|
+
$Section([$TextDisplay], $Button),
|
|
60
|
+
),
|
|
61
|
+
]);
|
|
52
62
|
|
|
53
|
-
let guessBtn =
|
|
63
|
+
let guessBtn = tree.search(/guess-.+/, $Button);
|
|
54
64
|
|
|
55
65
|
while (guessBtn) {
|
|
56
|
-
guessBtn.
|
|
57
|
-
guessBtn =
|
|
66
|
+
guessBtn.disabled = true;
|
|
67
|
+
guessBtn = tree.sibling(guessBtn, $Button);
|
|
58
68
|
}
|
|
59
69
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
header.insertBefore(TextDisplay(header.child("TextDisplay").value.content));
|
|
63
|
-
removeNode(header);
|
|
70
|
+
const body = tree[0].components;
|
|
71
|
+
if ($Section.$.allows(body[0])) body[0] = TextDisplay(body[0].components[0].content);
|
|
64
72
|
|
|
65
73
|
// Insert-if-not-exists the warning message
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
const warning = "-# This has expired!";
|
|
75
|
+
if (!tree.search(warning, $TextDisplay)) {
|
|
76
|
+
body.push(TextDisplay(warning));
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
editMessage("<CHANNEL_ID>", "<MESSAGE_ID>", { components: message.components });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,82 +1,100 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { type Type, type } from "arktype";
|
|
2
|
+
import type { APIActionRowComponent, APIButtonComponentWithCustomId, APIButtonComponentWithSKUId, APIButtonComponentWithURL, APIChannelSelectComponent, APICheckboxComponent, APICheckboxGroupComponent, APIComponentInActionRow, APIComponentInMessageActionRow, APIContainerComponent, APIFileComponent, APIFileUploadComponent, APILabelComponent, APIMediaGalleryComponent, APIMentionableSelectComponent, APIMessageComponent, APIModalComponent, APIRadioGroupComponent, APIRoleSelectComponent, APISectionComponent, APISeparatorComponent, APIStringSelectComponent, APITextDisplayComponent, APITextInputComponent, APIThumbnailComponent, APIUserSelectComponent } from "discord-api-types/v10";
|
|
3
|
+
export declare const $Button: import("arktype/internal/variants/object.ts").ObjectType<APIButtonComponentWithCustomId, {}> & {
|
|
4
|
+
URL: Type<APIButtonComponentWithURL>;
|
|
5
|
+
SKU: Type<APIButtonComponentWithSKUId>;
|
|
6
6
|
};
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
declare const AnyButton: import("arktype/internal/variants/object.ts").ObjectType<APIButtonComponentWithCustomId | APIButtonComponentWithSKUId | APIButtonComponentWithURL, {}>;
|
|
8
|
+
export declare const $StringSelect: Type<APIStringSelectComponent>;
|
|
9
|
+
export declare const $UserSelect: Type<APIUserSelectComponent>;
|
|
10
|
+
export declare const $RoleSelect: Type<APIRoleSelectComponent>;
|
|
11
|
+
export declare const $MentionableSelect: Type<APIMentionableSelectComponent>;
|
|
12
|
+
export declare const $ChannelSelect: Type<APIChannelSelectComponent>;
|
|
13
|
+
declare const AnySelect: import("arktype/internal/variants/object.ts").ObjectType<APIChannelSelectComponent | APIMentionableSelectComponent | APIRoleSelectComponent | APIStringSelectComponent | APIUserSelectComponent, {}>;
|
|
14
|
+
export declare const $ActionRow: (<const T extends (typeof AnyButton)[] | [typeof AnySelect] | [typeof $TextInput]>(...components: T) => Type<Omit<APIActionRowComponent<APIComponentInActionRow>, "components"> & {
|
|
15
|
+
components: type.infer<T>;
|
|
16
|
+
}>) & {
|
|
17
|
+
$: Type<APIActionRowComponent<APIComponentInMessageActionRow>>;
|
|
9
18
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
type
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
type
|
|
23
|
-
|
|
24
|
-
type
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
} &
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
parent: <P extends Last<Pop<D>> extends never ? keyof ChildrenMap : Last<Pop<D>>[0]>(type: Arrable<P>) => Traverser<Last<Pop<D>> extends never ? [...(P extends "Container" ? [] : [never]), [P, keyof CT]] : Pop<D>, [
|
|
60
|
-
...H,
|
|
61
|
-
D
|
|
62
|
-
]>;
|
|
63
|
-
});
|
|
64
|
-
/** Initiate a tree traverser. */
|
|
65
|
-
export default function abseil<T extends APIMessageComponent>(components: T[]): {
|
|
19
|
+
export declare const $Thumbnail: Type<APIThumbnailComponent>;
|
|
20
|
+
export declare const $Section: (<const T extends (typeof $TextDisplay)[], const A extends Type<typeof AnyButton.infer | typeof $Thumbnail.infer>>(components: T, accessory: A) => Type<Omit<APISectionComponent, "accessory" | "components"> & {
|
|
21
|
+
accessory: type.infer<A>;
|
|
22
|
+
components: type.infer<T>;
|
|
23
|
+
}>) & {
|
|
24
|
+
$: Type<APISectionComponent>;
|
|
25
|
+
};
|
|
26
|
+
export declare const $TextDisplay: Type<APITextDisplayComponent>;
|
|
27
|
+
export declare const $MediaGallery: Type<APIMediaGalleryComponent>;
|
|
28
|
+
export declare const $File: Type<APIFileComponent>;
|
|
29
|
+
export declare const $Separator: Type<APISeparatorComponent>;
|
|
30
|
+
declare const ComponentInContainer: import("arktype/internal/variants/object.ts").ObjectType<APIFileComponent | APIMediaGalleryComponent | APISeparatorComponent | APITextDisplayComponent | {
|
|
31
|
+
type: 1;
|
|
32
|
+
} | {
|
|
33
|
+
type: 9;
|
|
34
|
+
}, {}>;
|
|
35
|
+
export declare const $Container: (<const T extends (typeof ComponentInContainer)[]>(...components: T) => Type<Omit<APIContainerComponent, "components"> & {
|
|
36
|
+
components: type.infer<T>;
|
|
37
|
+
}>) & {
|
|
38
|
+
$: Type<APIContainerComponent>;
|
|
39
|
+
};
|
|
40
|
+
export declare const $TextInput: Type<APITextInputComponent>;
|
|
41
|
+
export declare const $FileUpload: Type<APIFileUploadComponent>;
|
|
42
|
+
export declare const $RadioGroup: Type<APIRadioGroupComponent>;
|
|
43
|
+
export declare const $CheckboxGroup: Type<APICheckboxGroupComponent>;
|
|
44
|
+
export declare const $Checkbox: Type<APICheckboxComponent>;
|
|
45
|
+
declare const ComponentInLabel: import("arktype/internal/variants/object.ts").ObjectType<APIChannelSelectComponent | APIMentionableSelectComponent | APIRoleSelectComponent | APIStringSelectComponent | APIUserSelectComponent | APITextInputComponent | APICheckboxComponent | APICheckboxGroupComponent | APIFileUploadComponent | APIRadioGroupComponent, {}>;
|
|
46
|
+
export declare const $Label: (<const T extends typeof ComponentInLabel>(component: T) => Type<Omit<APILabelComponent, "component"> & {
|
|
47
|
+
component: type.infer<T>;
|
|
48
|
+
}>) & {
|
|
49
|
+
$: Type<APILabelComponent>;
|
|
50
|
+
};
|
|
51
|
+
type AnyComponent = APIMessageComponent | APIModalComponent;
|
|
52
|
+
export declare const $Any: import("arktype/internal/variants/object.ts").ObjectType<AnyComponent, {}> & {
|
|
53
|
+
ActionRow: import("arktype/internal/variants/object.ts").ObjectType<APIButtonComponentWithCustomId | APIButtonComponentWithSKUId | APIButtonComponentWithURL | APIChannelSelectComponent | APIMentionableSelectComponent | APIRoleSelectComponent | APIStringSelectComponent | APIUserSelectComponent, {}>;
|
|
54
|
+
Button: import("arktype/internal/variants/object.ts").ObjectType<APIButtonComponentWithCustomId | APIButtonComponentWithSKUId | APIButtonComponentWithURL, {}>;
|
|
55
|
+
Container: import("arktype/internal/variants/object.ts").ObjectType<APIFileComponent | APIMediaGalleryComponent | APISeparatorComponent | APITextDisplayComponent | {
|
|
56
|
+
type: 1;
|
|
57
|
+
} | {
|
|
58
|
+
type: 9;
|
|
59
|
+
}, {}>;
|
|
60
|
+
Label: import("arktype/internal/variants/object.ts").ObjectType<APIChannelSelectComponent | APIMentionableSelectComponent | APIRoleSelectComponent | APIStringSelectComponent | APIUserSelectComponent | APITextInputComponent | APICheckboxComponent | APICheckboxGroupComponent | APIFileUploadComponent | APIRadioGroupComponent, {}>;
|
|
61
|
+
Section: {
|
|
62
|
+
Component: import("arktype/internal/variants/object.ts").ObjectType<APITextDisplayComponent, {}>;
|
|
63
|
+
Accessory: import("arktype/internal/variants/object.ts").ObjectType<APIButtonComponentWithCustomId | APIButtonComponentWithSKUId | APIButtonComponentWithURL | APIThumbnailComponent, {}>;
|
|
64
|
+
};
|
|
65
|
+
Select: import("arktype/internal/variants/object.ts").ObjectType<APIChannelSelectComponent | APIMentionableSelectComponent | APIRoleSelectComponent | APIStringSelectComponent | APIUserSelectComponent, {}>;
|
|
66
|
+
};
|
|
67
|
+
interface LayoutFns {
|
|
66
68
|
/**
|
|
67
|
-
*
|
|
68
|
-
* @param
|
|
69
|
+
* Discover the parent node.
|
|
70
|
+
* @param node Node to jump from
|
|
71
|
+
* @param $component Schema the found component must match
|
|
69
72
|
*/
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
parent: <C extends Type<AnyComponent>>(node: AnyComponent, $component: C) => C["infer"] | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Destroy a node.
|
|
76
|
+
* @param node Node to remove
|
|
77
|
+
*/
|
|
78
|
+
remove: (node: AnyComponent | undefined) => void;
|
|
79
|
+
/**
|
|
80
|
+
* Search the layout for a specific node.
|
|
81
|
+
* @param query Substring of a TextDisplay content, custom_id, or the component `id`
|
|
82
|
+
* @param $component Schema the found component must match
|
|
83
|
+
*/
|
|
84
|
+
search: <Q extends number | string | RegExp, C extends Q extends number ? Type<AnyComponent> : typeof AnyButton | typeof ComponentInLabel | typeof $TextDisplay>(query: Q, $component: C) => C["infer"] | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* Discover a neighbouring node.
|
|
87
|
+
* @param node Node to offset from
|
|
88
|
+
* @param $component Schema the found component must match
|
|
89
|
+
* @param offset Difference from {@link node} (defaults to `1`)
|
|
90
|
+
*/
|
|
91
|
+
sibling: <C extends Type<AnyComponent>>(node: AnyComponent, $component: C, offset?: number) => C["infer"] | undefined;
|
|
92
|
+
}
|
|
74
93
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* @important This may cause issues when you try to navigate {@link abseil} instances you've previously created
|
|
94
|
+
* Validates that {@link components} match the {@link schema} and creates traversal functions
|
|
95
|
+
* @returns Validated input components and functions for interacting with the layout
|
|
78
96
|
*/
|
|
79
|
-
export
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
export default function abseil<const S extends Type<{
|
|
98
|
+
type: number;
|
|
99
|
+
}>[]>(components: unknown[], schema: S): type.infer<S> & LayoutFns;
|
|
82
100
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,168 +1,132 @@
|
|
|
1
|
-
|
|
2
|
-
import { ComponentType
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
import { type } from "arktype";
|
|
2
|
+
import { ComponentType } from "discord-api-types/v10";
|
|
3
|
+
const BaseButton = type({ type: `${ComponentType.Button}` });
|
|
4
|
+
export const $Button = Object.assign(BaseButton.and({ custom_id: "string" }), {
|
|
5
|
+
URL: BaseButton.and({ url: "string" }),
|
|
6
|
+
SKU: BaseButton.and({ sku_id: "string" }),
|
|
7
|
+
});
|
|
8
|
+
const AnyButton = $Button.or($Button.URL).or($Button.SKU);
|
|
9
|
+
export const $StringSelect = type({ type: `${ComponentType.StringSelect}` });
|
|
10
|
+
export const $UserSelect = type({ type: `${ComponentType.UserSelect}` });
|
|
11
|
+
export const $RoleSelect = type({ type: `${ComponentType.RoleSelect}` });
|
|
12
|
+
export const $MentionableSelect = type({
|
|
13
|
+
type: `${ComponentType.MentionableSelect}`,
|
|
14
|
+
});
|
|
15
|
+
export const $ChannelSelect = type({ type: `${ComponentType.ChannelSelect}` });
|
|
16
|
+
const AnySelect = $StringSelect.or($UserSelect).or($RoleSelect).or($MentionableSelect).or($ChannelSelect);
|
|
17
|
+
export const $ActionRow = Object.assign((...components) =>
|
|
18
|
+
// @ts-expect-error
|
|
19
|
+
type({ type: `${ComponentType.ActionRow}`, components }), {
|
|
20
|
+
$: type({ type: `${ComponentType.ActionRow}` }),
|
|
21
|
+
});
|
|
22
|
+
export const $Thumbnail = type({ type: `${ComponentType.Thumbnail}` });
|
|
23
|
+
export const $Section = Object.assign((components, accessory) =>
|
|
24
|
+
// @ts-expect-error
|
|
25
|
+
type({ type: `${ComponentType.Section}`, accessory, components }), { $: type({ type: `${ComponentType.Section}` }) });
|
|
26
|
+
export const $TextDisplay = type({ type: `${ComponentType.TextDisplay}` });
|
|
27
|
+
export const $MediaGallery = type({ type: `${ComponentType.MediaGallery}` });
|
|
28
|
+
export const $File = type({ type: `${ComponentType.File}` });
|
|
29
|
+
export const $Separator = type({ type: `${ComponentType.Separator}` });
|
|
30
|
+
const ComponentInContainer = type({ type: `${ComponentType.ActionRow}` })
|
|
31
|
+
.or($File)
|
|
32
|
+
.or($MediaGallery)
|
|
33
|
+
.or({ type: `${ComponentType.Section}` })
|
|
34
|
+
.or($Separator)
|
|
35
|
+
.or($TextDisplay);
|
|
36
|
+
export const $Container = Object.assign((...components) =>
|
|
37
|
+
// @ts-expect-error
|
|
38
|
+
type({ type: `${ComponentType.Container}`, components }), { $: type({ type: `${ComponentType.Container}` }) });
|
|
39
|
+
export const $TextInput = type({ type: `${ComponentType.TextInput}` });
|
|
40
|
+
export const $FileUpload = type({ type: `${ComponentType.FileUpload}` });
|
|
41
|
+
export const $RadioGroup = type({ type: `${ComponentType.RadioGroup}` });
|
|
42
|
+
export const $CheckboxGroup = type({ type: `${ComponentType.CheckboxGroup}` });
|
|
43
|
+
export const $Checkbox = type({ type: `${ComponentType.Checkbox}` });
|
|
44
|
+
const ComponentInLabel = AnySelect.or($TextInput).or($FileUpload).or($RadioGroup).or($CheckboxGroup).or($Checkbox);
|
|
45
|
+
export const $Label = Object.assign((component) =>
|
|
46
|
+
// @ts-expect-error
|
|
47
|
+
type({ type: `${ComponentType.Label}`, component }), { $: type({ type: `${ComponentType.Label}` }) });
|
|
48
|
+
export const $Any = Object.assign(type({ type: "number" }), {
|
|
49
|
+
ActionRow: AnyButton.or(AnySelect),
|
|
50
|
+
Button: AnyButton,
|
|
51
|
+
Container: ComponentInContainer,
|
|
52
|
+
Label: ComponentInLabel,
|
|
53
|
+
Section: { Component: $TextDisplay, Accessory: AnyButton.or($Thumbnail) },
|
|
54
|
+
Select: AnySelect,
|
|
55
|
+
});
|
|
56
|
+
function recurseFind(yesFn, components) {
|
|
34
57
|
for (const component of components) {
|
|
35
|
-
if ((
|
|
36
|
-
("custom_id" in component &&
|
|
37
|
-
((typeof query === "string" && component.custom_id === query) ||
|
|
38
|
-
(query instanceof RegExp && query.test(component.custom_id))))) {
|
|
58
|
+
if (yesFn(component))
|
|
39
59
|
return component;
|
|
40
|
-
}
|
|
41
60
|
if ("components" in component) {
|
|
42
|
-
const found =
|
|
61
|
+
const found = recurseFind(yesFn, component.components);
|
|
43
62
|
if (found)
|
|
44
63
|
return found;
|
|
45
64
|
}
|
|
46
65
|
if (component.type === ComponentType.Section && component.accessory) {
|
|
47
|
-
const found =
|
|
66
|
+
const found = recurseFind(yesFn, [component.accessory]);
|
|
48
67
|
if (found)
|
|
49
68
|
return found;
|
|
50
69
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
function findParent(child, components) {
|
|
54
|
-
if (components.includes(child))
|
|
55
|
-
return components;
|
|
56
|
-
for (const component of components) {
|
|
57
|
-
if ("components" in component) {
|
|
58
|
-
if (component.components.includes(child))
|
|
59
|
-
return component;
|
|
60
|
-
const found = findParent(child, component.components);
|
|
70
|
+
if (component.type === ComponentType.Label && component.component) {
|
|
71
|
+
const found = recurseFind(yesFn, [component.component]);
|
|
61
72
|
if (found)
|
|
62
73
|
return found;
|
|
63
74
|
}
|
|
64
|
-
if (component.type === ComponentType.Section && component.accessory === child) {
|
|
65
|
-
return component;
|
|
66
|
-
}
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
77
|
+
const findParent = (node, components) => recurseFind((component) => ("components" in component && component.components.includes(node)) ||
|
|
78
|
+
("accessory" in component && component.accessory === node) ||
|
|
79
|
+
("component" in component && component.component === node), components);
|
|
80
|
+
/**
|
|
81
|
+
* Validates that {@link components} match the {@link schema} and creates traversal functions
|
|
82
|
+
* @returns Validated input components and functions for interacting with the layout
|
|
83
|
+
*/
|
|
84
|
+
export default function abseil(components, schema) {
|
|
85
|
+
// @ts-expect-error
|
|
86
|
+
const layout = type(schema).assert(components);
|
|
87
|
+
const fns = {
|
|
88
|
+
parent(node, $component) {
|
|
89
|
+
const parent = findParent(node, layout);
|
|
90
|
+
return $component.allows(parent) ? parent : undefined;
|
|
91
|
+
},
|
|
92
|
+
remove(node) {
|
|
93
|
+
function r(components) {
|
|
94
|
+
if (!node)
|
|
95
|
+
return;
|
|
96
|
+
const index = components.indexOf(node);
|
|
97
|
+
if (index !== -1) {
|
|
98
|
+
components.splice(index, 1);
|
|
99
|
+
return;
|
|
87
100
|
}
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
};
|
|
92
|
-
if (previous)
|
|
93
|
-
traverser.previous = previous;
|
|
94
|
-
if (neighbours) {
|
|
95
|
-
if (!selects.has(component.type)) {
|
|
96
|
-
traverser.insertBefore = (v) => {
|
|
97
|
-
neighbours.splice(neighbours.indexOf(component), 0, v);
|
|
98
|
-
return traverser;
|
|
99
|
-
};
|
|
100
|
-
traverser.last = (t) => {
|
|
101
|
-
for (let i = neighbours.length - 1; i >= 0; --i) {
|
|
102
|
-
const sibling = neighbours[i];
|
|
103
|
-
if (!typesMatch(sibling, t))
|
|
104
|
-
continue;
|
|
105
|
-
return createTraverser(sibling, t, traverser, neighbours);
|
|
101
|
+
for (const branch of components) {
|
|
102
|
+
if ("components" in branch) {
|
|
103
|
+
r(branch.components);
|
|
106
104
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
if (neighbours.length !== neighbours.indexOf(component) + 1) {
|
|
116
|
-
traverser.sibling = (t) => createTraverser(neighbours[neighbours.indexOf(component) + 1], t, traverser, neighbours);
|
|
105
|
+
// @ts-expect-error
|
|
106
|
+
if ("accessory" in branch && branch.accessory === node)
|
|
107
|
+
delete branch.accessory;
|
|
108
|
+
// @ts-expect-error
|
|
109
|
+
if ("component" in branch && branch.component === node)
|
|
110
|
+
delete branch.component;
|
|
111
|
+
}
|
|
117
112
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const component = find(query, components);
|
|
134
|
-
if (!component || !typesMatch(component, type))
|
|
135
|
-
return undefined;
|
|
136
|
-
const parent = findParent(component, components);
|
|
137
|
-
return createTraverser(component, type, undefined, parent && "components" in parent ? parent.components : components);
|
|
113
|
+
r(layout);
|
|
114
|
+
},
|
|
115
|
+
search: (query, $component) => recurseFind((component) => ((typeof query === "number" && component.id === query) ||
|
|
116
|
+
("custom_id" in component &&
|
|
117
|
+
((typeof query === "string" && component.custom_id === query) ||
|
|
118
|
+
(query instanceof RegExp && query.test(component.custom_id)))) ||
|
|
119
|
+
(component.type === ComponentType.TextDisplay &&
|
|
120
|
+
((typeof query === "string" && component.content.includes(query)) ||
|
|
121
|
+
(query instanceof RegExp && query.test(component.content))))) &&
|
|
122
|
+
$component.allows(component), layout),
|
|
123
|
+
sibling(node, $component, offset = 1) {
|
|
124
|
+
var _a, _b;
|
|
125
|
+
const neighbours = (_b = (_a = findParent(node, layout)) === null || _a === void 0 ? void 0 : _a.components) !== null && _b !== void 0 ? _b : layout;
|
|
126
|
+
const next = neighbours[neighbours.indexOf(node) + offset];
|
|
127
|
+
return $component.allows(next) ? next : undefined;
|
|
138
128
|
},
|
|
139
|
-
/** Entry point of the tree, visually this is the top-leftmost component. */
|
|
140
|
-
initial: ((t) => createTraverser(components[0], t, undefined, components)),
|
|
141
129
|
};
|
|
130
|
+
return Object.assign(layout, fns);
|
|
142
131
|
}
|
|
143
|
-
/**
|
|
144
|
-
* Destroy a node in the tree.
|
|
145
|
-
* Data on this node will be stripped, attempting to access most functions will throw.
|
|
146
|
-
* @important This may cause issues when you try to navigate {@link abseil} instances you've previously created
|
|
147
|
-
*/
|
|
148
|
-
export function removeNode(node) {
|
|
149
|
-
const { components } = node.parent([
|
|
150
|
-
"ActionRow",
|
|
151
|
-
"Container",
|
|
152
|
-
"Root",
|
|
153
|
-
"Section",
|
|
154
|
-
]).value;
|
|
155
|
-
components === null || components === void 0 ? void 0 : components.splice(components.indexOf(node.value), 1);
|
|
156
|
-
for (const key of Object.keys(node)) {
|
|
157
|
-
if (key === "previous" || key === "last")
|
|
158
|
-
continue;
|
|
159
|
-
node[key] = (key === "value"
|
|
160
|
-
? null
|
|
161
|
-
: () => {
|
|
162
|
-
throw new RangeError(`Unable to access ${key} on node as it was removed`);
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/** Assert that a component is of certain type(s). */
|
|
167
|
-
export const assert = (component, type) => typesMatch(component, type);
|
|
168
132
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "abseil",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.0-canary.1",
|
|
4
|
+
"description": "Validate and traverse Discord Components V2 trees.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dist": "rm -fr dist && tsc",
|
|
7
7
|
"checks": "tsc --noEmit && biome check --write",
|
|
8
8
|
"dry-publish": "bun publish --dry-run"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
+
"arktype": "^2.2.0",
|
|
11
12
|
"discord-api-types": "^0.38.42"
|
|
12
13
|
},
|
|
13
14
|
"devDependencies": {
|