chapterjs 0.3.0 → 0.4.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 (48) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/build.js +14 -1
  3. package/dist/cli/dev.js +29 -1
  4. package/dist/cli/env.d.ts +5 -0
  5. package/dist/cli/env.js +39 -3
  6. package/dist/cli/project.d.ts +30 -7
  7. package/dist/cli/project.js +85 -11
  8. package/dist/cli/redis.d.ts +23 -0
  9. package/dist/cli/redis.js +57 -0
  10. package/dist/cli/start.js +43 -17
  11. package/dist/cli/test.js +4 -2
  12. package/dist/events/registry.d.ts +252 -239
  13. package/dist/events/registry.js +125 -100
  14. package/dist/index.d.ts +4 -2
  15. package/dist/index.js +1 -0
  16. package/dist/ranking/declaration.d.ts +12 -0
  17. package/dist/ranking/declaration.js +48 -0
  18. package/dist/ranking/ranking.d.ts +102 -0
  19. package/dist/ranking/ranking.js +129 -0
  20. package/dist/redis/client.d.ts +36 -0
  21. package/dist/redis/client.js +252 -0
  22. package/dist/redis/protocol.d.ts +22 -0
  23. package/dist/redis/protocol.js +99 -0
  24. package/dist/redis/url.d.ts +24 -0
  25. package/dist/redis/url.js +50 -0
  26. package/dist/store/backend.d.ts +49 -4
  27. package/dist/store/backend.js +66 -11
  28. package/dist/store/declaration.d.ts +3 -0
  29. package/dist/store/declaration.js +8 -3
  30. package/dist/store/file-backend.d.ts +3 -1
  31. package/dist/store/file-backend.js +29 -0
  32. package/dist/store/ipc-backend.d.ts +24 -1
  33. package/dist/store/ipc-backend.js +49 -0
  34. package/dist/store/memory-backend.d.ts +4 -2
  35. package/dist/store/memory-backend.js +27 -1
  36. package/dist/store/ranking-backend.d.ts +45 -0
  37. package/dist/store/ranking-backend.js +82 -0
  38. package/dist/store/redis-backend.d.ts +38 -0
  39. package/dist/store/redis-backend.js +245 -0
  40. package/dist/store/redis-ranking.d.ts +15 -0
  41. package/dist/store/redis-ranking.js +69 -0
  42. package/dist/store/scope.d.ts +11 -0
  43. package/dist/store/scope.js +23 -0
  44. package/dist/store/store.d.ts +39 -5
  45. package/dist/store/store.js +79 -32
  46. package/dist/structures/invite.d.ts +6 -0
  47. package/dist/structures/invite.js +11 -0
  48. package/package.json +1 -1
@@ -16,13 +16,15 @@ const channel = (on) => ({
16
16
  on,
17
17
  build: (ctx, data) => {
18
18
  const found = ctx.cache.channels.get(data.id);
19
- return found ? { channel: found } : null;
19
+ return found ? { channel: found, guild: found.guild } : null;
20
20
  },
21
21
  });
22
22
  const deletedChannel = (on) => ({
23
23
  on,
24
24
  before: (ctx, data) => ctx.cache.channels.get(data.id) ?? null,
25
- build: (_ctx, _data, { before }) => before ? { channel: before } : null,
25
+ build: (_ctx, _data, { before }) => before
26
+ ? { channel: before, guild: before.guild }
27
+ : null,
26
28
  });
27
29
  /**
28
30
  * Discord refused what an event needs before it can be given to its files:
@@ -95,14 +97,19 @@ const placeAndUser = (ctx, data, userId) => {
95
97
  };
96
98
  /** Who did it: what `prepare` got, or else what the bot knows. */
97
99
  const userFrom = (ctx, prepared, id) => prepared ?? ctx.cache.users.get(id) ?? null;
