cordo 2.0.0 → 2.0.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.
Files changed (46) hide show
  1. package/package.json +2 -7
  2. package/src/README.md +3 -0
  3. package/src/components/builtin/button.ts +64 -0
  4. package/src/components/builtin/collection.ts +6 -0
  5. package/src/components/builtin/container.ts +31 -0
  6. package/src/components/builtin/divider.ts +25 -0
  7. package/src/components/builtin/gallery.ts +13 -0
  8. package/src/components/builtin/image.ts +31 -0
  9. package/src/components/builtin/link-button.ts +42 -0
  10. package/src/components/builtin/row.ts +19 -0
  11. package/src/components/builtin/section.ts +24 -0
  12. package/src/components/builtin/select-string.ts +92 -0
  13. package/src/components/builtin/spacer.ts +25 -0
  14. package/src/components/builtin/text.ts +94 -0
  15. package/src/components/component.ts +170 -0
  16. package/src/components/modifier.ts +23 -0
  17. package/src/components/mods/debug-id-to-label.ts +28 -0
  18. package/src/components/mods/debug-print.ts +14 -0
  19. package/src/components/mods/debug-route.ts +17 -0
  20. package/src/components/mods/disable-all-components.ts +38 -0
  21. package/src/core/dynamic-types.ts +5 -0
  22. package/src/core/files/config.ts +161 -0
  23. package/src/core/files/error-boundary.ts +41 -0
  24. package/src/core/files/lockfile.ts +150 -0
  25. package/src/core/files/route.ts +175 -0
  26. package/src/core/gateway.ts +165 -0
  27. package/src/core/hooks.ts +41 -0
  28. package/src/core/interaction.ts +37 -0
  29. package/src/core/magic.ts +65 -0
  30. package/src/core/routing/filesystem.ts +115 -0
  31. package/src/core/routing/resolve.ts +145 -0
  32. package/src/core/routing/respond.ts +208 -0
  33. package/src/errors/builtin/route-assumption-failed.ts +20 -0
  34. package/src/errors/builtin/route-not-found.ts +13 -0
  35. package/src/errors/cordo-error.ts +20 -0
  36. package/src/errors/handle.ts +74 -0
  37. package/src/functions/compiler.ts +206 -0
  38. package/src/functions/funct.ts +56 -0
  39. package/src/functions/impl/goto.ts +82 -0
  40. package/src/functions/impl/run.ts +104 -0
  41. package/src/functions/impl/value.ts +15 -0
  42. package/src/http/express.ts +28 -0
  43. package/src/lib/emoji.ts +28 -0
  44. package/src/lib/ids.test.ts +47 -0
  45. package/src/lib/ids.ts +37 -0
  46. package/src/lib/utils.ts +12 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cordo",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "A framework for handling complex discord api interactions",
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
@@ -11,12 +11,7 @@
11
11
  "./http": "./src/http/index.ts"
12
12
  },
13
13
  "files": [
14
- "src/index.ts",
15
- "src/core/index.ts",
16
- "src/components/index.ts",
17
- "src/errors/index.ts",
18
- "src/functions/index.ts",
19
- "src/http/index.ts"
14
+ "src/*"
20
15
  ],
