chapterjs 0.1.1 → 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,100 @@
1
+ import { Locale } from '../discord/types/common.js';
2
+ import type { Context } from '../structures/context.js';
3
+ import type { Guild } from '../structures/guild.js';
4
+ import type { MessageText, MessageTexts, PluralText, Translator } from './messages.js';
5
+ /** A text, cut once: its literal parts and the placeholders between them. */
6
+ export interface TextTemplate {
7
+ readonly text: string;
8
+ /** Odd entries are placeholder names, even ones literal text. */
9
+ readonly parts: readonly string[];
10
+ readonly params: readonly string[];
11
+ }
12
+ /** The forms of a plural, by name. */
13
+ export type PluralForm = keyof PluralText;
14
+ /** A text with several forms, one per quantity, picked by `count`. */
15
+ export interface PluralTemplate {
16
+ readonly forms: ReadonlyMap<PluralForm, TextTemplate>;
17
+ /** `count`, then the placeholders of every form. */
18
+ readonly params: readonly string[];
19
+ }
20
+ export type Template = TextTemplate | PluralTemplate;
21
+ /** The languages of `src/messages/`, checked and assembled: what `t` reads. */
22
+ export interface LoadedMessages {
23
+ readonly default: Locale;
24
+ /** The texts of each language declared, compiled. */
25
+ readonly locales: ReadonlyMap<Locale, ReadonlyMap<string, Template>>;
26
+ /** The phrases of the framework each language translates, compiled. */
27
+ readonly framework: ReadonlyMap<Locale, ReadonlyMap<string, Template>>;
28
+ /**
29
+ * The `commands` section of each language that has one, by the path of
30
+ * the command file, not checked yet: the commands of the project are
31
+ * needed for that.
32
+ */
33
+ readonly commands: ReadonlyMap<Locale, Readonly<Record<string, unknown>>>;
34
+ /** The file of each language: `src/messages/fr.ts`. */
35
+ readonly files: ReadonlyMap<Locale, string>;
36
+ }
37
+ /** The forms a plural can have, in the order of the docs. */
38
+ export declare const PLURAL_FORMS: readonly PluralForm[];
39
+ /**
40
+ * Cuts a text around its placeholders, read from left to right: `{name}`
41
+ * is a placeholder, `{{` and `}}` are braces written as is, and a `{`
42
+ * with another `{` before its `}` is a brace too.
43
+ */
44
+ export declare function compile(text: string): TextTemplate;
45
+ /** Puts the forms of a plural together: `count` picks one when filled. */
46
+ export declare function compilePlural(forms: ReadonlyMap<PluralForm, TextTemplate>): PluralTemplate;
47
+ /** A text of a language file, trusted, compiled. */
48
+ export declare function templateOf(text: MessageText): Template;
49
+ /** Whether a template is a plural. */
50
+ export declare const isPlural: (template: Template) => template is PluralTemplate;
51
+ /** Every locale Discord knows. */
52
+ export declare const LOCALES: ReadonlySet<string>;
53
+ /**
54
+ * The language of the file to use for one asked: the same one, else one
55
+ * of the same language in another region (`en-GB` → `en-US`, `es-419` →
56
+ * `es-ES`, `zh-TW` → `zh-CN`), else the default.
57
+ */
58
+ export declare function pickLocale(messages: LoadedMessages, locale: string | null): Locale;
59
+ /**
60
+ * Fills the placeholders of a template, with an error naming a missing
61
+ * one. Numbers are written the way `locale` writes them, and a plural
62
+ * takes the form that language uses for `count`.
63
+ */
64
+ export declare function fill(key: string, template: Template, params: Record<string, unknown> | undefined, locale?: string): string;
65
+ /** `t` for one language of the file, the same object every time. */
66
+ export declare function translatorFor(messages: LoadedMessages, locale: string | null): Translator<MessageTexts>;
67
+ /** Who will read what is sent. */
68
+ export interface Audience {
69
+ /** The language of the person (the one of an interaction), when known. */
70
+ person?: string | null | undefined;
71
+ /** The server the message goes to; `null` in private messages. */
72
+ guild?: Guild | null | undefined;
73
+ /** Whether the person alone sees it. */
74
+ ephemeral?: boolean | undefined;
75
+ }
76
+ /**
77
+ * The language of those who will read what is sent: the person when they
78
+ * alone see it (an ephemeral answer, a private message), else the language
79
+ * of the server when Discord knows it (it only does for Community servers:
80
+ * elsewhere `preferred_locale` is always `en-US`), else none, which means
81
+ * the default language of the file.
82
+ * @see https://docs.discord.com/developers/resources/guild#guild-object-guild-structure
83
+ */
84
+ export declare function audienceLocale({ person, guild, ephemeral, }: Audience): string | null;
85
+ /** A `t` for a project without a language in `src/messages/`: it says so when called. */
86
+ export declare function missingTranslator(locale: string | null): Translator<MessageTexts>;
87
+ /**
88
+ * `t` for what the bot sends, in the language of who will read it. Without
89
+ * a language, a `t` that explains it is missing when called: the
90
+ * texts of a component may still be functions of it.
91
+ */
92
+ export declare function translatorOf(ctx: Pick<Context, 'messages'>, audience: Audience): Translator<MessageTexts>;
93
+ /**
94
+ * What a handler receives about translations: `t` in the language asked
95
+ * when the project has a language in `src/messages/`, nothing otherwise. Spread it
96
+ * into the context of any handler.
97
+ */
98
+ export declare function translation(ctx: Pick<Context, 'messages'>, locale: string | null): {
99
+ t?: Translator<MessageTexts>;
100
+ };
@@ -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 src/messages/. It has: ${[...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 in `src/messages/`: 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 in src/messages/: add one, like src/messages/en-US.ts, 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 in `src/messages/`, 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,26 @@
1
+ import type { Convention } from '../loader/loader.js';
2
+ import { type RawGatewayPresenceUpdate } from '../discord/types/gateway-events.js';
3
+ /** The presence file, 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 file shows: online, doing nothing. Sent
12
+ * when the file is removed while the bot runs.
13
+ */
14
+ export declare const DEFAULT_PRESENCE: RawGatewayPresenceUpdate;
15
+ /**
16
+ * `src/presence.ts`: the status of the bot and what it is doing. The file
17
+ * exports by default what `presence()` returns:
18
+ *
19
+ * ```ts
20
+ * // src/presence.ts
21
+ * import { presence } from 'chapterjs';
22
+ *
23
+ * export default presence({ activity: { type: 'playing', name: '/help' } });
24
+ * ```
25
+ */
26
+ export declare const presenceConvention: Convention<LoadedPresence>;
@@ -0,0 +1,133 @@
1
+ // `src/presence.ts`: the one file that says what the bot shows under its
2
+ // name. What it declares is checked when it loads and turned into what the
3
+ // gateway sends, so a wrong status or type is explained before the bot
4
+ // connects.
5
+ import { ActivityType, } from '../discord/types/gateway-events.js';
6
+ import { isPresenceFile } from './presence.js';
7
+ const fail = message => {
8
+ throw new TypeError(message);
9
+ };
10
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
11
+ const KEYS = ['status', 'activity'];
12
+ const ACTIVITY_KEYS = ['type', 'name', 'url'];
13
+ /**
14
+ * The statuses a file may give.
15
+ * @see https://docs.discord.com/developers/events/gateway-events#update-presence-status-types
16
+ */
17
+ const STATUSES = ['online', 'idle', 'dnd', 'invisible'];
18
+ /**
19
+ * The activity types, by the word a file writes.
20
+ * @see https://docs.discord.com/developers/events/gateway-events#activity-object-activity-types
21
+ */
22
+ const TYPES = {
23
+ playing: ActivityType.Playing,
24
+ streaming: ActivityType.Streaming,
25
+ listening: ActivityType.Listening,
26
+ watching: ActivityType.Watching,
27
+ custom: ActivityType.Custom,
28
+ competing: ActivityType.Competing,
29
+ };
30
+ /**
31
+ * "The streaming type currently only supports Twitch and YouTube. Only
32
+ * https://twitch.tv/ and https://youtube.com/ urls will work."
33
+ * @see https://docs.discord.com/developers/events/gateway-events#activity-object-activity-types
34
+ */
35
+ const STREAM_URL = /^https:\/\/(?:www\.)?(?:twitch\.tv|youtube\.com)\//;
36
+ const list = (words) => words.map(word => `'${word}'`).join(', ');
37
+ const EXAMPLE = `presence({ status: 'online', activity: { type: 'watching', name: 'over the server' } })`;
38
+ /**
39
+ * What a bot without a presence file shows: online, doing nothing. Sent
40
+ * when the file is removed while the bot runs.
41
+ */
42
+ export const DEFAULT_PRESENCE = Object.freeze({
43
+ since: null,
44
+ activities: [],
45
+ status: 'online',
46
+ afk: false,
47
+ });
48
+ /** Turns a checked config into what the gateway sends. */
49
+ function toRaw(config) {
50
+ const activities = [];
51
+ if (config.activity) {
52
+ const { type, name, url } = config.activity;
53
+ activities.push(type === 'custom'
54
+ ? // A custom status shows its `state`; `name` is not shown but required.
55
+ { name: 'Custom Status', type: TYPES.custom, state: name }
56
+ : { name, type: TYPES[type], ...(url ? { url } : {}) });
57
+ }
58
+ // `since` and `afk` are about a person's client going idle: a bot is
59
+ // never away from its keyboard.
60
+ return {
61
+ since: null,
62
+ activities,
63
+ status: config.status ?? 'online',
64
+ afk: false,
65
+ };
66
+ }
67
+ /**
68
+ * `src/presence.ts`: the status of the bot and what it is doing. The file
69
+ * exports by default what `presence()` returns:
70
+ *
71
+ * ```ts
72
+ * // src/presence.ts
73
+ * import { presence } from 'chapterjs';
74
+ *
75
+ * export default presence({ activity: { type: 'playing', name: '/help' } });
76
+ * ```
77
+ */
78
+ export const presenceConvention = {
79
+ folder: 'presence',
80
+ single: true,
81
+ one: 'presence',
82
+ many: 'presences',
83
+ read(exports) {
84
+ const file = exports.default;
85
+ const example = `import { presence } from 'chapterjs'; export default ${EXAMPLE}`;
86
+ if (!isPresenceFile(file)) {
87
+ return fail('default' in exports
88
+ ? `The default export of this file must be what presence() returns: ${example}`
89
+ : `This file has no default export. It should look like: ${example}`);
90
+ }
91
+ const config = file.config;
92
+ if (!isRecord(config)) {
93
+ return fail(`presence() needs an object: ${EXAMPLE}`);
94
+ }
95
+ for (const key of Object.keys(config)) {
96
+ if (!KEYS.includes(key)) {
97
+ fail(`"${key}" is not something a presence has. It can have: ${KEYS.join(', ')}.`);
98
+ }
99
+ }
100
+ const { status, activity } = config;
101
+ if (status !== undefined &&
102
+ !STATUSES.includes(status)) {
103
+ fail(`"status" is ${JSON.stringify(status)}: it can be ${list(STATUSES)}.`);
104
+ }
105
+ if (activity !== undefined) {
106
+ if (!isRecord(activity)) {
107
+ fail(`"activity" is what the bot is doing, an object: activity: { type: 'playing', name: '/help' }`);
108
+ }
109
+ for (const key of Object.keys(activity)) {
110
+ if (!ACTIVITY_KEYS.includes(key)) {
111
+ fail(`"${key}" is not something an activity has. It can have: ${ACTIVITY_KEYS.join(', ')}.`);
112
+ }
113
+ }
114
+ const { type, name, url } = activity;
115
+ if (typeof type !== 'string' || !(type in TYPES)) {
116
+ fail(`The "type" of the activity is ${JSON.stringify(type)}: it can be ${list(Object.keys(TYPES))}.`);
117
+ }
118
+ if (typeof name !== 'string' || name.trim() === '') {
119
+ fail(`The "name" of the activity is the text shown under the name of the bot: it can't be empty.`);
120
+ }
121
+ if (type === 'streaming') {
122
+ if (typeof url !== 'string' || !STREAM_URL.test(url)) {
123
+ 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)})` : ''}.`);
124
+ }
125
+ }
126
+ else if (url !== undefined) {
127
+ fail(`"url" only goes with a "streaming" activity: a ${JSON.stringify(type)} activity has no link.`);
128
+ }
129
+ }
130
+ const raw = toRaw(config);
131
+ return { raw, key: JSON.stringify(raw) };
132
+ },
133
+ };
@@ -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 `src/presence.ts` 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: the default export of `src/presence.ts`. */
37
+ export interface PresenceFile {
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 as the default export of
44
+ * `src/presence.ts`; the bot 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): PresenceFile;
56
+ /** Whether a value was made by `presence()`. */
57
+ export declare function isPresenceFile(value: unknown): value is PresenceFile;
@@ -0,0 +1,27 @@
1
+ // `presence()`, as `src/presence.ts` imports it from 'chapterjs': what the
2
+ // bot 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 as the default export of
8
+ * `src/presence.ts`; the bot 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 isPresenceFile(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
+ }