98
- /** Where something happened in a server or in private, as the bot knows it. */
100
+ /**
101
+ * Where something happened in a server or in private, as the bot knows
102
+ * it: `at` goes in the object of the event, `guild` next to it.
103
+ */
99
104
  const placeOf = (ctx, data) => {
100
105
  const found = ctx.cache.channels.get(data.channel_id);
101
106
  const guildId = data.guild_id ?? found?.guildId ?? null;
102
107
  return {
103
- channelId: data.channel_id,
104
- channel: found?.isTextBased() ? found : null,
105
- guildId,
108
+ at: {
109
+ channelId: data.channel_id,
110
+ channel: found?.isTextBased() ? found : null,
111
+ guildId,
112
+ },
106
113
  guild: guildId ? (ctx.cache.guilds.get(guildId) ?? null) : null,
107
114
  };
108
115
  };
@@ -114,15 +121,15 @@ const rememberedMember = (guild, userId) => guild?.members.get(userId) ?? null;
114
121
  * Whether a file wants something that happened in a server or in private:
115
122
  * it listens there, and in a server what it is promised is there.
116
123
  */
117
- const wantedPlace = (context, where, needsMember = false) => {
118
- const inGuild = context.guildId !== null;
124
+ const wantedPlace = (about, guild, where, needsMember = false) => {
125
+ const inGuild = about.guildId !== null;
119
126
  if (!listensThere(where, inGuild))
120
127
  return false;
121
128
  if (!inGuild)
122
129
  return true;
123
- return (context.guild !== null &&
124
- context.channel !== null &&
125
- (!needsMember || context.member != null));
130
+ return (guild !== null &&
131
+ about.channel !== null &&
132
+ (!needsMember || about.member != null));
126
133
  };
127
134
  /**
128
135
  * Private messages, bots and webhooks only reach the files that asked for
@@ -152,9 +159,10 @@ const wantedMessage = ({ message }, options) => {
152
159
  const message = (on) => ({
153
160
  on,
154
161
  prepare: channelOf,
155
- build: (ctx, data) => ({
156
- message: ctx.entities.message(data, data.guild_id),
157
- }),
162
+ build: (ctx, data) => {
163
+ const message = ctx.entities.message(data, data.guild_id);
164
+ return { message, guild: message.guild };
165
+ },
158
166
  });
159
167
  const member = (on) => ({
160
168
  on,
@@ -226,12 +234,15 @@ const cleared = (on) => ({
226
234
  on,
227
235
  prepare: channelOf,
228
236
  build: (ctx, data) => {
229
- const place = placeOf(ctx, data);
237
+ const { at, guild } = placeOf(ctx, data);
230
238
  return {
231
- emoji: 'emoji' in data ? toCamelCase(data.emoji) : null,
232
- messageId: data.message_id,
233
- message: rememberedMessage(place.channel, data.message_id),
234
- ...place,
239
+ reactions: {
240
+ emoji: 'emoji' in data ? toCamelCase(data.emoji) : null,
241
+ messageId: data.message_id,
242
+ message: rememberedMessage(at.channel, data.message_id),
243
+ ...at,
244
+ },
245
+ guild,
235
246
  };
236
247
  },
237
248
  });
@@ -239,17 +250,20 @@ const vote = (on) => ({
239
250
  on,
240
251
  prepare: (ctx, data) => placeAndUser(ctx, data, data.user_id),
241
252
  build: (ctx, data, { prepared }) => {
242
- const place = placeOf(ctx, data);
253
+ const { at, guild } = placeOf(ctx, data);
243
254
  const user = userFrom(ctx, prepared, data.user_id);
244
255
  if (!user)
245
256
  return null;
246
257
  return {
247
- answerId: data.answer_id,
248
- user,
249
- member: rememberedMember(place.guild, data.user_id),
250
- messageId: data.message_id,
251
- message: rememberedMessage(place.channel, data.message_id),
252
- ...place,
258
+ vote: {
259
+ answerId: data.answer_id,
260
+ user,
261
+ member: rememberedMember(guild, data.user_id),
262
+ messageId: data.message_id,
263
+ message: rememberedMessage(at.channel, data.message_id),
264
+ ...at,
265
+ },
266
+ guild,
253
267
  };
254
268
  },
255
269
  });
@@ -283,7 +297,7 @@ const voice = (pick) => ({
283
297
  : guild.members.get(data.user_id);
284
298
  if (!picked || !member)
285
299
  return null;
286
- return { ...picked, member, guild };
300
+ return { voice: { ...picked, member }, guild };
287
301
  },
288
302
  });
289
303
  const scheduled = (on) => ({
@@ -308,9 +322,11 @@ const interested = (on) => ({
308
322
  if (!guild || !user)
309
323
  return null;
310
324
  return {
311
- scheduledEventId: data.guild_scheduled_event_id,
312
- user,
313
- member: rememberedMember(guild, data.user_id),
325
+ interest: {
326
+ scheduledEventId: data.guild_scheduled_event_id,
327
+ user,
328
+ member: rememberedMember(guild, data.user_id),
329
+ },
314
330
  guild,
315
331
  };
316
332
  },
@@ -322,7 +338,7 @@ const interested = (on) => ({
322
338
  export const EVENTS = {
323
339
  ready: { intents: 0, sources: [] },
324
340
  messageCreate: {
325
- guildOf: ({ message }) => message.guild,
341
+ guildOf: ofGuild,
326
342
  // Without Message Content, the messages of others come empty.
327
343
  intents: ({ where }) => messagesIn(where) | I.MessageContent,
328
344
  options: { bots: 'boolean', where: PLACES },
@@ -330,7 +346,7 @@ export const EVENTS = {
330
346
  sources: [message('MESSAGE_CREATE')],
331
347
  },
332
348
  messageUpdate: {
333
- guildOf: ({ message }) => message.guild,
349
+ guildOf: ofGuild,
334
350
  intents: ({ where }) => messagesIn(where) | I.MessageContent,
335
351
  options: { bots: 'boolean', where: PLACES },
336
352
  accepts: wantedMessage,
@@ -340,9 +356,7 @@ export const EVENTS = {
340
356
  guildOf: ofGuild,
341
357
  intents: ({ where }) => messagesIn(where),
342
358
  options: { where: PLACES },
343
- accepts: ({ guildId, guild, channel }, options) => listensThere(options.where, guildId !== null) &&
344
- // In a server, the server and the channel are promised.
345
- (guildId === null || (guild !== null && channel !== null)),
359
+ accepts: ({ message, guild }, options) => wantedPlace(message, guild, options.where),
346
360
  sources: [
347
361
  {
348
362
  on: 'MESSAGE_DELETE',
@@ -353,11 +367,13 @@ export const EVENTS = {
353
367
  ? (found.messages.get(data.id) ?? null)
354
368
  : null;
355
369
  },
356
- build: (ctx, data, { before }) => ({
357
- messageId: data.id,
358
- ...placeOf(ctx, data),
359
- message: before,
360
- }),
370
+ build: (ctx, data, { before }) => {
371
+ const { at, guild } = placeOf(ctx, data);
372
+ return {
373
+ message: { id: data.id, ...at, before: before },
374
+ guild,
375
+ };
376
+ },
361
377
  },
362
378
  {
363
379
  // Messages deleted together: one occurrence each.
@@ -368,12 +384,11 @@ export const EVENTS = {
368
384
  return data.ids.map(id => found?.isTextBased() ? (found.messages.get(id) ?? null) : null);
369
385
  },
370
386
  build: (ctx, data, { before }) => {
371
- const place = placeOf(ctx, data);
387
+ const { at, guild } = placeOf(ctx, data);
372
388
  const messages = before;
373
- return data.ids.map((messageId, index) => ({
374
- messageId,
375
- ...place,
376
- message: messages[index] ?? null,
389
+ return data.ids.map((id, index) => ({
390
+ message: { id, ...at, before: messages[index] ?? null },
391
+ guild,
377
392
  }));
378
393
  },
379
394
  },
@@ -392,21 +407,14 @@ export const EVENTS = {
392
407
  memberLeave: {
393
408
  guildOf: ofGuild,
394
409
  intents: I.GuildMembers,
395
- // "The member as it was" is a member the bot remembered.
396
- remembers: { members: 1000 },
397
410
  sources: [
398
411
  {
399
412
  on: 'GUILD_MEMBER_REMOVE',
400
- before: (ctx, data) => findGuild(ctx, data.guild_id)?.members.get(data.user.id) ?? null,
401
- build: (ctx, data, { before }) => {
413
+ build: (ctx, data) => {
402
414
  const guild = findGuild(ctx, data.guild_id);
403
415
  if (!guild)
404
416
  return null;
405
- return {
406
- user: ctx.entities.user(data.user),
407
- guild,
408
- member: before,
409
- };
417
+ return { user: ctx.entities.user(data.user), guild };
410
418
  },
411
419
  },
412
420
  ],
@@ -437,17 +445,17 @@ export const EVENTS = {
437
445
  ],
438
446
  },
439
447
  channelCreate: {
440
- guildOf: ({ channel }) => channel.guild,
448
+ guildOf: ofGuild,
441
449
  intents: I.Guilds,
442
450
  sources: [channel('CHANNEL_CREATE'), channel('THREAD_CREATE')],
443
451
  },
444
452
  channelUpdate: {
445
- guildOf: ({ channel }) => channel.guild,
453
+ guildOf: ofGuild,
446
454
  intents: I.Guilds,
447
455
  sources: [channel('CHANNEL_UPDATE'), channel('THREAD_UPDATE')],
448
456
  },
449
457
  channelDelete: {
450
- guildOf: ({ channel }) => channel.guild,
458
+ guildOf: ofGuild,
451
459
  intents: I.Guilds,
452
460
  sources: [
453
461
  deletedChannel('CHANNEL_DELETE'),
@@ -476,7 +484,7 @@ export const EVENTS = {
476
484
  // Every role of a server is known: one that is not never was.
477
485
  if (!guild || !before)
478
486
  return null;
479
- return { roleId: data.role_id, guild, role: before };
487
+ return { role: before, guild };
480
488
  },
481
489
  },
482
490
  ],
@@ -544,7 +552,7 @@ export const EVENTS = {
544
552
  channel: { id: channelId },
545
553
  ...(inviter ? { inviter } : {}),
546
554
  });
547
- return { invite, ...place };
555
+ return { invite, guild: place.guild };
548
556
  },
549
557
  },
550
558
  ],
@@ -558,7 +566,12 @@ export const EVENTS = {
558
566
  prepare: channelOf,
559
567
  build: (ctx, data) => {
560
568
  const place = inviteChannel(ctx, data);
561
- return place ? { code: data.code, ...place } : null;
569
+ return place
570
+ ? {
571
+ invite: { code: data.code, channel: place.channel },
572
+ guild: place.guild,
573
+ }
574
+ : null;
562
575
  },
563
576
  },
564
577
  ],
@@ -583,15 +596,15 @@ export const EVENTS = {
583
596
  guildOf: ofGuild,
584
597
  intents: ({ where }) => reactionsIn(where),
585
598
  options: { bots: 'boolean', where: PLACES },
586
- accepts: (context, options) => wantedPlace(context, options.where, true) &&
587
- (options.bots === true || !context.user.bot),
599
+ accepts: ({ reaction, guild }, options) => wantedPlace(reaction, guild, options.where, true) &&
600
+ (options.bots === true || !reaction.user.bot),
588
601
  sources: [
589
602
  {
590
603
  on: 'MESSAGE_REACTION_ADD',
591
604
  // In a server, who reacted comes with the event.
592
605
  prepare: (ctx, data) => placeAndUser(ctx, data, data.member?.user ? null : data.user_id),
593
606
  build: (ctx, data, { prepared }) => {
594
- const place = placeOf(ctx, data);
607
+ const { at, guild } = placeOf(ctx, data);
595
608
  const member = data.member && data.guild_id
596
609
  ? ctx.entities.member(data.guild_id, data.member)
597
610
  : null;
@@ -599,14 +612,17 @@ export const EVENTS = {
599
612
  if (!user)
600
613
  return null;
601
614
  return {
602
- emoji: toCamelCase(data.emoji),
603
- user,
604
- member,
605
- burst: data.burst,
606
- messageId: data.message_id,
607
- message: rememberedMessage(place.channel, data.message_id),
608
- messageAuthorId: data.message_author_id ?? null,
609
- ...place,
615
+ reaction: {
616
+ emoji: toCamelCase(data.emoji),
617
+ user,
618
+ member,
619
+ burst: data.burst,
620
+ messageId: data.message_id,
621
+ message: rememberedMessage(at.channel, data.message_id),
622
+ messageAuthorId: data.message_author_id ?? null,
623
+ ...at,
624
+ },
625
+ guild,
610
626
  };
611
627
  },
612
628
  },
@@ -616,25 +632,28 @@ export const EVENTS = {
616
632
  guildOf: ofGuild,
617
633
  intents: ({ where }) => reactionsIn(where),
618
634
  options: { bots: 'boolean', where: PLACES },
619
- accepts: (context, options) => wantedPlace(context, options.where) &&
620
- (options.bots === true || !context.user.bot),
635
+ accepts: ({ reaction, guild }, options) => wantedPlace(reaction, guild, options.where) &&
636
+ (options.bots === true || !reaction.user.bot),
621
637
  sources: [
622
638
  {
623
639
  on: 'MESSAGE_REACTION_REMOVE',
624
640
  prepare: (ctx, data) => placeAndUser(ctx, data, data.user_id),
625
641
  build: (ctx, data, { prepared }) => {
626
- const place = placeOf(ctx, data);
642
+ const { at, guild } = placeOf(ctx, data);
627
643
  const user = userFrom(ctx, prepared, data.user_id);
628
644
  if (!user)
629
645
  return null;
630
646
  return {
631
- emoji: toCamelCase(data.emoji),
632
- user,
633
- member: rememberedMember(place.guild, data.user_id),
634
- burst: data.burst,
635
- messageId: data.message_id,
636
- message: rememberedMessage(place.channel, data.message_id),
637
- ...place,
647
+ reaction: {
648
+ emoji: toCamelCase(data.emoji),
649
+ user,
650
+ member: rememberedMember(guild, data.user_id),
651
+ burst: data.burst,
652
+ messageId: data.message_id,
653
+ message: rememberedMessage(at.channel, data.message_id),
654
+ ...at,
655
+ },
656
+ guild,
638
657
  };
639
658
  },
640
659
  },
@@ -644,7 +663,7 @@ export const EVENTS = {
644
663
  guildOf: ofGuild,
645
664
  intents: ({ where }) => reactionsIn(where),
646
665
  options: { where: PLACES },
647
- accepts: (context, options) => wantedPlace(context, options.where),
666
+ accepts: ({ reactions, guild }, options) => wantedPlace(reactions, guild, options.where),
648
667
  sources: [
649
668
  cleared('MESSAGE_REACTION_REMOVE_ALL'),
650
669
  cleared('MESSAGE_REACTION_REMOVE_EMOJI'),
@@ -654,14 +673,14 @@ export const EVENTS = {
654
673
  guildOf: ofGuild,
655
674
  intents: ({ where }) => pollsIn(where),
656
675
  options: { where: PLACES },
657
- accepts: (context, options) => wantedPlace(context, options.where),
676
+ accepts: ({ vote, guild }, options) => wantedPlace(vote, guild, options.where),
658
677
  sources: [vote('MESSAGE_POLL_VOTE_ADD')],
659
678
  },
660
679
  pollVoteRemove: {
661
680
  guildOf: ofGuild,
662
681
  intents: ({ where }) => pollsIn(where),
663
682
  options: { where: PLACES },
664
- accepts: (context, options) => wantedPlace(context, options.where),
683
+ accepts: ({ vote, guild }, options) => wantedPlace(vote, guild, options.where),
665
684
  sources: [vote('MESSAGE_POLL_VOTE_REMOVE')],
666
685
  },
667
686
  typingStart: {
@@ -669,7 +688,7 @@ export const EVENTS = {
669
688
  intents: ({ where }) => (where === 'dm' ? 0 : I.GuildMessageTyping) |
670
689
  (where === 'dm' || where === 'both' ? I.DirectMessageTyping : 0),
671
690
  options: { where: PLACES },
672
- accepts: (context, options) => wantedPlace(context, options.where, true),
691
+ accepts: ({ typing, guild }, options) => wantedPlace(typing, guild, options.where, true),
673
692
  sources: [
674
693
  {
675
694
  on: 'TYPING_START',
@@ -681,11 +700,15 @@ export const EVENTS = {
681
700
  const user = member?.user ?? userFrom(ctx, prepared, data.user_id);
682
701
  if (!user)
683
702
  return null;
703
+ const { at, guild } = placeOf(ctx, data);
684
704
  return {
685
- user,
686
- member,
687
- startedAt: new Date(data.timestamp * 1000),
688
- ...placeOf(ctx, data),
705
+ typing: {
706
+ user,
707
+ member,
708
+ startedAt: new Date(data.timestamp * 1000),
709
+ ...at,
710
+ },
711
+ guild,
689
712
  };
690
713
  },
691
714
  },
@@ -711,7 +734,7 @@ export const EVENTS = {
711
734
  if (!added.member?.user)
712
735
  continue;
713
736
  const member = ctx.entities.member(data.guild_id, added.member);
714
- joined.push({ member, user: member.user, ...place });
737
+ joined.push({ thread: place.thread, member, guild: place.guild });
715
738
  }
716
739
  return joined;
717
740
  },
@@ -732,10 +755,10 @@ export const EVENTS = {
732
755
  if (!place)
733
756
  return null;
734
757
  return (data.removed_member_ids ?? []).map(userId => ({
758
+ thread: place.thread,
735
759
  userId,
736
- user: ctx.cache.users.get(userId) ?? null,
737
760
  member: rememberedMember(place.guild, userId),
738
- ...place,
761
+ guild: place.guild,
739
762
  }));
740
763
  },
741
764
  },
@@ -745,14 +768,14 @@ export const EVENTS = {
745
768
  guildOf: ofGuild,
746
769
  intents: I.GuildVoiceStates,
747
770
  sources: [
748
- voice((was, now) => !was && now ? { channel: now.channel, voice: stateOf(now) } : null),
771
+ voice((was, now) => !was && now ? { channel: now.channel, state: stateOf(now) } : null),
749
772
  ],
750
773
  },
751
774
  voiceLeave: {
752
775
  guildOf: ofGuild,
753
776
  intents: I.GuildVoiceStates,
754
777
  sources: [
755
- voice((was, now) => was && !now ? { channel: was.channel, voice: stateOf(was) } : null),
778
+ voice((was, now) => was && !now ? { channel: was.channel, state: stateOf(was) } : null),
756
779
  ],
757
780
  },
758
781
  voiceMove: {
@@ -760,7 +783,7 @@ export const EVENTS = {
760
783
  intents: I.GuildVoiceStates,
761
784
  sources: [
762
785
  voice((was, now) => was && now && was.channel.id !== now.channel.id
763
- ? { from: was.channel, to: now.channel, voice: stateOf(now) }
786
+ ? { from: was.channel, to: now.channel, state: stateOf(now) }
764
787
  : null),
765
788
  ],
766
789
  },
@@ -774,7 +797,7 @@ export const EVENTS = {
774
797
  JSON.stringify(was.state) !== JSON.stringify(now.state)
775
798
  ? {
776
799
  channel: now.channel,
777
- voice: stateOf(now),
800
+ state: stateOf(now),
778
801
  before: stateOf(was),
779
802
  }
780
803
  : null),
@@ -797,10 +820,12 @@ export const EVENTS = {
797
820
  ? ctx.entities.user(raw)
798
821
  : (ctx.cache.users.get(raw.id) ?? null);
799
822
  return {
800
- userId: raw.id,
801
- user,
802
- member: rememberedMember(guild, raw.id),
803
- presence: toCamelCase(presence),
823
+ presence: {
824
+ ...toCamelCase(presence),
825
+ userId: raw.id,
826
+ user,
827
+ member: rememberedMember(guild, raw.id),
828
+ },
804
829
  guild,
805
830
  };
806
831
  },
package/dist/index.d.ts CHANGED
@@ -18,7 +18,7 @@ export type { ContainerOptions, LinkButtonOptions, MediaOptions, SeparatorOption
18
18
  export type { ActionRowComponent, ButtonComponent, ContainerChild, ContainerComponent, FileComponent, GalleryComponent, MessageComponent, ModalComponent, Piece, SectionComponent, SelectComponent, SeparatorComponent, TextComponent, ThumbnailComponent, } from './components/instance.js';
19
19
  export { event } from './events/event.js';
20
20
  export type { EventConfig, EventDeclaration } from './events/event.js';
21
- export type { ContextOf, EventWhere, WhereEventOptions, EventContexts, EventName, EventOptions, MessageEventOptions, ReactionEventOptions, } from './events/registry.js';
21
+ export type { ContextOf, EventWhere, WhereEventOptions, EventContexts, EventName, EventOptions, MessageEventOptions, ReactionEventOptions, AddedReaction, ClearedReactions, DeletedInvite, DeletedMessage, PollVote, PresenceChange, ScheduledEventInterest, Typing, UserReaction, VoiceActivity, VoiceChange, VoiceMove, } from './events/registry.js';
22
22
  export type { CategoryChannel, Channel, DMChannel, FetchMessagesOptions, ForumChannel, ForumPostOptions, ForumTag, GuildChannel, GuildChannelEditOptions, InviteCreateOptions, PermissionOverwrite, TextBasedChannel, GuildTextBasedChannel, TextChannel, ThreadChannel, ThreadCreateOptions, ThreadEditOptions, ThreadMember, VoiceChannel, } from './structures/channel.js';
23
23
  export type { GuildEmoji } from './structures/emoji.js';
24
24
  export type { AuditLog, AuditLogEntry, AutoModerationRule, Ban, ChannelCreateOptions, Guild, GuildEditOptions, RoleCreateOptions, ScheduledEvent, Sticker, VoiceState, } from './structures/guild.js';
@@ -30,7 +30,9 @@ export type { CommandsTranslations, FrameworkTexts, LanguageConfig, LanguageDecl
30
30
  export { presence } from './presence/presence.js';
31
31
  export type { Activity, ActivityKind, PlainActivity, PresenceConfig, PresenceDeclaration, PresenceStatusName, StreamingActivity, } from './presence/presence.js';
32
32
  export { store } from './store/store.js';
33
- export type { ScopedStore, Store, StoreConfig, StoreEntry, StoreKey, StoreScope, StoreSetOptions, } from './store/store.js';
33
+ export type { ScopedStore, Store, StoreConfig, StoreEntry, StoreKey, StoreScope, StoreSetOptions, StoreStorage, } from './store/store.js';
34
+ export { ranking } from './ranking/ranking.js';
35
+ export type { Ranking, RankingConfig, RankingEntry, RankingOrder, ScopedRanking, } from './ranking/ranking.js';
34
36
  export { task } from './tasks/task.js';
35
37
  export type { CronTaskConfig, EveryTaskConfig, TaskConfig, TaskContext, TaskDeclaration, } from './tasks/task.js';
36
38
  export type { AssetFile, AssetOptions } from './assets/asset.js';
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ export { event } from './events/event.js';
22
22
  export { language } from './messages/messages.js';
23
23
  export { presence } from './presence/presence.js';
24
24
  export { store } from './store/store.js';
25
+ export { ranking } from './ranking/ranking.js';
25
26
  export { task } from './tasks/task.js';
26
27
  // Errors handlers can catch.
27
28
  export { DiscordApiError, DiscordUnavailableError, InvalidTokenError, } from './rest/errors.js';
@@ -0,0 +1,12 @@
1
+ import type { Declaration } from '../loader/loader.js';
2
+ import { type StoreStorage } from '../store/backend.js';
3
+ import type { StoreScope } from '../store/store.js';
4
+ import { type RankingOrder } from './ranking.js';
5
+ /** A ranking, checked: what the project keeps of it. */
6
+ export interface LoadedRanking {
7
+ readonly name: string;
8
+ readonly scope: StoreScope;
9
+ readonly storage: StoreStorage;
10
+ readonly order: RankingOrder;
11
+ }
12
+ export declare const rankingDeclaration: Declaration<LoadedRanking>;
@@ -0,0 +1,48 @@
1
+ // What a file declares with `ranking()`, checked when it loads, so a wrong
2
+ // scope, storage or order is explained before any score is kept; the
3
+ // ranking is then bound to the name of its export.
4
+ import { STORAGES } from '../store/backend.js';
5
+ import { bindRanking, isRankingDeclaration, rankingStateOf, } from './ranking.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 = ['scope', 'storage', 'order'];
11
+ const SCOPES = ['guild', 'global'];
12
+ const ORDERS = ['highest', 'lowest'];
13
+ /** A name can name a file in `data/`, like a store's. */
14
+ const NAME = /^[\w.-]+$/;
15
+ export const rankingDeclaration = {
16
+ one: 'ranking',
17
+ many: 'rankings',
18
+ is: isRankingDeclaration,
19
+ list: false,
20
+ read(value, { name }) {
21
+ const config = rankingStateOf(value)?.config;
22
+ if (!isRecord(config)) {
23
+ return fail("ranking() takes an object, or nothing: ranking({ order: 'lowest' }).");
24
+ }
25
+ for (const key of Object.keys(config)) {
26
+ if (!KEYS.includes(key)) {
27
+ fail(`"${key}" is not something a ranking has. It can have: ${KEYS.join(', ')}.`);
28
+ }
29
+ }
30
+ if (!NAME.test(name)) {
31
+ fail(`"${name}" can't name a ranking: the name of the export is the name of the ranking and of its file in data/, made of letters, digits, _ . and -.`);
32
+ }
33
+ const scope = (config.scope ?? 'guild');
34
+ if (!SCOPES.includes(scope)) {
35
+ fail(`"scope" is 'guild' (a ranking per server, the default) or 'global' (one ranking for the whole bot), got ${JSON.stringify(config.scope)}.`);
36
+ }
37
+ const storage = (config.storage ?? 'file');
38
+ if (!STORAGES.includes(storage)) {
39
+ fail(`"storage" is 'file' (a file in data/, the default) or 'redis' (the Redis of REDIS_URL in .env), got ${JSON.stringify(config.storage)}.`);
40
+ }
41
+ const order = (config.order ?? 'highest');
42
+ if (!ORDERS.includes(order)) {
43
+ fail(`"order" is 'highest' (the highest score first, the default) or 'lowest' (the lowest first), got ${JSON.stringify(config.order)}.`);
44
+ }
45
+ bindRanking(value, { name, scope, storage, order });
46
+ return Object.freeze({ name, scope, storage, order });
47
+ },
48
+ };