chapterjs 0.1.2 → 0.1.4

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 (320) hide show
  1. package/dist/assets/asset.d.ts +46 -0
  2. package/dist/assets/asset.js +86 -0
  3. package/dist/assets/public.d.ts +19 -0
  4. package/dist/assets/public.js +120 -0
  5. package/dist/cache/cache.d.ts +62 -0
  6. package/dist/cache/cache.js +80 -0
  7. package/dist/cache/store.d.ts +45 -0
  8. package/dist/cache/store.js +53 -0
  9. package/dist/cli/build.d.ts +41 -0
  10. package/dist/cli/build.js +206 -0
  11. package/dist/cli/cli.js +108 -25
  12. package/dist/cli/cluster.d.ts +70 -0
  13. package/dist/cli/cluster.js +213 -0
  14. package/dist/cli/dev.d.ts +18 -1
  15. package/dist/cli/dev.js +252 -198
  16. package/dist/cli/env.d.ts +31 -0
  17. package/dist/cli/env.js +93 -0
  18. package/dist/cli/log.d.ts +13 -0
  19. package/dist/cli/log.js +17 -0
  20. package/dist/cli/node-version.d.ts +8 -0
  21. package/dist/cli/node-version.js +13 -0
  22. package/dist/cli/preflight.d.ts +36 -0
  23. package/dist/cli/preflight.js +122 -0
  24. package/dist/cli/project.d.ts +122 -0
  25. package/dist/cli/project.js +452 -0
  26. package/dist/cli/start.d.ts +25 -0
  27. package/dist/cli/start.js +225 -0
  28. package/dist/commands/autocomplete.d.ts +13 -0
  29. package/dist/commands/autocomplete.js +159 -0
  30. package/dist/commands/command.d.ts +346 -0
  31. package/dist/commands/command.js +28 -0
  32. package/dist/commands/convention.d.ts +64 -0
  33. package/dist/commands/convention.js +464 -0
  34. package/dist/commands/register.d.ts +18 -0
  35. package/dist/commands/register.js +42 -0
  36. package/dist/commands/router.d.ts +20 -0
  37. package/dist/commands/router.js +228 -0
  38. package/dist/commands/tree.d.ts +43 -0
  39. package/dist/commands/tree.js +206 -0
  40. package/dist/commands/twins.d.ts +15 -0
  41. package/dist/commands/twins.js +78 -0
  42. package/dist/components/button.d.ts +79 -0
  43. package/dist/components/button.js +108 -0
  44. package/dist/components/component.d.ts +140 -0
  45. package/dist/components/component.js +39 -0
  46. package/dist/components/convention.d.ts +35 -0
  47. package/dist/components/convention.js +339 -0
  48. package/dist/components/custom-id.d.ts +72 -0
  49. package/dist/components/custom-id.js +128 -0
  50. package/dist/components/embed.d.ts +28 -0
  51. package/dist/components/embed.js +11 -0
  52. package/dist/components/file.d.ts +30 -0
  53. package/dist/components/file.js +52 -0
  54. package/dist/components/instance.d.ts +100 -0
  55. package/dist/components/instance.js +86 -0
  56. package/dist/components/layout.d.ts +103 -0
  57. package/dist/components/layout.js +279 -0
  58. package/dist/components/modal.d.ts +244 -0
  59. package/dist/components/modal.js +399 -0
  60. package/dist/components/options.d.ts +41 -0
  61. package/dist/components/options.js +101 -0
  62. package/dist/components/render.d.ts +12 -0
  63. package/dist/components/render.js +58 -0
  64. package/dist/components/router.d.ts +18 -0
  65. package/dist/components/router.js +256 -0
  66. package/dist/components/select.d.ts +144 -0
  67. package/dist/components/select.js +179 -0
  68. package/dist/core/bot.d.ts +99 -0
  69. package/dist/core/bot.js +174 -0
  70. package/dist/core/memory.d.ts +66 -0
  71. package/dist/core/memory.js +115 -0
  72. package/dist/discord/api.d.ts +112 -0
  73. package/dist/discord/api.js +115 -0
  74. package/dist/discord/cdn.d.ts +67 -0
  75. package/dist/discord/cdn.js +100 -0
  76. package/dist/discord/codes.d.ts +171 -0
  77. package/dist/discord/codes.js +421 -0
  78. package/dist/discord/endpoint.d.ts +56 -0
  79. package/dist/discord/endpoint.js +53 -0
  80. package/dist/discord/endpoints.d.ts +1529 -0
  81. package/dist/discord/endpoints.js +1139 -0
  82. package/dist/discord/formatting.d.ts +67 -0
  83. package/dist/discord/formatting.js +87 -0
  84. package/dist/discord/index.d.ts +10 -0
  85. package/dist/discord/index.js +13 -0
  86. package/dist/discord/intents.d.ts +48 -0
  87. package/dist/discord/intents.js +128 -0
  88. package/dist/discord/permissions.d.ts +94 -0
  89. package/dist/discord/permissions.js +185 -0
  90. package/dist/discord/snowflake.d.ts +21 -0
  91. package/dist/discord/snowflake.js +36 -0
  92. package/dist/discord/types/application-command.d.ts +332 -0
  93. package/dist/discord/types/application-command.js +48 -0
  94. package/dist/discord/types/application-role-connection-metadata.d.ts +42 -0
  95. package/dist/discord/types/application-role-connection-metadata.js +22 -0
  96. package/dist/discord/types/application.d.ts +208 -0
  97. package/dist/discord/types/application.js +48 -0
  98. package/dist/discord/types/audit-log.d.ts +255 -0
  99. package/dist/discord/types/audit-log.js +144 -0
  100. package/dist/discord/types/auto-moderation.d.ts +167 -0
  101. package/dist/discord/types/auto-moderation.js +52 -0
  102. package/dist/discord/types/channel.d.ts +557 -0
  103. package/dist/discord/types/channel.js +88 -0
  104. package/dist/discord/types/common.d.ts +54 -0
  105. package/dist/discord/types/common.js +38 -0
  106. package/dist/discord/types/component.d.ts +775 -0
  107. package/dist/discord/types/component.js +76 -0
  108. package/dist/discord/types/emoji.d.ts +74 -0
  109. package/dist/discord/types/emoji.js +1 -0
  110. package/dist/discord/types/entitlement.d.ts +84 -0
  111. package/dist/discord/types/entitlement.js +22 -0
  112. package/dist/discord/types/gateway-events.d.ts +1148 -0
  113. package/dist/discord/types/gateway-events.js +54 -0
  114. package/dist/discord/types/gateway.d.ts +38 -0
  115. package/dist/discord/types/gateway.js +1 -0
  116. package/dist/discord/types/guild-scheduled-event.d.ts +257 -0
  117. package/dist/discord/types/guild-scheduled-event.js +68 -0
  118. package/dist/discord/types/guild-template.d.ts +51 -0
  119. package/dist/discord/types/guild-template.js +1 -0
  120. package/dist/discord/types/guild.d.ts +910 -0
  121. package/dist/discord/types/guild.js +153 -0
  122. package/dist/discord/types/index.d.ts +31 -0
  123. package/dist/discord/types/index.js +31 -0
  124. package/dist/discord/types/interaction.d.ts +321 -0
  125. package/dist/discord/types/interaction.js +47 -0
  126. package/dist/discord/types/invite.d.ts +158 -0
  127. package/dist/discord/types/invite.js +39 -0
  128. package/dist/discord/types/message.d.ts +834 -0
  129. package/dist/discord/types/message.js +159 -0
  130. package/dist/discord/types/oauth2.d.ts +22 -0
  131. package/dist/discord/types/oauth2.js +1 -0
  132. package/dist/discord/types/permissions.d.ts +183 -0
  133. package/dist/discord/types/permissions.js +118 -0
  134. package/dist/discord/types/poll.d.ts +106 -0
  135. package/dist/discord/types/poll.js +8 -0
  136. package/dist/discord/types/sku.d.ts +47 -0
  137. package/dist/discord/types/sku.js +26 -0
  138. package/dist/discord/types/soundboard.d.ts +71 -0
  139. package/dist/discord/types/soundboard.js +1 -0
  140. package/dist/discord/types/stage-instance.d.ts +58 -0
  141. package/dist/discord/types/stage-instance.js +10 -0
  142. package/dist/discord/types/sticker.d.ts +115 -0
  143. package/dist/discord/types/sticker.js +20 -0
  144. package/dist/discord/types/subscription.d.ts +54 -0
  145. package/dist/discord/types/subscription.js +12 -0
  146. package/dist/discord/types/team.d.ts +55 -0
  147. package/dist/discord/types/team.js +21 -0
  148. package/dist/discord/types/user.d.ts +273 -0
  149. package/dist/discord/types/user.js +83 -0
  150. package/dist/discord/types/voice.d.ts +72 -0
  151. package/dist/discord/types/voice.js +1 -0
  152. package/dist/discord/types/webhook.d.ts +184 -0
  153. package/dist/discord/types/webhook.js +12 -0
  154. package/dist/events/convention.d.ts +25 -0
  155. package/dist/events/convention.js +97 -0
  156. package/dist/events/event.d.ts +8 -0
  157. package/dist/events/event.js +20 -0
  158. package/dist/events/registry.d.ts +706 -0
  159. package/dist/events/registry.js +839 -0
  160. package/dist/events/router.d.ts +45 -0
  161. package/dist/events/router.js +133 -0
  162. package/dist/events/types.d.ts +7 -0
  163. package/dist/events/types.js +48 -0
  164. package/dist/gateway/errors.d.ts +18 -0
  165. package/dist/gateway/errors.js +47 -0
  166. package/dist/gateway/gateway.d.ts +64 -0
  167. package/dist/gateway/gateway.js +117 -0
  168. package/dist/gateway/identify-queue.d.ts +25 -0
  169. package/dist/gateway/identify-queue.js +29 -0
  170. package/dist/gateway/shard.d.ts +94 -0
  171. package/dist/gateway/shard.js +414 -0
  172. package/dist/gateway/state.d.ts +11 -0
  173. package/dist/gateway/state.js +184 -0
  174. package/dist/index.d.ts +55 -18
  175. package/dist/index.js +38 -12
  176. package/dist/interactions/cooldown.d.ts +33 -0
  177. package/dist/interactions/cooldown.js +74 -0
  178. package/dist/interactions/dispatch.d.ts +91 -0
  179. package/dist/interactions/dispatch.js +110 -0
  180. package/dist/loader/generated.d.ts +37 -0
  181. package/dist/loader/generated.js +114 -0
  182. package/dist/loader/hot.d.ts +13 -0
  183. package/dist/loader/hot.js +85 -0
  184. package/dist/loader/loader.d.ts +72 -0
  185. package/dist/loader/loader.js +135 -0
  186. package/dist/loader/locate.d.ts +10 -0
  187. package/dist/loader/locate.js +36 -0
  188. package/dist/loader/watch.d.ts +17 -0
  189. package/dist/loader/watch.js +66 -0
  190. package/dist/main.d.ts +3 -0
  191. package/dist/main.js +6 -0
  192. package/dist/messages/commands.d.ts +23 -0
  193. package/dist/messages/commands.js +123 -0
  194. package/dist/messages/convention.d.ts +89 -0
  195. package/dist/messages/convention.js +347 -0
  196. package/dist/messages/messages.d.ts +219 -0
  197. package/dist/messages/messages.js +30 -0
  198. package/dist/messages/phrases.d.ts +23 -0
  199. package/dist/messages/phrases.js +44 -0
  200. package/dist/messages/translate.d.ts +100 -0
  201. package/dist/messages/translate.js +249 -0
  202. package/dist/presence/convention.d.ts +26 -0
  203. package/dist/presence/convention.js +133 -0
  204. package/dist/presence/presence.d.ts +57 -0
  205. package/dist/presence/presence.js +27 -0
  206. package/dist/rest/errors.d.ts +55 -0
  207. package/dist/rest/errors.js +92 -0
  208. package/dist/rest/rate-limiter.d.ts +46 -0
  209. package/dist/rest/rate-limiter.js +153 -0
  210. package/dist/rest/rest.d.ts +63 -0
  211. package/dist/rest/rest.js +194 -0
  212. package/dist/structures/base.d.ts +48 -0
  213. package/dist/structures/base.js +84 -0
  214. package/dist/structures/channel.d.ts +380 -0
  215. package/dist/structures/channel.js +627 -0
  216. package/dist/structures/context.d.ts +30 -0
  217. package/dist/structures/context.js +7 -0
  218. package/dist/structures/emoji.d.ts +46 -0
  219. package/dist/structures/emoji.js +65 -0
  220. package/dist/structures/entities.d.ts +62 -0
  221. package/dist/structures/entities.js +226 -0
  222. package/dist/structures/guild.d.ts +348 -0
  223. package/dist/structures/guild.js +466 -0
  224. package/dist/structures/interaction.d.ts +197 -0
  225. package/dist/structures/interaction.js +396 -0
  226. package/dist/structures/invite.d.ts +38 -0
  227. package/dist/structures/invite.js +57 -0
  228. package/dist/structures/known.d.ts +28 -0
  229. package/dist/structures/known.js +58 -0
  230. package/dist/structures/member.d.ts +140 -0
  231. package/dist/structures/member.js +260 -0
  232. package/dist/structures/message.d.ts +197 -0
  233. package/dist/structures/message.js +268 -0
  234. package/dist/structures/payload.d.ts +91 -0
  235. package/dist/structures/payload.js +184 -0
  236. package/dist/structures/role.d.ts +69 -0
  237. package/dist/structures/role.js +112 -0
  238. package/dist/structures/user.d.ts +59 -0
  239. package/dist/structures/user.js +94 -0
  240. package/dist/structures/webhook.d.ts +46 -0
  241. package/dist/structures/webhook.js +79 -0
  242. package/dist/tasks/convention.d.ts +26 -0
  243. package/dist/tasks/convention.js +78 -0
  244. package/dist/tasks/schedule.d.ts +54 -0
  245. package/dist/tasks/schedule.js +236 -0
  246. package/dist/tasks/scheduler.d.ts +33 -0
  247. package/dist/tasks/scheduler.js +107 -0
  248. package/dist/tasks/task.d.ts +76 -0
  249. package/dist/tasks/task.js +28 -0
  250. package/dist/util/case.d.ts +13 -0
  251. package/dist/util/case.js +54 -0
  252. package/dist/util/duration.d.ts +8 -0
  253. package/dist/util/duration.js +29 -0
  254. package/dist/util/websocket.d.ts +9 -0
  255. package/dist/util/websocket.js +31 -0
  256. package/dist/voice/connection.d.ts +90 -0
  257. package/dist/voice/connection.js +494 -0
  258. package/dist/voice/dave.d.ts +109 -0
  259. package/dist/voice/dave.js +245 -0
  260. package/dist/voice/opus.d.ts +19 -0
  261. package/dist/voice/opus.js +273 -0
  262. package/dist/voice/session.d.ts +55 -0
  263. package/dist/voice/session.js +405 -0
  264. package/dist/voice/source.d.ts +22 -0
  265. package/dist/voice/source.js +195 -0
  266. package/dist/voice/transport.d.ts +43 -0
  267. package/dist/voice/transport.js +140 -0
  268. package/dist/voice/usage.d.ts +4 -0
  269. package/dist/voice/usage.js +33 -0
  270. package/package.json +26 -7
  271. package/vendor/dave/LICENSE +231 -0
  272. package/vendor/dave/libdave.d.mts +99 -0
  273. package/vendor/dave/libdave.mjs +2 -0
  274. package/vendor/dave/libdave.wasm +0 -0
  275. package/dist/components.d.ts +0 -97
  276. package/dist/components.js +0 -260
  277. package/dist/context.d.ts +0 -72
  278. package/dist/context.js +0 -273
  279. package/dist/discord/client.d.ts +0 -35
  280. package/dist/discord/client.js +0 -66
  281. package/dist/discord/constants.d.ts +0 -49
  282. package/dist/discord/constants.js +0 -61
  283. package/dist/discord/events.d.ts +0 -129
  284. package/dist/discord/events.js +0 -102
  285. package/dist/discord/gateway.d.ts +0 -61
  286. package/dist/discord/gateway.js +0 -320
  287. package/dist/discord/payloads.d.ts +0 -275
  288. package/dist/discord/payloads.js +0 -3
  289. package/dist/discord/presence.d.ts +0 -42
  290. package/dist/discord/presence.js +0 -51
  291. package/dist/discord/rest.d.ts +0 -49
  292. package/dist/discord/rest.js +0 -271
  293. package/dist/discord/types.d.ts +0 -334
  294. package/dist/discord/types.js +0 -4
  295. package/dist/events.d.ts +0 -94
  296. package/dist/events.js +0 -255
  297. package/dist/presence.d.ts +0 -92
  298. package/dist/presence.js +0 -250
  299. package/dist/structures/Channel.d.ts +0 -110
  300. package/dist/structures/Channel.js +0 -171
  301. package/dist/structures/Guild.d.ts +0 -70
  302. package/dist/structures/Guild.js +0 -105
  303. package/dist/structures/Interaction.d.ts +0 -113
  304. package/dist/structures/Interaction.js +0 -274
  305. package/dist/structures/Member.d.ts +0 -70
  306. package/dist/structures/Member.js +0 -124
  307. package/dist/structures/Message.d.ts +0 -87
  308. package/dist/structures/Message.js +0 -182
  309. package/dist/structures/Reaction.d.ts +0 -38
  310. package/dist/structures/Reaction.js +0 -64
  311. package/dist/structures/Role.d.ts +0 -43
  312. package/dist/structures/Role.js +0 -77
  313. package/dist/structures/User.d.ts +0 -41
  314. package/dist/structures/User.js +0 -79
  315. package/dist/structures/utils.d.ts +0 -72
  316. package/dist/structures/utils.js +0 -59
  317. package/dist/utils/errors.d.ts +0 -5
  318. package/dist/utils/errors.js +0 -28
  319. package/dist/utils/modules.d.ts +0 -39
  320. package/dist/utils/modules.js +0 -156
