abseil 0.1.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 +58 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Abseil
|
|
2
|
+
|
|
3
|
+
Quickly traverse Discord [Components V2](https://docs.discord.com/developers/components/overview) trees.
|
|
4
|
+
|
|
5
|
+
## Reference
|
|
6
|
+
|
|
7
|
+
| function | description |
|
|
8
|
+
| --------- | ----------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| accessory | Access the accessory of a [Section](https://docs.discord.com/developers/components/reference#section) |
|
|
10
|
+
| child | Access the first child of a node |
|
|
11
|
+
| previous | Return to the previous node |
|
|
12
|
+
| sibling | Access the next node in an array |
|
|
13
|
+
| update | Shallow merge with the current component in place |
|
|
14
|
+
| value | The current component |
|
|
15
|
+
|
|
16
|
+
## Example
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { type APIMessage, ComponentType } from "discord-api-types/v10";
|
|
20
|
+
import { Button, editMessage } from "dressed";
|
|
21
|
+
import abseil from "abseil";
|
|
22
|
+
|
|
23
|
+
const message = {
|
|
24
|
+
components: [
|
|
25
|
+
{ type: ComponentType.TextDisplay },
|
|
26
|
+
{
|
|
27
|
+
type: ComponentType.Container,
|
|
28
|
+
components: [
|
|
29
|
+
{
|
|
30
|
+
type: ComponentType.Section,
|
|
31
|
+
accessory: { type: ComponentType.Thumbnail },
|
|
32
|
+
components: [{ type: ComponentType.TextDisplay }, { type: ComponentType.TextDisplay }],
|
|
33
|
+
},
|
|
34
|
+
{ type: ComponentType.Separator },
|
|
35
|
+
{ type: ComponentType.ActionRow, components: [{ type: ComponentType.UserSelect }] },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: ComponentType.ActionRow,
|
|
40
|
+
components: [{ type: ComponentType.Button }, { type: ComponentType.Button }],
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
} as APIMessage;
|
|
44
|
+
|
|
45
|
+
abseil(message.components ?? [])
|
|
46
|
+
.initial("TextDisplay")
|
|
47
|
+
.sibling("Container")
|
|
48
|
+
.update({ spoiler: true })
|
|
49
|
+
.sibling("ActionRow")
|
|
50
|
+
.update((p) => ({
|
|
51
|
+
components: p.components.concat(Button({ custom_id: "new", label: "I'm new!" })),
|
|
52
|
+
}))
|
|
53
|
+
.previous.child("Section")
|
|
54
|
+
.accessory(["Button", "Thumbnail"])
|
|
55
|
+
.update({ id: 32 });
|
|
56
|
+
|
|
57
|
+
editMessage("<CHANNEL_ID>", "<MESSAGE_ID>", message.components);
|
|
58
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: Not worth my time */
|
|
2
|
+
import { type APIComponentInContainer, type APIComponentInMessageActionRow, type APIMessageComponent, type APISectionAccessoryComponent, type APISelectMenuComponent, ComponentType } from "discord-api-types/v10";
|
|
3
|
+
type CT = typeof ComponentType & {
|
|
4
|
+
"Button:URL": ComponentType.Button;
|
|
5
|
+
"Button:SKU": ComponentType.Button;
|
|
6
|
+
};
|
|
7
|
+
type IComponentType = {
|
|
8
|
+
[K in keyof CT as CT[K]]: K;
|
|
9
|
+
};
|
|
10
|
+
type Pop<T extends any[]> = T extends [...infer Rest, any] ? Rest : never;
|
|
11
|
+
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
|
|
12
|
+
type Arrable<T> = T | T[];
|
|
13
|
+
type Narr<T extends Arrable<string>> = T extends string ? T : T[number];
|
|
14
|
+
type Overwriter<T> = Partial<T> | ((prev: T) => Partial<T>);
|
|
15
|
+
interface ChildrenMap {
|
|
16
|
+
ActionRow: IComponentType[APIComponentInMessageActionRow["type"]] | "Button:URL" | "Button:SKU";
|
|
17
|
+
Container: IComponentType[APIComponentInContainer["type"]];
|
|
18
|
+
Section: "TextDisplay";
|
|
19
|
+
}
|
|
20
|
+
type TraverserFn<C extends keyof CT, H extends (keyof CT)[], O extends string = ""> = <const T extends Arrable<C>>(type: T) => Omit<Traverser<Narr<T>, H, C>, O>;
|
|
21
|
+
type Component<T extends keyof CT> = Extract<APIMessageComponent, {
|
|
22
|
+
type: CT[T extends "Button:URL" | "Button:SKU" ? "Button" : T];
|
|
23
|
+
} & (T extends "Button" ? {
|
|
24
|
+
custom_id: string;
|
|
25
|
+
} : T extends "Button:URL" ? {
|
|
26
|
+
url: string;
|
|
27
|
+
} : T extends "Button:SKU" ? {
|
|
28
|
+
sku_id: string;
|
|
29
|
+
} : object)>;
|
|
30
|
+
type Traverser<T extends keyof CT, H extends (keyof CT)[], ST extends keyof CT = keyof CT> = {
|
|
31
|
+
/** Shallow merge with the current component in place */
|
|
32
|
+
update: (value: Overwriter<Component<T>>) => Traverser<T, H, ST>;
|
|
33
|
+
/** The current component */
|
|
34
|
+
value: Component<T>;
|
|
35
|
+
} & (T extends IComponentType[APISelectMenuComponent["type"]] ? object : {
|
|
36
|
+
/** Access the next node in an array */
|
|
37
|
+
sibling: TraverserFn<ST, [...H, T]>;
|
|
38
|
+
}) & (T extends keyof ChildrenMap ? {
|
|
39
|
+
/** Access the first child of a node */
|
|
40
|
+
child: TraverserFn<ChildrenMap[T], [...H, T]>;
|
|
41
|
+
} : object) & (T extends "Section" ? {
|
|
42
|
+
/** Access the accessory of a [Section](https://docs.discord.com/developers/components/reference#section) */
|
|
43
|
+
accessory: TraverserFn<IComponentType[APISectionAccessoryComponent["type"]], [...H, T], "sibling">;
|
|
44
|
+
} : object) & (H extends [] ? object : {
|
|
45
|
+
/** Return to the previous node */
|
|
46
|
+
previous: Traverser<Last<H>, Pop<H>>;
|
|
47
|
+
});
|
|
48
|
+
declare const abseil: <T extends APIMessageComponent>(components: T[]) => {
|
|
49
|
+
initial: TraverserFn<IComponentType[T["type"]], [], "previous">;
|
|
50
|
+
};
|
|
51
|
+
export default abseil;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: Not worth my time */
|
|
2
|
+
import { ComponentType, } from "discord-api-types/v10";
|
|
3
|
+
const withChildren = new Set([ComponentType.ActionRow, ComponentType.Section, ComponentType.Container]);
|
|
4
|
+
function createTraverser(component, expected, previous, siblings) {
|
|
5
|
+
const expectedTypes = new Set(Array.isArray(expected) ? expected : [expected]);
|
|
6
|
+
let actualType = ComponentType[component.type];
|
|
7
|
+
if (actualType === "Button") {
|
|
8
|
+
if ("url" in component)
|
|
9
|
+
actualType += ":URL";
|
|
10
|
+
else if ("sku_id" in component)
|
|
11
|
+
actualType += ":SKU";
|
|
12
|
+
}
|
|
13
|
+
if (!expectedTypes.has(actualType)) {
|
|
14
|
+
throw new TypeError(`Type mismatch: expected ${[...expectedTypes].join(" or ")}, got ${actualType}`);
|
|
15
|
+
}
|
|
16
|
+
const traverser = {
|
|
17
|
+
value: component,
|
|
18
|
+
update(v) {
|
|
19
|
+
Object.assign(component, typeof v === "function" ? v(component) : v);
|
|
20
|
+
return traverser;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
if (previous)
|
|
24
|
+
traverser.previous = previous;
|
|
25
|
+
if (siblings === null || siblings === void 0 ? void 0 : siblings.length) {
|
|
26
|
+
traverser.sibling = (t) => createTraverser(siblings[0], t, traverser, siblings.slice(1));
|
|
27
|
+
}
|
|
28
|
+
if (withChildren.has(component.type) && "components" in component) {
|
|
29
|
+
traverser.child = (t) => createTraverser(component.components[0], t, traverser, component.components.slice(1));
|
|
30
|
+
}
|
|
31
|
+
if (component.type === ComponentType.Section) {
|
|
32
|
+
traverser.accessory = (t) => createTraverser(component.accessory, t, traverser);
|
|
33
|
+
}
|
|
34
|
+
return traverser;
|
|
35
|
+
}
|
|
36
|
+
const abseil = (components) => ({
|
|
37
|
+
initial: ((t) => createTraverser(components[0], t, undefined, components.slice(1))),
|
|
38
|
+
});
|
|
39
|
+
export default abseil;
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,OAAO,EAML,aAAa,GACd,MAAM,uBAAuB,CAAC;AAmE/B,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,CAAC,SAAS,EAAE,aAAa,CAAC,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;AAExG,SAAS,eAAe,CACtB,SAAY,EACZ,QAA2B,EAC3B,QAA4C,EAC5C,QAAgC;IAEhC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/E,IAAI,UAAU,GAAG,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/C,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,IAAI,KAAK,IAAI,SAAS;YAAE,UAAU,IAAI,MAAM,CAAC;aACxC,IAAI,QAAQ,IAAI,SAAS;YAAE,UAAU,IAAI,MAAM,CAAC;IACvD,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC,2BAA2B,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;IACvG,CAAC;IACD,MAAM,SAAS,GAAQ;QACrB,KAAK,EAAE,SAAS;QAChB,MAAM,CAAC,CAAkC;YACvC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrE,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;IACF,IAAI,QAAQ;QAAE,SAAS,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC5C,IAAI,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,MAAM,EAAE,CAAC;QACrB,SAAS,CAAC,OAAO,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnG,CAAC;IACD,IAAI,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,YAAY,IAAI,SAAS,EAAE,CAAC;QAClE,SAAS,CAAC,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAC9B,eAAe,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,SAAS,CAAC,IAAI,KAAK,aAAa,CAAC,OAAO,EAAE,CAAC;QAC7C,SAAS,CAAC,SAAS,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,MAAM,GAAG,CAAgC,UAAe,EAAE,EAAE,CAAC,CAAC;IAClE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAIjF;CACF,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "abseil",
|
|
3
|
+
"version": "0.1.0-canary.1",
|
|
4
|
+
"description": "Quickly traverse Discord Components V2 trees.",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dist": "rm -fr dist && tsc",
|
|
7
|
+
"checks": "tsc --noEmit && biome check --write",
|
|
8
|
+
"dry-publish": "bun publish --dry-run"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"discord-api-types": "^0.38.42"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@biomejs/biome": "^2.4.8",
|
|
15
|
+
"@types/bun": "^1.3.11",
|
|
16
|
+
"typescript": "^5.9.3"
|
|
17
|
+
},
|
|
18
|
+
"exports": "./dist/index.js",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"discord",
|
|
26
|
+
"components",
|
|
27
|
+
"components v2"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/Inbestigator/abseil.git",
|
|
33
|
+
"directory": "packages/abseil"
|
|
34
|
+
}
|
|
35
|
+
}
|