21
16
  "scripts": {
22
17
  "build": "tsc",
package/src/README.md ADDED
@@ -0,0 +1,3 @@
1
+ /core includes all core components
2
+ /lib includes helper functions etc
3
+ /component contains default discord components
@@ -0,0 +1,64 @@
1
+ import { ButtonStyle, type APIEmoji } from "discord-api-types/v10"
2
+ import { ComponentType, createComponent } from "../component"
3
+ import { LibEmoji } from "../../lib/emoji"
4
+ import { Hooks } from "../../core/hooks"
5
+ import type { CordoFunct, CordoFunctRun } from "../../functions"
6
+ import { FunctCompiler } from "../../functions/compiler"
7
+
8
+
9
+ export function button() {
10
+ let labelVal: string | undefined = undefined
11
+ let emojiVal: APIEmoji | undefined = undefined
12
+ let disabledVal: boolean | undefined = undefined
13
+ let styleVal = ButtonStyle.Secondary
14
+ const functVal: CordoFunct[] = []
15
+
16
+ function getLabel() {
17
+ if (!labelVal)
18
+ return emojiVal ? '' : 'Click'
19
+
20
+ return Hooks.callHook(
21
+ 'transformUserFacingText',
22
+ labelVal,
23
+ { component: 'Button', position: 'label' }
24
+ )
25
+ }
26
+
27
+ const out = {
28
+ ...createComponent('Button', () => ({
29
+ type: ComponentType.Button,
30
+ label: getLabel(),
31
+ emoji: emojiVal,
32
+ style: styleVal,
33
+ disabled: disabledVal,
34
+ custom_id: FunctCompiler.toCustomId(disabledVal ? [] : functVal)
35
+ })),
36
+
37
+ label: (text: string) => {
38
+ labelVal = text
39
+ return out
40
+ },
41
+ emoji: (emoji: LibEmoji.Input) => {
42
+ emojiVal = LibEmoji.read(emoji)
43
+ return out
44
+ },
45
+ style: (style: 'primary' | 'secondary' | 'success' | 'danger') => {
46
+ if (style === 'primary') styleVal = ButtonStyle.Primary
47
+ else if (style === 'secondary') styleVal = ButtonStyle.Secondary
48
+ else if (style === 'success') styleVal = ButtonStyle.Success
49
+ else if (style === 'danger') styleVal = ButtonStyle.Danger
50
+ return out
51
+ },
52
+ disabled(disabled = true, opts?: { greyOut?: boolean }) {
53
+ disabledVal = disabled
54
+ if (opts?.greyOut) styleVal = ButtonStyle.Secondary
55
+ return out
56
+ },
57
+ onClick: (...funct: CordoFunctRun) => {
58
+ functVal.push(...funct)
59
+ return out
60
+ }
61
+ }
62
+
63
+ return out
64
+ }
@@ -0,0 +1,6 @@
1
+ import type { CordoComponent } from "../component"
2
+
3
+
4
+ export function collection(...components: CordoComponent[]) {
5
+ return components
6
+ }
@@ -0,0 +1,31 @@
1
+ import { ComponentType, createComponent, renderComponentList, type CordoComponent } from "../component"
2
+ import type { CordoModifier } from "../modifier"
3
+
4
+
5
+ type AllowedComponents = CordoComponent<'ActionRow' | 'Button' | 'TextDisplay' | 'Section' | 'MediaGallery' | 'Seperator' | 'File' | 'StringSelect'> | CordoModifier
6
+ type AllowedComponentArray = Array<AllowedComponents | AllowedComponents[]>
7
+
8
+ export function container(...components: AllowedComponentArray) {
9
+ let accentColor: string | number | undefined = undefined
10
+ let spoiler: boolean | undefined = undefined
11
+
12
+ const out = {
13
+ ...createComponent('Container', ({ hirarchy, attributes }) => ({
14
+ type: ComponentType.Container,
15
+ components: renderComponentList(components.flat(), 'Container', hirarchy, attributes),
16
+ accent_color: typeof accentColor === 'string' ? parseInt(accentColor.slice(1), 16) : accentColor,
17
+ spoiler
18
+ })),
19
+
20
+ accentColor: (color: string | number) => {
21
+ accentColor = color
22
+ return out
23
+ },
24
+ spoiler: (value: boolean = true) => {
25
+ spoiler = value
26
+ return out
27
+ }
28
+ }
29
+
30
+ return out
31
+ }
@@ -0,0 +1,25 @@
1
+ import { ComponentType, createComponent } from "../component"
2
+
3
+
4
+ export function divider() {
5
+ let spacing: number | undefined = undefined
6
+
7
+ const out = {
8
+ ...createComponent('Seperator', () => ({
9
+ type: ComponentType.Seperator,
10
+ spacing,
11
+ divider: true
12
+ })),
13
+
14
+ size: (size: 'small' | 'large') => {
15
+ spacing = (size === 'small')
16
+ ? 1
17
+ : (size === 'large')
18
+ ? 2
19
+ : undefined
20
+ return out
21
+ }
22
+ }
23
+
24
+ return out
25
+ }
@@ -0,0 +1,13 @@
1
+ import { ComponentType, createComponent, renderComponentList, type CordoComponent } from "../component"
2
+
3
+
4
+ export function gallery(...items: CordoComponent<'Thumbnail'>[]) {
5
+ const out = {
6
+ ...createComponent('MediaGallery', ({ }) => ({
7
+ type: ComponentType.MediaGallery,
8
+ items: renderComponentList(items, 'MediaGallery')
9
+ })),
10
+ }
11
+
12
+ return out
13
+ }
@@ -0,0 +1,31 @@
1
+ import { ComponentType, createComponent } from "../component"
2
+
3
+
4
+ export function image(url: string) {
5
+ let description: string | undefined = undefined
6
+ let spoiler: boolean | undefined = undefined
7
+
8
+ const out = {
9
+ ...createComponent('Thumbnail', ({ hirarchy }) => ({
10
+ type: hirarchy[0] === 'MediaGallery' // if placed inside a media gallery we act as a media gallery item instead
11
+ ? undefined
12
+ : ComponentType.Thumbnail,
13
+ media: {
14
+ url
15
+ },
16
+ spoiler,
17
+ description
18
+ })),
19
+
20
+ description: (value: string) => {
21
+ description = value
22
+ return out
23
+ },
24
+ spoiler: (value: boolean = true) => {
25
+ spoiler = value
26
+ return out
27
+ }
28
+ }
29
+
30
+ return out
31
+ }
@@ -0,0 +1,42 @@
1
+ import { ButtonStyle } from "discord-api-types/v10"
2
+ import { ComponentType, createComponent } from "../component"
3
+ import { Hooks } from "../../core/hooks"
4
+
5
+
6
+ export function linkButton(url: string) {
7
+ let labelVal: string | undefined = undefined
8
+ let emojiVal: string | undefined = undefined
9
+
10
+
11
+ function getLabel() {
12
+ if (!labelVal)
13
+ return emojiVal ? '' : new URL(url).hostname
14
+
15
+ return Hooks.callHook(
16
+ 'transformUserFacingText',
17
+ labelVal,
18
+ { component: 'Button', position: 'label' }
19
+ )
20
+ }
21
+
22
+ const out = {
23
+ ...createComponent('Button', () => ({
24
+ type: ComponentType.Button,
25
+ label: getLabel(),
26
+ emoji: emojiVal,
27
+ style: ButtonStyle.Link,
28
+ url
29
+ })),
30
+
31
+ label: (text: string) => {
32
+ labelVal = text
33
+ return out
34
+ },
35
+ emoji: (emoji: string) => {
36
+ emojiVal = emoji
37
+ return out
38
+ },
39
+ }
40
+
41
+ return out
42
+ }
@@ -0,0 +1,19 @@
1
+ import { ComponentType, createComponent, renderComponentList, type CordoComponent } from "../component"
2
+
3
+
4
+ type AllowedComponents = CordoComponent<'Button' | 'StringSelect'>
5
+ type AllowedComponentArray
6
+ = [ AllowedComponents ]
7
+ | [ AllowedComponents, AllowedComponents ]
8
+ | [ AllowedComponents, AllowedComponents, AllowedComponents ]
9
+ | [ AllowedComponents, AllowedComponents, AllowedComponents, AllowedComponents ]
10
+ | [ AllowedComponents, AllowedComponents, AllowedComponents, AllowedComponents, AllowedComponents ]
11
+
12
+ export function row(...components: AllowedComponentArray | [ AllowedComponentArray ]) {
13
+ if (Array.isArray(components[0]))
14
+ components = components[0]
15
+ return createComponent('ActionRow', ({ hirarchy, attributes }) => ({
16
+ type: ComponentType.ActionRow,
17
+ components: renderComponentList(components as AllowedComponentArray, 'ActionRow', hirarchy, attributes)
18
+ }))
19
+ }
@@ -0,0 +1,24 @@
1
+ import { ComponentType, createComponent, renderComponent, renderComponentList, type CordoComponent } from "../component"
2
+
3
+
4
+ type AllowedComponentsContent = CordoComponent<'TextDisplay'>
5
+ type AllowedComponentsAccessory = CordoComponent<'Thumbnail' | 'Button'>
6
+
7
+ export function section(...components: AllowedComponentsContent[]) {
8
+ let accessory: AllowedComponentsAccessory | undefined = undefined
9
+
10
+ const out = {
11
+ ...createComponent('Section', ({ hirarchy, attributes }) => ({
12
+ type: ComponentType.Section,
13
+ components: renderComponentList(components, 'Section', hirarchy, attributes),
14
+ accessory: accessory ? renderComponent(accessory, 'Section', hirarchy, attributes) : undefined
15
+ })),
16
+
17
+ decorate(component: AllowedComponentsAccessory) {
18
+ accessory = component
19
+ return out
20
+ }
21
+ }
22
+
23
+ return out
24
+ }
@@ -0,0 +1,92 @@
1
+ import { type APISelectMenuOption } from "discord-api-types/v10"
2
+ import { ComponentType, createComponent } from "../component"
3
+ import { Hooks } from "../../core/hooks"
4
+ import { value, type CordoFunct, type CordoFunctRun } from "../../functions"
5
+ import { FunctCompiler } from "../../functions/compiler"
6
+
7
+
8
+ type SelectMenuOption<Value extends string = string> = Omit<APISelectMenuOption, 'value'> & ({
9
+ value?: Value
10
+ onClick?: CordoFunct | CordoFunctRun
11
+ })
12
+
13
+ export function selectString<Values extends string = string>() {
14
+ let placeholderVal: string | undefined = undefined
15
+ let minValues: number | undefined = undefined
16
+ let maxValues: number | undefined = undefined
17
+ let optionsVal: SelectMenuOption[] = []
18
+ let disabledVal: boolean | undefined = undefined
19
+ const functVal: CordoFunct[] = []
20
+
21
+ function getPlaceholder() {
22
+ if (!placeholderVal)
23
+ return undefined
24
+ return Hooks.callHook(
25
+ 'transformUserFacingText',
26
+ placeholderVal,
27
+ { component: 'StringSelect', position: 'placeholder' }
28
+ )
29
+ }
30
+
31
+ function getOptions(): SelectMenuOption[] {
32
+ return optionsVal.slice(0, 25).map(o => ({
33
+ ...o,
34
+ label: Hooks.callHook('transformUserFacingText', o.label, { component: 'StringSelect', position: 'option.label' }),
35
+ description: o.description
36
+ ? Hooks.callHook('transformUserFacingText', o.description, { component: 'StringSelect', position: 'option.description' })
37
+ : undefined,
38
+ value: FunctCompiler.toCustomId([
39
+ ...(o.onClick
40
+ ? Array.isArray(o.onClick)
41
+ ? o.onClick
42
+ : [ o.onClick ]
43
+ : []
44
+ ),
45
+ o.value ? value(o.value) : null
46
+ ])
47
+ }))
48
+ }
49
+
50
+ const out = {
51
+ ...createComponent('StringSelect', () => ({
52
+ type: ComponentType.StringSelect,
53
+ placeholder: getPlaceholder(),
54
+ min_values: minValues,
55
+ max_values: maxValues ? Math.min(optionsVal.length, maxValues) : undefined,
56
+ disabled: disabledVal,
57
+ options: getOptions(),
58
+ custom_id: FunctCompiler.toCustomId(disabledVal ? [] : functVal)
59
+ })),
60
+
61
+ placeholder: (text: string) => {
62
+ placeholderVal = text
63
+ return out
64
+ },
65
+ min: (num: number = 1) => {
66
+ minValues = num
67
+ return out
68
+ },
69
+ max: (num: number = 25) => {
70
+ maxValues = num
71
+ return out
72
+ },
73
+ disabled(disabled = true) {
74
+ disabledVal = disabled
75
+ return out
76
+ },
77
+ onSubmit: (...funct: CordoFunctRun) => {
78
+ functVal.push(...funct)
79
+ return out
80
+ },
81
+ setOptions(options: Array<SelectMenuOption<Values>>) {
82
+ optionsVal = options
83
+ return out
84
+ },
85
+ addOption(o: SelectMenuOption<Values>) {
86
+ optionsVal.push(o)
87
+ return out
88
+ }
89
+ }
90
+
91
+ return out
92
+ }
@@ -0,0 +1,25 @@
1
+ import { ComponentType, createComponent } from "../component"
2
+
3
+
4
+ export function spacer() {
5
+ let spacing: number | undefined = undefined
6
+
7
+ const out = {
8
+ ...createComponent('Seperator', () => ({
9
+ type: ComponentType.Seperator,
10
+ spacing,
11
+ divider: false
12
+ })),
13
+
14
+ size: (size: 'small' | 'large') => {
15
+ spacing = (size === 'small')
16
+ ? 1
17
+ : (size === 'large')
18
+ ? 2
19
+ : undefined
20
+ return out
21
+ }
22
+ }
23
+
24
+ return out
25
+ }
@@ -0,0 +1,94 @@
1
+ import { Hooks } from "../../core/hooks"
2
+ import { ComponentType, createComponent } from "../component"
3
+
4
+
5
+ export function text(...content: Array<string | { toString: () => string }>) {
6
+ let outerPrefix: string = ''
7
+ let innerPrefix: string = ''
8
+ let innerSuffix: string = ''
9
+ let linkUrl: string | null = null
10
+
11
+ function toString(attributes: Record<string, any> = {}): string {
12
+ const stringContent = content
13
+ .map(c => typeof c === 'string' ? c : c.toString())
14
+ .map(c => Hooks.callHook('transformUserFacingText', c, { ...attributes, component: 'TextDisplay', position: null }))
15
+ const innerContent = (innerPrefix ?? '') + stringContent.join(' ').replace(/^ +| +$/mg, '') + (innerSuffix ?? '')
16
+ const outerContent = linkUrl
17
+ ? `[${innerContent}](${linkUrl})`
18
+ : innerContent
19
+ return outerPrefix + outerContent as string
20
+ }
21
+
22
+ const out = {
23
+ ...createComponent('TextDisplay', ({ attributes }) => ({
24
+ type: ComponentType.TextDisplay,
25
+ content: toString(attributes)
26
+ })),
27
+
28
+ toString,
29
+
30
+ size: (size: 'h1' | 'h2' | 'h3' | 'small' | 'default') => {
31
+ if (size === 'h1') outerPrefix = '# '
32
+ else if (size === 'h2') outerPrefix = '## '
33
+ else if (size === 'h3') outerPrefix = '### '
34
+ else if (size === 'small') outerPrefix = '-# '
35
+ else outerPrefix = ''
36
+ return out
37
+ },
38
+
39
+ link: (url: string) => {
40
+ linkUrl = url
41
+ return out
42
+ },
43
+
44
+ bold: (val = true) => {
45
+ if (val) {
46
+ innerPrefix = innerPrefix + '**'
47
+ innerSuffix = '**' + innerSuffix
48
+ }
49
+ return out
50
+ },
51
+ italic: (val = true) => {
52
+ if (val) {
53
+ innerPrefix = innerPrefix + '*'
54
+ innerSuffix = '*' + innerSuffix
55
+ }
56
+ return out
57
+ },
58
+ underline: (val = true) => {
59
+ if (val) {
60
+ innerPrefix = innerPrefix + '__'
61
+ innerSuffix = '__' + innerSuffix
62
+ }
63
+ return out
64
+ },
65
+ strike: (val = true) => {
66
+ if (val) {
67
+ innerPrefix = innerPrefix + '~~'
68
+ innerSuffix = '~~' + innerSuffix
69
+ }
70
+ return out
71
+ },
72
+ quote: (val = true) => {
73
+ if (val)
74
+ outerPrefix = '> '
75
+ return out
76
+ },
77
+ code: (val = true) => {
78
+ if (val) {
79
+ innerPrefix = innerPrefix + '`'
80
+ innerSuffix = '`' + innerSuffix
81
+ }
82
+ return out
83
+ },
84
+ codeBlock: (language = '', val = true) => {
85
+ if (val) {
86
+ innerPrefix = innerPrefix + '```' + (language ? language + '\n' : '')
87
+ innerSuffix = '```' + innerSuffix
88
+ }
89
+ return out
90
+ }
91
+ }
92
+
93
+ return out
94
+ }
@@ -0,0 +1,170 @@
1
+ import defu from "defu"
2
+ import { row } from "./builtin/row"
3
+ import { readModifier, type CordoModifier } from "./modifier"
4
+
5
+
6
+ const CordoComponentSymbol = Symbol('CordoComponent')
7
+
8
+ export const ComponentType = {
9
+ ActionRow: 1,
10
+ Button: 2,
11
+ StringSelect: 3,
12
+ TextInput: 4,
13
+ UserSelect: 5,
14
+ RoleSelect: 6,
15
+ MentionableSelect: 7,
16
+ ChannelSelect: 8,
17
+ Section: 9,
18
+ TextDisplay: 10,
19
+ Thumbnail: 11,
20
+ MediaGallery: 12,
21
+ File: 13,
22
+ Seperator: 14,
23
+ Container: 17
24
+ } as const
25
+ export type StringComponentType = keyof typeof ComponentType
26
+ export type ComponentIdFromName<Name extends StringComponentType> = typeof ComponentType[Name]
27
+
28
+ export type CordoComponent<Type extends StringComponentType = StringComponentType> = {
29
+ [CordoComponentSymbol]: {
30
+ nativeName: Type
31
+ nativeType: typeof ComponentType[Type]
32
+ visible: boolean
33
+ attributes: Record<string, any>
34
+ render: (meta: {
35
+ hirarchy: Array<StringComponentType>
36
+ attributes: Record<string, any>
37
+ }) => Record<string, any> | null
38
+ }
39
+ visible: (value: boolean) => CordoComponent<Type>
40
+ attributes: (attrs: Record<string, any>) => CordoComponent<Type>
41
+ }
42
+ export type CordoComponentPayload<Type extends StringComponentType> = CordoComponent<Type>[typeof CordoComponentSymbol]
43
+
44
+ export function createComponent<Type extends StringComponentType>(
45
+ type: Type,
46
+ render: CordoComponentPayload<Type>['render']
47
+ ): CordoComponent<Type> {
48
+ const comp = {
49
+ nativeName: type,
50
+ nativeType: ComponentType[type],
51
+ visible: true,
52
+ attributes: {},
53
+ render
54
+ }
55
+ const out = {
56
+ [CordoComponentSymbol]: comp,
57
+ visible(value: boolean) {
58
+ comp.visible = value
59
+ return this
60
+ },
61
+ attributes(attrs: Record<string, any>) {
62
+ comp.attributes = defu(attrs, comp.attributes)
63
+ return this
64
+ }
65
+ }
66
+ return out
67
+ }
68
+
69
+ export function readComponent<T extends CordoComponent<StringComponentType>>(comp: T): T[typeof CordoComponentSymbol] {
70
+ return comp[CordoComponentSymbol]!
71
+ }
72
+
73
+ export function isComponent(t: Record<string, any>): t is CordoComponent<StringComponentType> {
74
+ return !!t && CordoComponentSymbol in t
75
+ }
76
+
77
+ export function renderComponent(
78
+ c: CordoComponent<StringComponentType> | CordoComponentPayload<StringComponentType>,
79
+ parent: StringComponentType | null,
80
+ hirarchy: Array<StringComponentType> = [],
81
+ inheritAttributes: Record<string, any> = {}
82
+ ) {
83
+ const extracted = CordoComponentSymbol in c ? readComponent(c) : c
84
+ if (!extracted.visible)
85
+ return null
86
+ return extracted.render({
87
+ hirarchy: parent
88
+ ? [ parent, ...hirarchy ]
89
+ : hirarchy,
90
+ attributes: defu(extracted.attributes, inheritAttributes)
91
+ })
92
+ }
93
+
94
+ export function renderComponentList(
95
+ list: Array<CordoComponent<StringComponentType> | CordoModifier>,
96
+ parent: StringComponentType | null,
97
+ hirarchy: Array<StringComponentType> = [],
98
+ inheritAttributes: Record<string, any> = {}
99
+ ) {
100
+ let pipeline: Array<CordoComponentPayload<StringComponentType>> = []
101
+ const rowBuilder: Array<CordoComponent<StringComponentType>> = []
102
+ const modifiers: Array<ReturnType<typeof readModifier>> = []
103
+
104
+ for (const item of list) {
105
+ if (!item)
106
+ continue
107
+
108
+ if (!isComponent(item)) {
109
+ const mod = readModifier(item)
110
+ if (!modifiers.some(m => m.name === mod.name))
111
+ modifiers.push(mod)
112
+ continue
113
+ }
114
+
115
+ let parsed = readComponent(item)
116
+ for (const mod of modifiers) {
117
+ if (mod.hooks?.onRender)
118
+ parsed = mod.hooks.onRender(parsed)
119
+ }
120
+
121
+ if (parent !== 'ActionRow') {
122
+ if (parsed.nativeName === 'Button') {
123
+ rowBuilder.push(item)
124
+ if (rowBuilder.length === 5) {
125
+ pipeline.push(readComponent(row(...rowBuilder as any)))
126
+ rowBuilder.splice(0)
127
+ }
128
+ continue
129
+ }
130
+
131
+ if (parsed.nativeName === 'StringSelect') {
132
+ if (rowBuilder.length > 0) {
133
+ pipeline.push(readComponent(row(...rowBuilder as any)))
134
+ rowBuilder.splice(0)
135
+ }
136
+
137
+ pipeline.push(readComponent(row(item as any)))
138
+ continue
139
+ }
140
+ }
141
+
142
+ if (rowBuilder.length > 0) {
143
+ pipeline.push(readComponent(row(...rowBuilder as any)))
144
+ rowBuilder.splice(0)
145
+ }
146
+
147
+ pipeline.push(parsed)
148
+ }
149
+
150
+ if (rowBuilder.length > 0) {
151
+ pipeline.push(readComponent(row(...rowBuilder as any)))
152
+ rowBuilder.splice(0)
153
+ }
154
+
155
+ for (const mod of modifiers) {
156
+ if (mod.hooks?.preRender)
157
+ pipeline = mod.hooks.preRender(pipeline)
158
+ }
159
+
160
+ let output = pipeline
161
+ .map(c => renderComponent(c, parent, hirarchy, inheritAttributes))
162
+ .filter(Boolean)
163
+
164
+ for (const mod of modifiers) {
165
+ if (mod.hooks?.postRender)
166
+ output = mod.hooks.postRender(output)
167
+ }
168
+
169
+ return output
170
+ }