chapterjs 0.1.2 → 0.2.0

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 (318) 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 +205 -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 +253 -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 +135 -0
  25. package/dist/cli/project.js +517 -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 +360 -0
  31. package/dist/commands/command.js +28 -0
  32. package/dist/commands/declaration.d.ts +77 -0
  33. package/dist/commands/declaration.js +492 -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 +47 -0
  39. package/dist/commands/tree.js +216 -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 +106 -0
  44. package/dist/components/component.d.ts +140 -0
  45. package/dist/components/component.js +39 -0
  46. package/dist/components/custom-id.d.ts +72 -0
  47. package/dist/components/custom-id.js +128 -0
  48. package/dist/components/declaration.d.ts +42 -0
  49. package/dist/components/declaration.js +342 -0
  50. package/dist/components/declared.d.ts +29 -0
  51. package/dist/components/declared.js +49 -0
  52. package/dist/components/embed.d.ts +29 -0
  53. package/dist/components/embed.js +11 -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 +245 -0
  59. package/dist/components/modal.js +398 -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 +19 -0
  65. package/dist/components/router.js +251 -0
  66. package/dist/components/select.d.ts +146 -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/declaration.d.ts +16 -0
  155. package/dist/events/declaration.js +80 -0
  156. package/dist/events/event.d.ts +45 -0
  157. package/dist/events/event.js +34 -0
  158. package/dist/events/registry.d.ts +697 -0
  159. package/dist/events/registry.js +839 -0
  160. package/dist/events/router.d.ts +46 -0
  161. package/dist/events/router.js +133 -0
  162. package/dist/gateway/errors.d.ts +18 -0
  163. package/dist/gateway/errors.js +47 -0
  164. package/dist/gateway/gateway.d.ts +64 -0
  165. package/dist/gateway/gateway.js +117 -0
  166. package/dist/gateway/identify-queue.d.ts +25 -0
  167. package/dist/gateway/identify-queue.js +29 -0
  168. package/dist/gateway/shard.d.ts +94 -0
  169. package/dist/gateway/shard.js +414 -0
  170. package/dist/gateway/state.d.ts +11 -0
  171. package/dist/gateway/state.js +184 -0
  172. package/dist/index.d.ts +57 -18
  173. package/dist/index.js +40 -12
  174. package/dist/interactions/cooldown.d.ts +33 -0
  175. package/dist/interactions/cooldown.js +74 -0
  176. package/dist/interactions/dispatch.d.ts +91 -0
  177. package/dist/interactions/dispatch.js +110 -0
  178. package/dist/loader/generated.d.ts +10 -0
  179. package/dist/loader/generated.js +80 -0
  180. package/dist/loader/hot.d.ts +13 -0
  181. package/dist/loader/hot.js +85 -0
  182. package/dist/loader/loader.d.ts +85 -0
  183. package/dist/loader/loader.js +119 -0
  184. package/dist/loader/locate.d.ts +10 -0
  185. package/dist/loader/locate.js +36 -0
  186. package/dist/loader/watch.d.ts +17 -0
  187. package/dist/loader/watch.js +66 -0
  188. package/dist/main.d.ts +2 -0
  189. package/dist/main.js +5 -0
  190. package/dist/messages/commands.d.ts +23 -0
  191. package/dist/messages/commands.js +142 -0
  192. package/dist/messages/declaration.d.ts +89 -0
  193. package/dist/messages/declaration.js +455 -0
  194. package/dist/messages/messages.d.ts +241 -0
  195. package/dist/messages/messages.js +31 -0
  196. package/dist/messages/phrases.d.ts +23 -0
  197. package/dist/messages/phrases.js +44 -0
  198. package/dist/messages/translate.d.ts +100 -0
  199. package/dist/messages/translate.js +249 -0
  200. package/dist/presence/declaration.d.ts +19 -0
  201. package/dist/presence/declaration.js +118 -0
  202. package/dist/presence/presence.d.ts +57 -0
  203. package/dist/presence/presence.js +27 -0
  204. package/dist/rest/errors.d.ts +55 -0
  205. package/dist/rest/errors.js +92 -0
  206. package/dist/rest/rate-limiter.d.ts +46 -0
  207. package/dist/rest/rate-limiter.js +153 -0
  208. package/dist/rest/rest.d.ts +63 -0
  209. package/dist/rest/rest.js +194 -0
  210. package/dist/structures/base.d.ts +48 -0
  211. package/dist/structures/base.js +84 -0
  212. package/dist/structures/channel.d.ts +380 -0
  213. package/dist/structures/channel.js +627 -0
  214. package/dist/structures/context.d.ts +30 -0
  215. package/dist/structures/context.js +7 -0
  216. package/dist/structures/emoji.d.ts +46 -0
  217. package/dist/structures/emoji.js +65 -0
  218. package/dist/structures/entities.d.ts +62 -0
  219. package/dist/structures/entities.js +226 -0
  220. package/dist/structures/guild.d.ts +348 -0
  221. package/dist/structures/guild.js +466 -0
  222. package/dist/structures/interaction.d.ts +197 -0
  223. package/dist/structures/interaction.js +396 -0
  224. package/dist/structures/invite.d.ts +38 -0
  225. package/dist/structures/invite.js +57 -0
  226. package/dist/structures/known.d.ts +28 -0
  227. package/dist/structures/known.js +58 -0
  228. package/dist/structures/member.d.ts +140 -0
  229. package/dist/structures/member.js +260 -0
  230. package/dist/structures/message.d.ts +197 -0
  231. package/dist/structures/message.js +268 -0
  232. package/dist/structures/payload.d.ts +91 -0
  233. package/dist/structures/payload.js +184 -0
  234. package/dist/structures/role.d.ts +69 -0
  235. package/dist/structures/role.js +112 -0
  236. package/dist/structures/user.d.ts +59 -0
  237. package/dist/structures/user.js +94 -0
  238. package/dist/structures/webhook.d.ts +46 -0
  239. package/dist/structures/webhook.js +79 -0
  240. package/dist/tasks/declaration.d.ts +16 -0
  241. package/dist/tasks/declaration.js +56 -0
  242. package/dist/tasks/schedule.d.ts +54 -0
  243. package/dist/tasks/schedule.js +236 -0
  244. package/dist/tasks/scheduler.d.ts +35 -0
  245. package/dist/tasks/scheduler.js +107 -0
  246. package/dist/tasks/task.d.ts +75 -0
  247. package/dist/tasks/task.js +27 -0
  248. package/dist/util/case.d.ts +13 -0
  249. package/dist/util/case.js +54 -0
  250. package/dist/util/duration.d.ts +8 -0
  251. package/dist/util/duration.js +29 -0
  252. package/dist/util/websocket.d.ts +9 -0
  253. package/dist/util/websocket.js +31 -0
  254. package/dist/voice/connection.d.ts +90 -0
  255. package/dist/voice/connection.js +494 -0
  256. package/dist/voice/dave.d.ts +109 -0
  257. package/dist/voice/dave.js +245 -0
  258. package/dist/voice/opus.d.ts +19 -0
  259. package/dist/voice/opus.js +273 -0
  260. package/dist/voice/session.d.ts +55 -0
  261. package/dist/voice/session.js +405 -0
  262. package/dist/voice/source.d.ts +22 -0
  263. package/dist/voice/source.js +195 -0
  264. package/dist/voice/transport.d.ts +43 -0
  265. package/dist/voice/transport.js +140 -0
  266. package/dist/voice/usage.d.ts +4 -0
  267. package/dist/voice/usage.js +33 -0
  268. package/package.json +26 -7
  269. package/vendor/dave/LICENSE +231 -0
  270. package/vendor/dave/libdave.d.mts +99 -0
  271. package/vendor/dave/libdave.mjs +2 -0
  272. package/vendor/dave/libdave.wasm +0 -0
  273. package/dist/components.d.ts +0 -97
  274. package/dist/components.js +0 -260
  275. package/dist/context.d.ts +0 -72
  276. package/dist/context.js +0 -273
  277. package/dist/discord/client.d.ts +0 -35
  278. package/dist/discord/client.js +0 -66
  279. package/dist/discord/constants.d.ts +0 -49
  280. package/dist/discord/constants.js +0 -61
  281. package/dist/discord/events.d.ts +0 -129
  282. package/dist/discord/events.js +0 -102
  283. package/dist/discord/gateway.d.ts +0 -61
  284. package/dist/discord/gateway.js +0 -320
  285. package/dist/discord/payloads.d.ts +0 -275
  286. package/dist/discord/payloads.js +0 -3
  287. package/dist/discord/presence.d.ts +0 -42
  288. package/dist/discord/presence.js +0 -51
  289. package/dist/discord/rest.d.ts +0 -49
  290. package/dist/discord/rest.js +0 -271
  291. package/dist/discord/types.d.ts +0 -334
  292. package/dist/discord/types.js +0 -4
  293. package/dist/events.d.ts +0 -94
  294. package/dist/events.js +0 -255
  295. package/dist/presence.d.ts +0 -92
  296. package/dist/presence.js +0 -250
  297. package/dist/structures/Channel.d.ts +0 -110
  298. package/dist/structures/Channel.js +0 -171
  299. package/dist/structures/Guild.d.ts +0 -70
  300. package/dist/structures/Guild.js +0 -105
  301. package/dist/structures/Interaction.d.ts +0 -113
  302. package/dist/structures/Interaction.js +0 -274
  303. package/dist/structures/Member.d.ts +0 -70
  304. package/dist/structures/Member.js +0 -124
  305. package/dist/structures/Message.d.ts +0 -87
  306. package/dist/structures/Message.js +0 -182
  307. package/dist/structures/Reaction.d.ts +0 -38
  308. package/dist/structures/Reaction.js +0 -64
  309. package/dist/structures/Role.d.ts +0 -43
  310. package/dist/structures/Role.js +0 -77
  311. package/dist/structures/User.d.ts +0 -41
  312. package/dist/structures/User.js +0 -79
  313. package/dist/structures/utils.d.ts +0 -72
  314. package/dist/structures/utils.js +0 -59
  315. package/dist/utils/errors.d.ts +0 -5
  316. package/dist/utils/errors.js +0 -28
  317. package/dist/utils/modules.d.ts +0 -39
  318. package/dist/utils/modules.js +0 -156