@@ -0,0 +1,279 @@
1
+ // The pieces of a message that have no action of their own, written where
2
+ // the message is: rows, link and premium buttons, texts, sections,
3
+ // thumbnails, galleries, files, separators and containers. Each is checked
4
+ // against the limits of Discord when it is made.
5
+ // https://docs.discord.com/developers/components/reference
6
+ import { Limits } from '../discord/api.js';
7
+ import { ButtonStyle, ComponentType, } from '../discord/types/component.js';
8
+ import { emojiOf, piece, compose, renderedOf, } from './instance.js';
9
+ const fail = (message) => {
10
+ throw new TypeError(message);
11
+ };
12
+ function checkText(what, value, max, { required = true } = {}) {
13
+ if (value === undefined) {
14
+ return required ? fail(`${what} is missing.`) : undefined;
15
+ }
16
+ if (typeof value !== 'string') {
17
+ return fail(`${what} is a text, got ${typeof value}.`);
18
+ }
19
+ if (value.trim() === '')
20
+ return fail(`${what} is empty.`);
21
+ if (value.length > max) {
22
+ return fail(`${what} is ${value.length} characters long: Discord accepts ${max} at most.`);
23
+ }
24
+ return value;
25
+ }
26
+ /**
27
+ * A button that opens a link. Nothing runs in your bot when it is clicked:
28
+ * Discord opens the address.
29
+ * @see https://docs.discord.com/developers/components/reference#button
30
+ */
31
+ export function linkButton(options) {
32
+ if (typeof options !== 'object' || options === null) {
33
+ fail(`linkButton() takes an object like { label: 'Docs', url: '...' }.`);
34
+ }
35
+ const url = checkText('The url of a link button', options.url, Limits.ButtonUrl);
36
+ if (!/^https?:\/\/|^discord:\/\//.test(url)) {
37
+ fail(`The url of a link button must start with https:// (got ${JSON.stringify(url)}).`);
38
+ }
39
+ const label = checkText('The label of a link button', options.label, Limits.ButtonLabel, { required: false });
40
+ if (label === undefined && options.emoji === undefined) {
41
+ fail('A link button needs a label or an emoji.');
42
+ }
43
+ const raw = {
44
+ type: ComponentType.Button,
45
+ style: ButtonStyle.Link,
46
+ url,
47
+ };
48
+ if (label !== undefined)
49
+ raw.label = label;
50
+ if (options.emoji !== undefined)
51
+ raw.emoji = emojiOf(options.emoji, 'a link button');
52
+ if (options.disabled)
53
+ raw.disabled = true;
54
+ return piece('button', raw);
55
+ }
56
+ /**
57
+ * A button that offers something to buy: Discord shows the name and the
58
+ * price of the SKU itself.
59
+ * @see https://docs.discord.com/developers/components/reference#button
60
+ */
61
+ export function premiumButton(skuId, { disabled = false } = {}) {
62
+ if (typeof skuId !== 'string' || !/^\d+$/.test(skuId)) {
63
+ fail(`premiumButton() takes the id of a SKU, got ${JSON.stringify(skuId)}.`);
64
+ }
65
+ const raw = {
66
+ type: ComponentType.Button,
67
+ style: ButtonStyle.Premium,
68
+ sku_id: skuId,
69
+ };
70
+ if (disabled)
71
+ raw.disabled = true;
72
+ return piece('button', raw);
73
+ }
74
+ /**
75
+ * A row of components: up to 5 buttons, or one select menu. The framework
76
+ * makes rows by itself; use this to choose which buttons share a row.
77
+ * @see https://docs.discord.com/developers/components/reference#action-row
78
+ */
79
+ export function row(components) {
80
+ if (!Array.isArray(components) || components.length === 0) {
81
+ fail('row() takes a list of buttons, or one select menu.');
82
+ }
83
+ const rendered = components.map((component, index) => renderedOf(component, `Component ${index + 1} of a row`));
84
+ if (rendered.some(one => one.kind === 'select')) {
85
+ if (rendered.length > 1) {
86
+ fail('A select menu takes a whole row: put it in a row of its own.');
87
+ }
88
+ }
89
+ else if (rendered.some(one => one.kind !== 'button')) {
90
+ fail('A row only holds buttons, or one select menu.');
91
+ }
92
+ else if (rendered.length > Limits.ActionRowButtons) {
93
+ fail(`A row holds ${Limits.ActionRowButtons} buttons at most, got ${rendered.length}.`);
94
+ }
95
+ return piece('row', compose(rendered, raws => ({
96
+ type: ComponentType.ActionRow,
97
+ components: raws,
98
+ })));
99
+ }
100
+ /**
101
+ * A text in a message built with components. Markdown works as in any
102
+ * message, mentions included. The texts of a message add up to 4000
103
+ * characters at most.
104
+ * @see https://docs.discord.com/developers/components/reference#text-display
105
+ */
106
+ export function text(content) {
107
+ return piece('text', {
108
+ type: ComponentType.TextDisplay,
109
+ content: checkText('A text', content, Limits.TextDisplayTotal),
110
+ });
111
+ }
112
+ function mediaOf(input, what) {
113
+ const options = typeof input === 'string' ? { url: input } : input;
114
+ if (typeof options !== 'object' || options === null) {
115
+ fail(`${what} is an address, or an object like { url: '...' }.`);
116
+ }
117
+ const url = checkText(`The url of ${what}`, options.url, 2048);
118
+ const item = { media: { url } };
119
+ const description = checkText(`The description of ${what}`, options.description, Limits.MediaDescription, { required: false });
120
+ if (description !== undefined)
121
+ item.description = description;
122
+ if (options.spoiler)
123
+ item.spoiler = true;
124
+ return item;
125
+ }
126
+ /**
127
+ * A small picture beside the texts of a section.
128
+ * @see https://docs.discord.com/developers/components/reference#thumbnail
129
+ */
130
+ export function thumbnail(media) {
131
+ const item = mediaOf(media, 'a thumbnail');
132
+ const raw = {
133
+ type: ComponentType.Thumbnail,
134
+ media: item.media,
135
+ };
136
+ if (item.description !== undefined)
137
+ raw.description = item.description;
138
+ if (item.spoiler)
139
+ raw.spoiler = true;
140
+ return piece('thumbnail', raw);
141
+ }
142
+ /**
143
+ * One to three texts with a button or a thumbnail beside them.
144
+ * @see https://docs.discord.com/developers/components/reference#section
145
+ */
146
+ export function section(texts, accessory) {
147
+ const list = typeof texts === 'string' ? [texts] : texts;
148
+ if (!Array.isArray(list) ||
149
+ list.length === 0 ||
150
+ list.length > Limits.SectionTexts) {
151
+ fail(`A section has 1 to ${Limits.SectionTexts} texts, got ${Array.isArray(list) ? list.length : typeof list}.`);
152
+ }
153
+ const side = renderedOf(accessory, 'The accessory of a section');
154
+ if (side.kind !== 'button' && side.kind !== 'thumbnail') {
155
+ fail(`Beside a section goes a button or a thumbnail(), got a ${side.kind}.`);
156
+ }
157
+ const displays = list.map((content, index) => ({
158
+ type: ComponentType.TextDisplay,
159
+ content: checkText(`Text ${index + 1} of a section`, content, Limits.TextDisplayTotal),
160
+ }));
161
+ return piece('section', compose([side], ([accessory]) => ({
162
+ type: ComponentType.Section,
163
+ components: displays,
164
+ accessory: accessory,
165
+ })));
166
+ }
167
+ /**
168
+ * Up to 10 pictures or videos, shown in a grid.
169
+ * @see https://docs.discord.com/developers/components/reference#media-gallery
170
+ */
171
+ export function gallery(items) {
172
+ if (!Array.isArray(items) ||
173
+ items.length === 0 ||
174
+ items.length > Limits.MediaGalleryItems) {
175
+ fail(`A gallery has 1 to ${Limits.MediaGalleryItems} items, got ${Array.isArray(items) ? items.length : typeof items}.`);
176
+ }
177
+ return piece('gallery', {
178
+ type: ComponentType.MediaGallery,
179
+ items: items.map((item, index) => mediaOf(item, `item ${index + 1} of a gallery`)),
180
+ });
181
+ }
182
+ /**
183
+ * A file sent with the message (its `files`), shown where it is placed.
184
+ * @see https://docs.discord.com/developers/components/reference#file
185
+ */
186
+ export function file(name, { spoiler = false } = {}) {
187
+ const fileName = checkText('The name of a file component', name, 1024).replace(/^attachment:\/\//, '');
188
+ const raw = {
189
+ type: ComponentType.File,
190
+ file: { url: `attachment://${fileName}` },
191
+ ...(spoiler ? { spoiler: true } : {}),
192
+ };
193
+ return piece('file', raw);
194
+ }
195
+ /**
196
+ * Space between two pieces, with a line by default.
197
+ * @see https://docs.discord.com/developers/components/reference#separator
198
+ */
199
+ export function separator({ divider = true, spacing = 'small', } = {}) {
200
+ if (spacing !== 'small' && spacing !== 'large') {
201
+ fail(`The spacing of a separator is 'small' or 'large', got ${JSON.stringify(spacing)}.`);
202
+ }
203
+ return piece('separator', {
204
+ type: ComponentType.Separator,
205
+ ...(divider ? {} : { divider: false }),
206
+ ...(spacing === 'large' ? { spacing: 2 } : {}),
207
+ });
208
+ }
209
+ /**
210
+ * A box around texts, sections, rows, galleries, files and separators,
211
+ * with an optional color on its left, like an embed.
212
+ * @see https://docs.discord.com/developers/components/reference#container
213
+ */
214
+ export function container(children, { color, spoiler = false } = {}) {
215
+ if (!Array.isArray(children) || children.length === 0) {
216
+ fail('container() takes a list of components to put in the box.');
217
+ }
218
+ if (color !== undefined &&
219
+ (!Number.isInteger(color) || color < 0 || color > 0xffffff)) {
220
+ fail(`The color of a container is a number between 0 and 0xFFFFFF, got ${String(color)}.`);
221
+ }
222
+ const components = autoRows(children.map((child, index) => renderedOf(child, `Component ${index + 1} of a container`)), 'a container');
223
+ for (const child of components) {
224
+ if (child.kind === 'container' || child.kind === 'thumbnail') {
225
+ fail(`A container can't hold a ${child.kind}: it holds texts, sections, rows, galleries, files and separators.`);
226
+ }
227
+ }
228
+ return piece('container', compose(components, raws => ({
229
+ type: ComponentType.Container,
230
+ components: raws,
231
+ ...(color !== undefined ? { accent_color: color } : {}),
232
+ ...(spoiler ? { spoiler: true } : {}),
233
+ })));
234
+ }
235
+ /**
236
+ * Puts the buttons and menus of a list in rows: buttons that follow each
237
+ * other share a row of 5 at most, a menu gets its own. Rows and layout
238
+ * pieces are kept as they are.
239
+ */
240
+ export function autoRows(pieces, where) {
241
+ const result = [];
242
+ let buttons = [];
243
+ const asRow = (children) => ({
244
+ kind: 'row',
245
+ raw: compose(children, raws => ({
246
+ type: ComponentType.ActionRow,
247
+ components: raws,
248
+ })),
249
+ });
250
+ const flush = () => {
251
+ if (buttons.length === 0)
252
+ return;
253
+ result.push(asRow(buttons));
254
+ buttons = [];
255
+ };
256
+ for (const one of pieces) {
257
+ if (one.kind === 'button') {
258
+ buttons.push(one);
259
+ if (buttons.length === Limits.ActionRowButtons)
260
+ flush();
261
+ }
262
+ else if (one.kind === 'select') {
263
+ flush();
264
+ result.push(asRow([one]));
265
+ }
266
+ else if (one.kind === 'thumbnail') {
267
+ fail(`A thumbnail only goes beside a section: section(['...'], thumbnail(...)). It can't be in ${where} on its own.`);
268
+ }
269
+ else if (one.kind === 'modal') {
270
+ fail(`A form is not part of ${where}: open it with interaction.showModal().`);
271
+ }
272
+ else {
273
+ flush();
274
+ result.push(one);
275
+ }
276
+ }
277
+ flush();
278
+ return result;
279
+ }
@@ -0,0 +1,244 @@
1
+ import type { ChannelType } from '../discord/types/channel.js';
2
+ import type { Channel } from '../structures/channel.js';
3
+ import type { ModalInteraction } from '../structures/interaction.js';
4
+ import type { Attachment, Message } from '../structures/message.js';
5
+ import type { Role } from '../structures/role.js';
6
+ import type { User } from '../structures/user.js';
7
+ import type { TranslationContext } from '../messages/messages.js';
8
+ import { type ComponentWhere, type DynamicText, type PlaceOf } from './component.js';
9
+ import { type DataShape, type DataInputOf, type DataValuesOf } from './custom-id.js';
10
+ import type { ModalComponent, Rendered } from './instance.js';
11
+ import { type LoadedOption, type OptionValue, type SelectOptions } from './options.js';
12
+ /** What every field of a form has: how it is named for the person. */
13
+ interface FieldBase {
14
+ /**
15
+ * The name of the field, above it (45 characters at most): written as
16
+ * is, or a function of `t` and the data, run when the form opens.
17
+ */
18
+ label: DynamicText;
19
+ /** A help text under the label (100 characters at most): a text, or a function of `t` and the data. */
20
+ description?: DynamicText;
21
+ }
22
+ /** A text the person types. */
23
+ export interface TextField extends FieldBase {
24
+ /** The kind of field: a text. */
25
+ type: 'text';
26
+ /** One line (`'short'`, the default) or several (`'paragraph'`). */
27
+ style?: 'short' | 'paragraph';
28
+ /** The text shown while the field is empty (100 characters at most): a text, or a function of `t` and the data. */
29
+ placeholder?: DynamicText;
30
+ /** Whether the person must fill it in. `true` by default. */
31
+ required?: boolean;
32
+ /** The shortest text accepted, in characters (0 to 4000). */
33
+ minLength?: number;
34
+ /** The longest text accepted, in characters (1 to 4000). */
35
+ maxLength?: number;
36
+ /** What the field holds when the form opens (4000 characters at most). */
37
+ value?: string;
38
+ }
39
+ /** A menu of texts you declare. */
40
+ export interface SelectField<Options extends SelectOptions = SelectOptions> extends FieldBase {
41
+ /** The kind of field: a menu of texts. */
42
+ type: 'select';
43
+ /** The options (25 at most), written like the options of a select menu. */
44
+ options: Options;
45
+ /** The text shown when nothing is picked (150 characters at most): a text, or a function of `t` and the data. */
46
+ placeholder?: DynamicText;
47
+ /** How many options must be picked at least (0 to 25). 1 by default. */
48
+ min?: number;
49
+ /** How many options can be picked at most (1 to 25). 1 by default. */
50
+ max?: number;
51
+ /** Whether something must be picked. `true` by default. */
52
+ required?: boolean;
53
+ }
54
+ /** A menu of users, roles, users and roles, or channels of the server. */
55
+ export interface EntityField extends FieldBase {
56
+ /** The kind of field: what Discord lists. */
57
+ type: 'user' | 'role' | 'mentionable' | 'channel';
58
+ /** The text shown when nothing is picked (150 characters at most): a text, or a function of `t` and the data. */
59
+ placeholder?: DynamicText;
60
+ /** How many things must be picked at least (0 to 25). 1 by default. */
61
+ min?: number;
62
+ /** How many things can be picked at most (1 to 25). 1 by default. */
63
+ max?: number;
64
+ /** Whether something must be picked. `true` by default. */
65
+ required?: boolean;
66
+ /** For a menu of channels: the kinds of channels listed. All by default. */
67
+ channelTypes?: readonly ChannelType[];
68
+ }
69
+ /** Files the person uploads. */
70
+ export interface FilesField extends FieldBase {
71
+ /** The kind of field: files to upload. */
72
+ type: 'files';
73
+ /** How many files must be uploaded at least (0 to 10). 1 by default. */
74
+ min?: number;
75
+ /** How many files can be uploaded at most (1 to 10). 1 by default. */
76
+ max?: number;
77
+ /** Whether files must be uploaded. `true` by default. */
78
+ required?: boolean;
79
+ /** The kinds of files accepted: `'image'`, `'video'`, `'audio'`, or extensions like `'.pdf'` (10 at most). */
80
+ fileTypes?: readonly string[];
81
+ }
82
+ /** One choice among a few, shown as radio buttons. */
83
+ export interface RadioField<Options extends SelectOptions = SelectOptions> extends FieldBase {
84
+ /** The kind of field: one choice among 2 to 10. */
85
+ type: 'radio';
86
+ /** The choices (2 to 10), written like the options of a select menu. */
87
+ options: Options;
88
+ /** Whether a choice must be made. `true` by default. */
89
+ required?: boolean;
90
+ }
91
+ /** Several choices among a few, shown as checkboxes. */
92
+ export interface CheckboxesField<Options extends SelectOptions = SelectOptions> extends FieldBase {
93
+ /** The kind of field: several choices among 1 to 10. */
94
+ type: 'checkboxes';
95
+ /** The choices (1 to 10), written like the options of a select menu. */
96
+ options: Options;
97
+ /** How many must be checked at least (0 to 10). 1 by default. */
98
+ min?: number;
99
+ /** How many can be checked at most (1 to 10). All by default. */
100
+ max?: number;
101
+ /** Whether something must be checked. `true` by default. */
102
+ required?: boolean;
103
+ }
104
+ /** One checkbox: yes or no. */
105
+ export interface CheckboxField extends FieldBase {
106
+ /** The kind of field: one checkbox. */
107
+ type: 'checkbox';
108
+ /** Whether it is checked when the form opens. */
109
+ default?: boolean;
110
+ }
111
+ /** A text shown in the form, which nobody fills in. */
112
+ export interface NoteField {
113
+ /** The kind of field: a text to read. */
114
+ type: 'note';
115
+ /** The text, with Markdown: written as is, or a function of `t` and the data. */
116
+ content: DynamicText;
117
+ }
118
+ /** One field of a form. */
119
+ export type ModalField = TextField | SelectField | EntityField | FilesField | RadioField | CheckboxesField | CheckboxField | NoteField;
120
+ /** The fields of a form, by name (5 at most). */
121
+ export type ModalFields = Readonly<Record<string, ModalField>>;
122
+ /** What `run` receives for one picked thing of a menu of the server. */
123
+ interface EntityValues {
124
+ user: User;
125
+ role: Role;
126
+ mentionable: User | Role;
127
+ channel: Channel;
128
+ }
129
+ /** What `run` receives for a field, from its declaration. */
130
+ export type FieldValue<Field extends ModalField> = Field extends TextField ? Field extends {
131
+ required: false;
132
+ } ? string | undefined : string : Field extends SelectField<infer Options> ? OptionValue<Options>[] : Field extends EntityField ? EntityValues[Field['type']][] : Field extends FilesField ? Attachment[] : Field extends RadioField<infer Options> ? Field extends {
133
+ required: false;
134
+ } ? OptionValue<Options> | undefined : OptionValue<Options> : Field extends CheckboxesField<infer Options> ? OptionValue<Options>[] : Field extends CheckboxField ? boolean : never;
135
+ /** What the person filled in, typed field by field. Notes are not there. */
136
+ export type FieldValuesOf<Fields extends ModalFields> = {
137
+ [Name in keyof Fields as Fields[Name] extends NoteField ? never : Name]: FieldValue<Fields[Name]>;
138
+ };
139
+ /** What a form can hold when it opens: a value for some of its fields. */
140
+ export type ModalPrefill<Fields extends ModalFields> = {
141
+ readonly [Name in keyof Fields as Fields[Name] extends TextField | SelectField | RadioField | CheckboxesField | CheckboxField ? Name : never]?: Fields[Name] extends TextField ? string : Fields[Name] extends SelectField<infer Options> | CheckboxesField<infer Options> ? readonly OptionValue<Options>[] : Fields[Name] extends RadioField<infer Options> ? OptionValue<Options> : boolean;
142
+ };
143
+ /** What `run` receives when the form is sent. */
144
+ export type ModalContext<Fields extends ModalFields = ModalFields, Data extends DataShape = {}, Where extends ComponentWhere = 'guild'> = {
145
+ /** Who sent the form. */
146
+ user: User;
147
+ /** What the person filled in. */
148
+ fields: FieldValuesOf<Fields>;
149
+ /** What the form carries, as declared in `data`. */
150
+ data: DataValuesOf<Data>;
151
+ /** The message whose button or menu opened the form; `null` when a command did. */
152
+ message: Message | null;
153
+ } & TranslationContext & PlaceOf<Where, ModalInteraction>;
154
+ /** What a modal file gives to `modal()`. */
155
+ export interface ModalConfig<Fields extends ModalFields = ModalFields, Data extends DataShape = {}, Where extends ComponentWhere = 'guild'> {
156
+ /**
157
+ * The title of the form (45 characters at most): written as is, or a
158
+ * function of `t` and the data, run when the form opens.
159
+ */
160
+ title: DynamicText;
161
+ /** The fields, by name (1 to 5). `run` receives them under the same names. */
162
+ fields: Fields;
163
+ /**
164
+ * What the form carries from the code that opens it to the code that
165
+ * runs when it is sent: a name and a kind for each value. Given when the
166
+ * form is opened, received typed in `run`.
167
+ */
168
+ data?: Data;
169
+ /**
170
+ * Where the form can be sent from: `'guild'` (servers, the default),
171
+ * `'dm'` (private messages with the bot) or `'both'`. What `run`
172
+ * receives follows it.
173
+ */
174
+ where?: Where;
175
+ /**
176
+ * Make the answers of the form only visible to who sent it. When a
177
+ * component of a message only the person sees opened it, they already are.
178
+ */
179
+ ephemeral?: boolean;
180
+ /** What to do when someone sends the form. */
181
+ run: (context: ModalContext<Fields, Data, Where>) => unknown;
182
+ }
183
+ /** What a form is opened with: its data, and what some fields hold. */
184
+ export interface ModalInstanceOptions<Fields extends ModalFields = ModalFields> {
185
+ /** What some fields hold when the form opens. */
186
+ values?: ModalPrefill<Fields>;
187
+ }
188
+ /**
189
+ * What `modal()` returns: the default export of a modal file. Import it
190
+ * where someone uses a command or a component, and open it:
191
+ * `interaction.showModal(report({ userId }))`, or
192
+ * `interaction.showModal(feedback)` for a form without data.
193
+ */
194
+ export type ModalFile<Fields extends ModalFields = ModalFields, Data extends DataShape = {}> = {
195
+ /** What the file gave to `modal()`, not checked yet. */
196
+ readonly config: unknown;
197
+ } & ({} extends Data ? ((options?: ModalInstanceOptions<Fields>) => ModalComponent) & ModalComponent : {} extends DataInputOf<Data> ? ((data?: DataInputOf<Data>, options?: ModalInstanceOptions<Fields>) => ModalComponent) & ModalComponent : (data: DataInputOf<Data>, options?: ModalInstanceOptions<Fields>) => ModalComponent);
198
+ /**
199
+ * Declares a form. Export the result as the default export of a file of
200
+ * `src/components/modals/`: the path of the file is what tells the form
201
+ * apart, so you never write an id.
202
+ *
203
+ * ```ts
204
+ * import { modal } from 'chapterjs';
205
+ *
206
+ * export default modal({
207
+ * title: 'Feedback',
208
+ * fields: {
209
+ * text: { type: 'text', label: 'What do you think?', style: 'paragraph' },
210
+ * },
211
+ * async run({ interaction, fields }) {
212
+ * await interaction.reply({ content: `Thanks! You wrote: ${fields.text}`, ephemeral: true });
213
+ * },
214
+ * });
215
+ * ```
216
+ */
217
+ export declare function modal<const Fields extends ModalFields, const Data extends DataShape = {}, const Where extends ComponentWhere = 'guild'>(config: ModalConfig<Fields, Data, Where>): ModalFile<Fields, Data>;
218
+ /** A field of a form, checked, with how it is sent. */
219
+ export interface LoadedField {
220
+ readonly name: string;
221
+ readonly field: ModalField;
222
+ /** The options of a menu, radio or checkboxes, their texts maybe computed when sent. */
223
+ readonly options: readonly LoadedOption[];
224
+ readonly required: boolean;
225
+ readonly min: number;
226
+ readonly max: number;
227
+ }
228
+ /** A modal file, checked. */
229
+ export interface LoadedModal {
230
+ readonly kind: 'modal';
231
+ readonly path: string;
232
+ /** The title (45 characters at most): a text, or computed when the form opens. */
233
+ readonly title: DynamicText;
234
+ readonly fields: readonly LoadedField[];
235
+ readonly data: DataShape;
236
+ readonly where: ComponentWhere;
237
+ readonly ephemeral: boolean;
238
+ readonly run: (context: ModalContext<ModalFields, DataShape, ComponentWhere>) => unknown;
239
+ }
240
+ /** Checks one field of a form. */
241
+ export declare function readField(name: string, given: unknown): LoadedField;
242
+ /** The form as Discord takes it, for the data and the values given. */
243
+ export declare function renderModal(loaded: LoadedModal, args: readonly unknown[]): Rendered<'modal'>;
244
+ export {};