@@ -0,0 +1,249 @@
1
+ // The runtime of `t`: which language to take, and a text with its
2
+ // placeholders filled in. No dependency on the loader or on Discord.
3
+ import { Locale } from '../discord/types/common.js';
4
+ /** The forms a plural can have, in the order of the docs. */
5
+ export const PLURAL_FORMS = [
6
+ 'zero',
7
+ 'one',
8
+ 'two',
9
+ 'few',
10
+ 'many',
11
+ 'other',
12
+ ];
13
+ /**
14
+ * Cuts a text around its placeholders, read from left to right: `{name}`
15
+ * is a placeholder, `{{` and `}}` are braces written as is, and a `{`
16
+ * with another `{` before its `}` is a brace too.
17
+ */
18
+ export function compile(text) {
19
+ const parts = [];
20
+ const params = [];
21
+ let literal = '';
22
+ let i = 0;
23
+ while (i < text.length) {
24
+ const char = text[i];
25
+ if (char === '{' && text[i + 1] === '{') {
26
+ literal += '{';
27
+ i += 2;
28
+ }
29
+ else if (char === '}' && text[i + 1] === '}') {
30
+ literal += '}';
31
+ i += 2;
32
+ }
33
+ else if (char === '{') {
34
+ const close = text.indexOf('}', i + 1);
35
+ const open = text.indexOf('{', i + 1);
36
+ if (close === -1 || (open !== -1 && open < close)) {
37
+ literal += '{';
38
+ i += 1;
39
+ }
40
+ else {
41
+ const name = text.slice(i + 1, close);
42
+ parts.push(literal, name);
43
+ if (!params.includes(name))
44
+ params.push(name);
45
+ literal = '';
46
+ i = close + 1;
47
+ }
48
+ }
49
+ else {
50
+ literal += char;
51
+ i += 1;
52
+ }
53
+ }
54
+ parts.push(literal);
55
+ return { text, parts, params };
56
+ }
57
+ /** Puts the forms of a plural together: `count` picks one when filled. */
58
+ export function compilePlural(forms) {
59
+ const params = ['count'];
60
+ for (const form of forms.values()) {
61
+ for (const name of form.params) {
62
+ if (!params.includes(name))
63
+ params.push(name);
64
+ }
65
+ }
66
+ return { forms, params };
67
+ }
68
+ /** A text of a language file, trusted, compiled. */
69
+ export function templateOf(text) {
70
+ if (typeof text === 'string')
71
+ return compile(text);
72
+ return compilePlural(new Map(Object.entries(text).map(([form, one]) => [
73
+ form,
74
+ compile(one),
75
+ ])));
76
+ }
77
+ /** Whether a template is a plural. */
78
+ export const isPlural = (template) => 'forms' in template;
79
+ /** Every locale Discord knows. */
80
+ export const LOCALES = new Set(Object.values(Locale));
81
+ /**
82
+ * The language of the file to use for one asked: the same one, else one
83
+ * of the same language in another region (`en-GB` → `en-US`, `es-419` →
84
+ * `es-ES`, `zh-TW` → `zh-CN`), else the default.
85
+ */
86
+ export function pickLocale(messages, locale) {
87
+ if (locale === null)
88
+ return messages.default;
89
+ if (messages.locales.has(locale))
90
+ return locale;
91
+ const language = locale.split('-')[0];
92
+ for (const known of messages.locales.keys()) {
93
+ if (known.split('-')[0] === language)
94
+ return known;
95
+ }
96
+ return messages.default;
97
+ }
98
+ const numberFormats = new Map();
99
+ const pluralRules = new Map();
100
+ /** How a language writes a number: `1,234` in English, `1 234` in French. */
101
+ function numberFormat(locale) {
102
+ let format = numberFormats.get(locale);
103
+ if (!format) {
104
+ try {
105
+ format = new Intl.NumberFormat(locale);
106
+ }
107
+ catch {
108
+ format = new Intl.NumberFormat('en-US');
109
+ }
110
+ numberFormats.set(locale, format);
111
+ }
112
+ return format;
113
+ }
114
+ /** Which form of a plural a language uses for a number. */
115
+ function pluralRule(locale) {
116
+ let rules = pluralRules.get(locale);
117
+ if (!rules) {
118
+ try {
119
+ rules = new Intl.PluralRules(locale);
120
+ }
121
+ catch {
122
+ rules = new Intl.PluralRules('en-US');
123
+ }
124
+ pluralRules.set(locale, rules);
125
+ }
126
+ return rules;
127
+ }
128
+ /** A value where a placeholder is: a number the way the language writes it. */
129
+ const show = (value, locale) => typeof value === 'number'
130
+ ? numberFormat(locale).format(value)
131
+ : String(value);
132
+ /**
133
+ * Fills the placeholders of a template, with an error naming a missing
134
+ * one. Numbers are written the way `locale` writes them, and a plural
135
+ * takes the form that language uses for `count`.
136
+ */
137
+ export function fill(key, template, params, locale = 'en-US') {
138
+ if (template.params.length === 0)
139
+ return template.text;
140
+ const missing = template.params.filter(name => params?.[name] === undefined);
141
+ if (missing.length > 0) {
142
+ throw new TypeError(`The message "${key}" needs ${missing.map(name => `{${name}}`).join(', ')}: t('${key}', { ${template.params.join(', ')} })`);
143
+ }
144
+ if (isPlural(template)) {
145
+ const count = params.count;
146
+ if (typeof count !== 'number') {
147
+ throw new TypeError(`The message "${key}" is a plural: its {count} is a number, got ${JSON.stringify(count)}.`);
148
+ }
149
+ const form = pluralRule(locale).select(count);
150
+ template = template.forms.get(form) ?? template.forms.get('other');
151
+ }
152
+ let text = '';
153
+ template.parts.forEach((part, index) => {
154
+ text += index % 2 === 0 ? part : show(params[part], locale);
155
+ });
156
+ return text;
157
+ }
158
+ /** The `t` of each language asked, made once per set of messages. */
159
+ const translators = new WeakMap();
160
+ /** `t` for one language of the file, the same object every time. */
161
+ export function translatorFor(messages, locale) {
162
+ let made = translators.get(messages);
163
+ if (!made)
164
+ translators.set(messages, (made = new Map()));
165
+ const asked = locale ?? '';
166
+ const known = made.get(asked);
167
+ if (known)
168
+ return known;
169
+ const picked = pickLocale(messages, locale);
170
+ const texts = messages.locales.get(picked);
171
+ // A text the language does not have is the default language's.
172
+ const base = messages.locales.get(messages.default);
173
+ const t = ((key, params) => {
174
+ const own = texts.get(key);
175
+ if (own)
176
+ return fill(key, own, params, picked);
177
+ const template = base.get(key);
178
+ if (!template) {
179
+ throw new TypeError(`There is no message "${key}" in your languages. They have: ${[...base.keys()].join(', ')}.`);
180
+ }
181
+ return fill(key, template, params, messages.default);
182
+ });
183
+ Object.defineProperties(t, {
184
+ locale: { value: picked, enumerable: true },
185
+ in: {
186
+ // `t.in('fr')` is a `t`, `t.in('fr', 'welcome', { name })` a text.
187
+ value: (other, key, params) => key === undefined
188
+ ? translatorFor(messages, other)
189
+ : translatorFor(messages, other)(key, params),
190
+ enumerable: false,
191
+ },
192
+ });
193
+ Object.freeze(t);
194
+ made.set(asked, t);
195
+ return t;
196
+ }
197
+ /**
198
+ * The language of those who will read what is sent: the person when they
199
+ * alone see it (an ephemeral answer, a private message), else the language
200
+ * of the server when Discord knows it (it only does for Community servers:
201
+ * elsewhere `preferred_locale` is always `en-US`), else none, which means
202
+ * the default language of the file.
203
+ * @see https://docs.discord.com/developers/resources/guild#guild-object-guild-structure
204
+ */
205
+ export function audienceLocale({ person, guild, ephemeral, }) {
206
+ if (ephemeral || !guild)
207
+ return person ?? null;
208
+ return guild.features.includes('COMMUNITY') ? guild.preferredLocale : null;
209
+ }
210
+ const missing = new Map();
211
+ /** A `t` for a project without a language: it says so when called. */
212
+ export function missingTranslator(locale) {
213
+ const shown = locale ?? 'en-US';
214
+ const known = missing.get(shown);
215
+ if (known)
216
+ return known;
217
+ const t = (() => {
218
+ throw new TypeError("This project has no language: declare one with language({ locale: 'en-US', texts: { ... } }) to use t().");
219
+ });
220
+ Object.defineProperties(t, {
221
+ locale: { value: shown, enumerable: true },
222
+ in: {
223
+ value: (_other, key) => key === undefined ? t : t(),
224
+ enumerable: false,
225
+ },
226
+ });
227
+ Object.freeze(t);
228
+ missing.set(shown, t);
229
+ return t;
230
+ }
231
+ /**
232
+ * `t` for what the bot sends, in the language of who will read it. Without
233
+ * a language, a `t` that explains it is missing when called: the
234
+ * texts of a component may still be functions of it.
235
+ */
236
+ export function translatorOf(ctx, audience) {
237
+ const locale = audienceLocale(audience);
238
+ return ctx.messages
239
+ ? translatorFor(ctx.messages, locale)
240
+ : missingTranslator(locale);
241
+ }
242
+ /**
243
+ * What a handler receives about translations: `t` in the language asked
244
+ * when the project has a language, nothing otherwise. Spread it
245
+ * into the context of any handler.
246
+ */
247
+ export function translation(ctx, locale) {
248
+ return ctx.messages ? { t: translatorFor(ctx.messages, locale) } : {};
249
+ }
@@ -0,0 +1,19 @@
1
+ import type { Declaration } from '../loader/loader.js';
2
+ import { type RawGatewayPresenceUpdate } from '../discord/types/gateway-events.js';
3
+ /** The presence, checked: what the gateway sends. */
4
+ export interface LoadedPresence {
5
+ /** The Update Presence payload (also sent with Identify). */
6
+ readonly raw: RawGatewayPresenceUpdate;
7
+ /** What tells one presence from another: the payload, as text. */
8
+ readonly key: string;
9
+ }
10
+ /**
11
+ * What a bot without a presence shows: online, doing nothing. Sent when
12
+ * the declaration is removed while the bot runs.
13
+ */
14
+ export declare const DEFAULT_PRESENCE: RawGatewayPresenceUpdate;
15
+ /**
16
+ * The presence of the bot, declared once anywhere in `src/` with
17
+ * `presence()` and exported.
18
+ */
19
+ export declare const presenceDeclaration: Declaration<LoadedPresence>;
@@ -0,0 +1,118 @@
1
+ // What a file declares with `presence()`: what the bot shows under its
2
+ // name. It is checked when it loads and turned into what the gateway
3
+ // sends, so a wrong status or type is explained before the bot connects.
4
+ import { ActivityType, } from '../discord/types/gateway-events.js';
5
+ import { isPresenceDeclaration } from './presence.js';
6
+ const fail = message => {
7
+ throw new TypeError(message);
8
+ };
9
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
10
+ const KEYS = ['status', 'activity'];
11
+ const ACTIVITY_KEYS = ['type', 'name', 'url'];
12
+ /**
13
+ * The statuses a file may give.
14
+ * @see https://docs.discord.com/developers/events/gateway-events#update-presence-status-types
15
+ */
16
+ const STATUSES = ['online', 'idle', 'dnd', 'invisible'];
17
+ /**
18
+ * The activity types, by the word a file writes.
19
+ * @see https://docs.discord.com/developers/events/gateway-events#activity-object-activity-types
20
+ */
21
+ const TYPES = {
22
+ playing: ActivityType.Playing,
23
+ streaming: ActivityType.Streaming,
24
+ listening: ActivityType.Listening,
25
+ watching: ActivityType.Watching,
26
+ custom: ActivityType.Custom,
27
+ competing: ActivityType.Competing,
28
+ };
29
+ /**
30
+ * "The streaming type currently only supports Twitch and YouTube. Only
31
+ * https://twitch.tv/ and https://youtube.com/ urls will work."
32
+ * @see https://docs.discord.com/developers/events/gateway-events#activity-object-activity-types
33
+ */
34
+ const STREAM_URL = /^https:\/\/(?:www\.)?(?:twitch\.tv|youtube\.com)\//;
35
+ const list = (words) => words.map(word => `'${word}'`).join(', ');
36
+ const EXAMPLE = `presence({ status: 'online', activity: { type: 'watching', name: 'over the server' } })`;
37
+ /**
38
+ * What a bot without a presence shows: online, doing nothing. Sent when
39
+ * the declaration is removed while the bot runs.
40
+ */
41
+ export const DEFAULT_PRESENCE = Object.freeze({
42
+ since: null,
43
+ activities: [],
44
+ status: 'online',
45
+ afk: false,
46
+ });
47
+ /** Turns a checked config into what the gateway sends. */
48
+ function toRaw(config) {
49
+ const activities = [];
50
+ if (config.activity) {
51
+ const { type, name, url } = config.activity;
52
+ activities.push(type === 'custom'
53
+ ? // A custom status shows its `state`; `name` is not shown but required.
54
+ { name: 'Custom Status', type: TYPES.custom, state: name }
55
+ : { name, type: TYPES[type], ...(url ? { url } : {}) });
56
+ }
57
+ // `since` and `afk` are about a person's client going idle: a bot is
58
+ // never away from its keyboard.
59
+ return {
60
+ since: null,
61
+ activities,
62
+ status: config.status ?? 'online',
63
+ afk: false,
64
+ };
65
+ }
66
+ /**
67
+ * The presence of the bot, declared once anywhere in `src/` with
68
+ * `presence()` and exported.
69
+ */
70
+ export const presenceDeclaration = {
71
+ one: 'presence',
72
+ many: 'presences',
73
+ is: isPresenceDeclaration,
74
+ list: false,
75
+ read(value) {
76
+ const config = value.config;
77
+ if (!isRecord(config)) {
78
+ return fail(`presence() needs an object: ${EXAMPLE}`);
79
+ }
80
+ for (const key of Object.keys(config)) {
81
+ if (!KEYS.includes(key)) {
82
+ fail(`"${key}" is not something a presence has. It can have: ${KEYS.join(', ')}.`);
83
+ }
84
+ }
85
+ const { status, activity } = config;
86
+ if (status !== undefined &&
87
+ !STATUSES.includes(status)) {
88
+ fail(`"status" is ${JSON.stringify(status)}: it can be ${list(STATUSES)}.`);
89
+ }
90
+ if (activity !== undefined) {
91
+ if (!isRecord(activity)) {
92
+ fail(`"activity" is what the bot is doing, an object: activity: { type: 'playing', name: '/help' }`);
93
+ }
94
+ for (const key of Object.keys(activity)) {
95
+ if (!ACTIVITY_KEYS.includes(key)) {
96
+ fail(`"${key}" is not something an activity has. It can have: ${ACTIVITY_KEYS.join(', ')}.`);
97
+ }
98
+ }
99
+ const { type, name, url } = activity;
100
+ if (typeof type !== 'string' || !(type in TYPES)) {
101
+ fail(`The "type" of the activity is ${JSON.stringify(type)}: it can be ${list(Object.keys(TYPES))}.`);
102
+ }
103
+ if (typeof name !== 'string' || name.trim() === '') {
104
+ fail(`The "name" of the activity is the text shown under the name of the bot: it can't be empty.`);
105
+ }
106
+ if (type === 'streaming') {
107
+ if (typeof url !== 'string' || !STREAM_URL.test(url)) {
108
+ fail(`A "streaming" activity needs the "url" of the stream, and Discord only accepts a https://twitch.tv/ or a https://youtube.com/ link${typeof url === 'string' ? ` (got ${JSON.stringify(url)})` : ''}.`);
109
+ }
110
+ }
111
+ else if (url !== undefined) {
112
+ fail(`"url" only goes with a "streaming" activity: a ${JSON.stringify(type)} activity has no link.`);
113
+ }
114
+ }
115
+ const raw = toRaw(config);
116
+ return { raw, key: JSON.stringify(raw) };
117
+ },
118
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The status of the bot: `'online'` (green), `'idle'` (orange), `'dnd'`
3
+ * (red, "do not disturb") or `'invisible'` (shown as offline).
4
+ */
5
+ export type PresenceStatusName = 'online' | 'idle' | 'dnd' | 'invisible';
6
+ /**
7
+ * What the bot is doing, as Discord shows it: "Playing {name}",
8
+ * "Listening to {name}", "Watching {name}", "Competing in {name}", or
9
+ * the text itself with `'custom'`.
10
+ */
11
+ export type ActivityKind = 'playing' | 'listening' | 'watching' | 'competing' | 'custom';
12
+ /** An activity that is not a stream. */
13
+ export interface PlainActivity {
14
+ type: ActivityKind;
15
+ /** What is shown after the type (or alone with `'custom'`). */
16
+ name: string;
17
+ url?: never;
18
+ }
19
+ /** A stream: "Streaming {name}", with a link to it. */
20
+ export interface StreamingActivity {
21
+ type: 'streaming';
22
+ /** What is shown after "Streaming". */
23
+ name: string;
24
+ /** The stream: Discord only accepts a `https://twitch.tv/` or a `https://youtube.com/` link. */
25
+ url: string;
26
+ }
27
+ /** What the bot is doing. */
28
+ export type Activity = PlainActivity | StreamingActivity;
29
+ /** What a file gives to `presence()`. */
30
+ export interface PresenceConfig {
31
+ /** The status of the bot. `'online'` by default. */
32
+ status?: PresenceStatusName;
33
+ /** What the bot is doing. Nothing by default. */
34
+ activity?: Activity;
35
+ }
36
+ /** What `presence()` returns: a declaration the framework finds in the exports of a file. */
37
+ export interface PresenceDeclaration {
38
+ /** What the file gave to `presence()`, not checked yet. */
39
+ readonly config: unknown;
40
+ }
41
+ /**
42
+ * Declares the presence of the bot: its status and what it is doing, shown
43
+ * under its name. Export the result from any file of `src/`, once; the bot
44
+ * has it as soon as it is connected.
45
+ *
46
+ * ```ts
47
+ * import { presence } from 'chapterjs';
48
+ *
49
+ * export default presence({
50
+ * status: 'online',
51
+ * activity: { type: 'watching', name: 'over the server' },
52
+ * });
53
+ * ```
54
+ */
55
+ export declare function presence(config: PresenceConfig): PresenceDeclaration;
56
+ /** Whether a value was made by `presence()`. */
57
+ export declare function isPresenceDeclaration(value: unknown): value is PresenceDeclaration;
@@ -0,0 +1,27 @@
1
+ // `presence()`, as user files import it from 'chapterjs': what the bot
2
+ // shows under its name, its status and what it is doing.
3
+ // https://docs.discord.com/developers/events/gateway-events#update-presence
4
+ const BRAND = Symbol.for('chapterjs.presence');
5
+ /**
6
+ * Declares the presence of the bot: its status and what it is doing, shown
7
+ * under its name. Export the result from any file of `src/`, once; the bot
8
+ * has it as soon as it is connected.
9
+ *
10
+ * ```ts
11
+ * import { presence } from 'chapterjs';
12
+ *
13
+ * export default presence({
14
+ * status: 'online',
15
+ * activity: { type: 'watching', name: 'over the server' },
16
+ * });
17
+ * ```
18
+ */
19
+ export function presence(config) {
20
+ return Object.freeze({ [BRAND]: true, config });
21
+ }
22
+ /** Whether a value was made by `presence()`. */
23
+ export function isPresenceDeclaration(value) {
24
+ return (typeof value === 'object' &&
25
+ value !== null &&
26
+ value[BRAND] === true);
27
+ }
@@ -0,0 +1,55 @@
1
+ import type { HttpMethod } from '../discord/endpoint.js';
2
+ /** One problem Discord found in a field of the request. */
3
+ export interface DiscordFieldError {
4
+ /** Where the problem is: `embeds[0].title`, or `` for the whole request. */
5
+ path: string;
6
+ /** The kind of problem, as Discord names it (`BASE_TYPE_MAX_LENGTH`). */
7
+ code: string;
8
+ /** What is wrong, in English. */
9
+ message: string;
10
+ }
11
+ /**
12
+ * Turns the nested `errors` object of an error response into a flat list.
13
+ * A problem is an `_errors` array placed under the key of the field, at any
14
+ * depth; array items are keyed by their index.
15
+ * @see https://docs.discord.com/developers/reference#error-messages
16
+ */
17
+ export declare function flattenFieldErrors(errors: unknown, path?: string): DiscordFieldError[];
18
+ /** Discord answered a request with an error. */
19
+ export declare class DiscordApiError extends Error {
20
+ readonly name = "DiscordApiError";
21
+ /** The HTTP status of the response (403, 404...). */
22
+ readonly status: number;
23
+ /**
24
+ * The JSON error code of Discord (50013 for "Missing Permissions"...), or
25
+ * 0 when the response had none.
26
+ * @see https://docs.discord.com/developers/topics/opcodes-and-status-codes#json-json-error-codes
27
+ */
28
+ readonly code: number;
29
+ /** The fields of the request Discord refused, when it says which. */
30
+ readonly fieldErrors: readonly DiscordFieldError[];
31
+ /** The HTTP method of the request that failed. */
32
+ readonly method: HttpMethod;
33
+ /** The path of the endpoint as written in the documentation. */
34
+ readonly route: string;
35
+ constructor(input: {
36
+ status: number;
37
+ method: HttpMethod;
38
+ route: string;
39
+ body: unknown;
40
+ });
41
+ }
42
+ /** A request could not reach Discord, or Discord kept failing. */
43
+ export declare class DiscordUnavailableError extends Error {
44
+ readonly name = "DiscordUnavailableError";
45
+ /** The HTTP method of the request that failed. */
46
+ readonly method: HttpMethod;
47
+ /** The path of the endpoint as written in the documentation. */
48
+ readonly route: string;
49
+ constructor(method: HttpMethod, route: string, cause: unknown);
50
+ }
51
+ /** The bot token was refused: no request can succeed until it is fixed. */
52
+ export declare class InvalidTokenError extends Error {
53
+ readonly name = "InvalidTokenError";
54
+ constructor();
55
+ }
@@ -0,0 +1,92 @@
1
+ import { JSON_ERROR_CODES } from '../discord/codes.js';
2
+ /**
3
+ * Turns the nested `errors` object of an error response into a flat list.
4
+ * A problem is an `_errors` array placed under the key of the field, at any
5
+ * depth; array items are keyed by their index.
6
+ * @see https://docs.discord.com/developers/reference#error-messages
7
+ */
8
+ export function flattenFieldErrors(errors, path = '') {
9
+ if (typeof errors !== 'object' || errors === null)
10
+ return [];
11
+ const found = [];
12
+ for (const [key, value] of Object.entries(errors)) {
13
+ if (key === '_errors') {
14
+ if (!Array.isArray(value))
15
+ continue;
16
+ for (const error of value) {
17
+ found.push({
18
+ path,
19
+ code: String(error?.code ?? ''),
20
+ message: String(error?.message ?? ''),
21
+ });
22
+ }
23
+ continue;
24
+ }
25
+ const child = /^\d+$/.test(key)
26
+ ? `${path}[${key}]`
27
+ : path === ''
28
+ ? key
29
+ : `${path}.${key}`;
30
+ found.push(...flattenFieldErrors(value, child));
31
+ }
32
+ return found;
33
+ }
34
+ /** Discord answered a request with an error. */
35
+ export class DiscordApiError extends Error {
36
+ name = 'DiscordApiError';
37
+ /** The HTTP status of the response (403, 404...). */
38
+ status;
39
+ /**
40
+ * The JSON error code of Discord (50013 for "Missing Permissions"...), or
41
+ * 0 when the response had none.
42
+ * @see https://docs.discord.com/developers/topics/opcodes-and-status-codes#json-json-error-codes
43
+ */
44
+ code;
45
+ /** The fields of the request Discord refused, when it says which. */
46
+ fieldErrors;
47
+ /** The HTTP method of the request that failed. */
48
+ method;
49
+ /** The path of the endpoint as written in the documentation. */
50
+ route;
51
+ constructor(input) {
52
+ const body = typeof input.body === 'object' && input.body !== null
53
+ ? input.body
54
+ : {};
55
+ const code = typeof body.code === 'number' ? body.code : 0;
56
+ const fieldErrors = flattenFieldErrors(body.errors);
57
+ const reason = typeof body.message === 'string' && body.message !== ''
58
+ ? body.message
59
+ : ((typeof body.code === 'number'
60
+ ? JSON_ERROR_CODES[code]
61
+ : undefined) ?? `HTTP ${input.status}`);
62
+ const details = fieldErrors
63
+ .map(error => `\n ${error.path || 'request'}: ${error.message}`)
64
+ .join('');
65
+ super(`Discord refused ${input.method} ${input.route}: ${reason} (${code || input.status})${details}`);
66
+ this.status = input.status;
67
+ this.code = code;
68
+ this.fieldErrors = fieldErrors;
69
+ this.method = input.method;
70
+ this.route = input.route;
71
+ }
72
+ }
73
+ /** A request could not reach Discord, or Discord kept failing. */
74
+ export class DiscordUnavailableError extends Error {
75
+ name = 'DiscordUnavailableError';
76
+ /** The HTTP method of the request that failed. */
77
+ method;
78
+ /** The path of the endpoint as written in the documentation. */
79
+ route;
80
+ constructor(method, route, cause) {
81
+ super(`Discord could not be reached for ${method} ${route}. Check your internet connection and https://discordstatus.com, then try again.`, { cause });
82
+ this.method = method;
83
+ this.route = route;
84
+ }
85
+ }
86
+ /** The bot token was refused: no request can succeed until it is fixed. */
87
+ export class InvalidTokenError extends Error {
88
+ name = 'InvalidTokenError';
89
+ constructor() {
90
+ super('Discord refused the bot token. Copy it again from the Developer Portal (your application → Bot → Reset Token) into BOT_TOKEN in your .env file.');
91
+ }
92
+ }
@@ -0,0 +1,46 @@
1
+ /** What the headers of a response say about the rate limit of its route. */
2
+ export interface RateLimitHeaders {
3
+ /** `X-RateLimit-Bucket`: routes sharing a limit share this value. */
4
+ bucket: string | null;
5
+ /** `X-RateLimit-Remaining` */
6
+ remaining: number | null;
7
+ /** `X-RateLimit-Reset-After`, in seconds. */
8
+ resetAfter: number | null;
9
+ }
10
+ /** A 429 response. */
11
+ export interface RateLimitHit {
12
+ /** `Retry-After` / `retry_after`, in seconds. */
13
+ retryAfter: number;
14
+ /** `X-RateLimit-Scope`: `user`, `global` or `shared`. */
15
+ scope: string | null;
16
+ /** `X-RateLimit-Global` */
17
+ global: boolean;
18
+ }
19
+ export declare function readRateLimitHeaders(headers: Headers): RateLimitHeaders;
20
+ export interface RateLimiterOptions {
21
+ /** Requests per second for the whole bot. Default: 50. */
22
+ globalLimit?: number;
23
+ /** Called every time a request has to wait. */
24
+ onWait?: (info: {
25
+ route: string;
26
+ ms: number;
27
+ global: boolean;
28
+ }) => void;
29
+ }
30
+ export declare class RateLimiter {
31
+ #private;
32
+ constructor(options?: RateLimiterOptions);
33
+ /**
34
+ * Runs `send` when the limits allow it. Requests of the same bucket run
35
+ * one after the other; different buckets run in parallel. `send` reports
36
+ * what the response said through `update` and returns its result, or a
37
+ * `RateLimitHit` to be run again after the delay Discord asked for.
38
+ */
39
+ run<T>(input: {
40
+ route: string;
41
+ major: string;
42
+ /** Interaction endpoints are not bound to the global rate limit. */
43
+ countsForGlobal: boolean;
44
+ }, send: (update: (headers: RateLimitHeaders) => void) => Promise<T | RateLimitHit>): Promise<T>;
45
+ }
46
+ export declare function rateLimitHit(hit: RateLimitHit): RateLimitHit;