@seedcord/core 0.1.0-next.7 → 0.1.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 (59) hide show
  1. package/dist/{Plugin-3rlH0uxH.d.mts → Plugin-BZit5HUI.d.mts} +52 -117
  2. package/dist/{Plugin-Cgp1a0uQ.cjs → Plugin-BlkhHNRm.cjs} +6 -34
  3. package/dist/Plugin-BlkhHNRm.cjs.map +1 -0
  4. package/dist/{Plugin-j9GYdroS.mjs → Plugin-CDAV7P9l.mjs} +7 -29
  5. package/dist/Plugin-CDAV7P9l.mjs.map +1 -0
  6. package/dist/basic-1hebYVcN.d.mts +22 -0
  7. package/dist/contextMenuLeaves-f2rbyo83.d.mts +19 -0
  8. package/dist/{Silence-DiP95iwD.cjs → cursor-BMkFAwO2.cjs} +293 -16
  9. package/dist/cursor-BMkFAwO2.cjs.map +1 -0
  10. package/dist/{Silence-dbC2slOB.mjs → cursor-BvSH8tgw.mjs} +222 -17
  11. package/dist/cursor-BvSH8tgw.mjs.map +1 -0
  12. package/dist/{AutocompleteOptions-DLp_B8as.d.mts → cursor-j5bRcn2O.d.mts} +166 -24
  13. package/dist/{index-Dueqztyy.d.mts → index-DafutfX8.d.mts} +8 -15
  14. package/dist/index.cjs +362 -40
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.mts +244 -44
  17. package/dist/index.mjs +322 -7
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/internal.index.cjs +50 -43
  20. package/dist/internal.index.cjs.map +1 -1
  21. package/dist/internal.index.d.mts +46 -41
  22. package/dist/internal.index.mjs +13 -14
  23. package/dist/internal.index.mjs.map +1 -1
  24. package/dist/node-internal.index.cjs +196 -29
  25. package/dist/node-internal.index.cjs.map +1 -1
  26. package/dist/node-internal.index.d.mts +59 -9
  27. package/dist/node-internal.index.mjs +196 -30
  28. package/dist/node-internal.index.mjs.map +1 -1
  29. package/dist/{phases-BfpAG6w3.mjs → phases-BauKJOTT.mjs} +2 -2
  30. package/dist/phases-BauKJOTT.mjs.map +1 -0
  31. package/dist/{phases-C_BGNyVo.cjs → phases-DcuDHoQl.cjs} +2 -2
  32. package/dist/phases-DcuDHoQl.cjs.map +1 -0
  33. package/dist/plugin.index.cjs +1 -1
  34. package/dist/plugin.index.d.mts +2 -2
  35. package/dist/plugin.index.mjs +1 -1
  36. package/dist/{publishDefault-l0NQ-SHo.cjs → publishDefault-CVlwYUS5.cjs} +32 -3
  37. package/dist/publishDefault-CVlwYUS5.cjs.map +1 -0
  38. package/dist/{publishDefault-CCf1azzW.mjs → publishDefault-CldnxcT2.mjs} +27 -4
  39. package/dist/publishDefault-CldnxcT2.mjs.map +1 -0
  40. package/dist/slashRouteLeaves-BANDtPFm.mjs +23 -0
  41. package/dist/slashRouteLeaves-BANDtPFm.mjs.map +1 -0
  42. package/dist/slashRouteLeaves-nfxOAM7Q.cjs +28 -0
  43. package/dist/slashRouteLeaves-nfxOAM7Q.cjs.map +1 -0
  44. package/dist/{subscribers-DYqFVP1X.mjs → subscribers-CmDKqC7y.mjs} +5 -5
  45. package/dist/subscribers-CmDKqC7y.mjs.map +1 -0
  46. package/dist/{subscribers-B1G6_LlK.cjs → subscribers-CojcCgj1.cjs} +5 -5
  47. package/dist/subscribers-CojcCgj1.cjs.map +1 -0
  48. package/package.json +8 -8
  49. package/dist/Plugin-Cgp1a0uQ.cjs.map +0 -1
  50. package/dist/Plugin-j9GYdroS.mjs.map +0 -1
  51. package/dist/Silence-DiP95iwD.cjs.map +0 -1
  52. package/dist/Silence-dbC2slOB.mjs.map +0 -1
  53. package/dist/basic-D7bDlgSP.d.mts +0 -10
  54. package/dist/phases-BfpAG6w3.mjs.map +0 -1
  55. package/dist/phases-C_BGNyVo.cjs.map +0 -1
  56. package/dist/publishDefault-CCf1azzW.mjs.map +0 -1
  57. package/dist/publishDefault-l0NQ-SHo.cjs.map +0 -1
  58. package/dist/subscribers-B1G6_LlK.cjs.map +0 -1
  59. package/dist/subscribers-DYqFVP1X.mjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,12 +1,14 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_publishDefault = require('./publishDefault-l0NQ-SHo.cjs');
3
- const require_Silence = require('./Silence-DiP95iwD.cjs');
4
- const require_phases = require('./phases-C_BGNyVo.cjs');
5
- const require_subscribers = require('./subscribers-B1G6_LlK.cjs');
2
+ const require_publishDefault = require('./publishDefault-CVlwYUS5.cjs');
3
+ const require_cursor = require('./cursor-BMkFAwO2.cjs');
4
+ const require_phases = require('./phases-DcuDHoQl.cjs');
5
+ const require_subscribers = require('./subscribers-CojcCgj1.cjs');
6
6
  require("reflect-metadata");
7
+ let _discordjs_builders = require("@discordjs/builders");
8
+ let _seedcord_logger = require("@seedcord/logger");
9
+ let discord_api_types_v10 = require("discord-api-types/v10");
7
10
  let _seedcord_errors = require("@seedcord/errors");
8
11
  let _seedcord_errors_internal = require("@seedcord/errors/internal");
9
- let _seedcord_logger = require("@seedcord/logger");
10
12
  let _seedcord_utils = require("@seedcord/utils");
11
13
 
12
14
  //#region src/decorators/Command.ts
@@ -51,7 +53,7 @@ function RegisterCommand(scope, guilds = []) {
51
53
  */
52
54
  function SlashRoute(...routes) {
53
55
  return function(constructor) {
54
- require_Silence.storeInteractionRoute("slash", routes, constructor);
56
+ require_cursor.storeInteractionRoute("slash", routes, constructor);
55
57
  };
56
58
  }
57
59
  /**
@@ -76,7 +78,7 @@ function SlashRoute(...routes) {
76
78
  */
77
79
  function AutocompleteRoute(...routes) {
78
80
  return function(constructor) {
79
- require_Silence.storeInteractionRoute("autocomplete", routes, constructor);
81
+ require_cursor.storeInteractionRoute("autocomplete", routes, constructor);
80
82
  };
81
83
  }
82
84
  /**
@@ -102,7 +104,7 @@ function AutocompleteRoute(...routes) {
102
104
  */
103
105
  function ContextMenuRoute(kind, ...names) {
104
106
  return function(constructor) {
105
- require_Silence.storeInteractionRoute(require_Silence.contextMenuRouteOf(kind), names, constructor);
107
+ require_cursor.storeInteractionRoute(require_cursor.contextMenuRouteOf(kind), names, constructor);
106
108
  };
107
109
  }
108
110
  /**
@@ -118,7 +120,7 @@ function ContextMenuRoute(kind, ...names) {
118
120
  */
119
121
  function ButtonRoute(...defs) {
120
122
  return function(constructor) {
121
- require_Silence.storeComponentRoute("button", defs, constructor);
123
+ require_cursor.storeComponentRoute("button", defs, constructor);
122
124
  };
123
125
  }
124
126
  /**
@@ -134,7 +136,7 @@ function ButtonRoute(...defs) {
134
136
  */
135
137
  function ModalRoute(...defs) {
136
138
  return function(constructor) {
137
- require_Silence.storeComponentRoute("modal", defs, constructor);
139
+ require_cursor.storeComponentRoute("modal", defs, constructor);
138
140
  };
139
141
  }
140
142
  /**
@@ -155,7 +157,7 @@ function ModalRoute(...defs) {
155
157
  */
156
158
  function SelectMenuRoute(type, ...defs) {
157
159
  return function(constructor) {
158
- require_Silence.storeComponentRoute(require_Silence.selectMenuRouteOf(type), defs, constructor);
160
+ require_cursor.storeComponentRoute(require_cursor.selectMenuRouteOf(type), defs, constructor);
159
161
  };
160
162
  }
161
163
 
@@ -211,6 +213,7 @@ var BaseHandler = class {
211
213
  * @typeParam Event - The repliable interaction type this handler processes
212
214
  * @typeParam TCore - The transport's Core
213
215
  * @typeParam TMessage - The transport's created-message lens
216
+ * @typeParam TNative - The transport's own file forms, beyond the portable `ReplyFile`
214
217
  * @typeParam TSender - The transport's reply sender
215
218
  */
216
219
  var RepliableHandler = class extends BaseHandler {
@@ -280,21 +283,21 @@ var RepliableHandler = class extends BaseHandler {
280
283
  * }
281
284
  * ```
282
285
  */
283
- var Fault = class extends require_Silence.Notice {
286
+ var Fault = class extends require_cursor.Notice {
284
287
  constructor(options) {
285
288
  super("A fault occurred", options?.cause === void 0 ? void 0 : { cause: options.cause });
286
289
  this.report = options?.report ?? true;
287
290
  }
288
291
  render(ctx) {
289
- return { components: [new require_Silence.NoticeCard(`Something went wrong. Please reach out to ${ctx.developerUsername ?? "the developer"} with a way to reproduce the error and the following:\n### UUID: \`${ctx.uuid}\``, "Error").component] };
292
+ return { components: [new require_cursor.NoticeCard(`Something went wrong. Please reach out to ${ctx.developerUsername ?? "the developer"} with a way to reproduce the error and the following:\n### UUID: \`${ctx.uuid}\``, "Error").component] };
290
293
  }
291
294
  };
292
295
 
293
296
  //#endregion
294
297
  //#region src/gates/combinators.ts
295
298
  function and(...gates) {
296
- return require_Silence.defineGate(gates.map((gate) => gate.name).join(" & "), async (ctx) => {
297
- for (const gate of gates) await require_Silence.runCheck(gate, ctx);
299
+ return require_cursor.defineGate(gates.map((gate) => gate.name).join(" & "), async (ctx) => {
300
+ for (const gate of gates) await require_cursor.runCheck(gate, ctx);
298
301
  });
299
302
  }
300
303
  function isOrOptions(arg) {
@@ -304,17 +307,17 @@ function or(...args) {
304
307
  const last = args.at(-1);
305
308
  const options = last !== void 0 && isOrOptions(last) ? last : void 0;
306
309
  const gates = options ? args.slice(0, -1) : args;
307
- return require_Silence.defineGate(gates.map((gate) => gate.name).join(" | "), async (ctx) => {
310
+ return require_cursor.defineGate(gates.map((gate) => gate.name).join(" | "), async (ctx) => {
308
311
  const summaries = [];
309
312
  let everyArmHasSummary = true;
310
313
  for (const gate of gates) {
311
- const mark = require_Silence.markCommits(ctx);
314
+ const mark = require_cursor.markCommits(ctx);
312
315
  try {
313
- await require_Silence.runCheck(gate, ctx);
316
+ await require_cursor.runCheck(gate, ctx);
314
317
  return;
315
318
  } catch (error) {
316
- if (error instanceof require_Silence.Notice && !(error instanceof Fault) && !error.report) {
317
- require_Silence.rollbackCommits(ctx, mark);
319
+ if (error instanceof require_cursor.Notice && !(error instanceof Fault) && !error.report) {
320
+ require_cursor.rollbackCommits(ctx, mark);
318
321
  if (error.summary === void 0) everyArmHasSummary = false;
319
322
  else summaries.push(error.summary);
320
323
  continue;
@@ -323,8 +326,8 @@ function or(...args) {
323
326
  }
324
327
  }
325
328
  if (options) throw typeof options.fail === "function" ? options.fail(ctx) : options.fail;
326
- if (everyArmHasSummary && summaries.length > 0) throw new require_Silence.NeedsAny(summaries);
327
- throw new require_Silence.NotAllowed();
329
+ if (everyArmHasSummary && summaries.length > 0) throw new require_cursor.NeedsAny(summaries);
330
+ throw new require_cursor.NotAllowed();
328
331
  });
329
332
  }
330
333
 
@@ -357,10 +360,10 @@ function or(...args) {
357
360
  * ```
358
361
  */
359
362
  function OwnerOnly(options) {
360
- return require_Silence.defineGate("OwnerOnly", (ctx) => {
363
+ return require_cursor.defineGate("OwnerOnly", (ctx) => {
361
364
  const owners = ctx.core.config.ownerIds ?? [];
362
365
  if (ctx.userId !== null && owners.includes(ctx.userId)) return;
363
- throw require_Silence.pickNotice(options, (message) => new require_Silence.NotOwner(message));
366
+ throw require_cursor.pickNotice(options, (message) => new require_cursor.NotOwner(message));
364
367
  });
365
368
  }
366
369
  /**
@@ -386,9 +389,9 @@ function OwnerOnly(options) {
386
389
  * ```
387
390
  */
388
391
  function GuildOnly(options) {
389
- return require_Silence.defineGate("GuildOnly", (ctx) => {
392
+ return require_cursor.defineGate("GuildOnly", (ctx) => {
390
393
  if (ctx.guildId) return;
391
- throw require_Silence.pickNotice(options, (message) => new require_Silence.NotInGuild(message));
394
+ throw require_cursor.pickNotice(options, (message) => new require_cursor.NotInGuild(message));
392
395
  });
393
396
  }
394
397
  /**
@@ -412,9 +415,9 @@ function GuildOnly(options) {
412
415
  * ```
413
416
  */
414
417
  function DmOnly(options) {
415
- return require_Silence.defineGate("DmOnly", (ctx) => {
418
+ return require_cursor.defineGate("DmOnly", (ctx) => {
416
419
  if (!ctx.guildId) return;
417
- throw require_Silence.pickNotice(options, (message) => new require_Silence.NotInDm(message));
420
+ throw require_cursor.pickNotice(options, (message) => new require_cursor.NotInDm(message));
418
421
  });
419
422
  }
420
423
 
@@ -486,16 +489,124 @@ function Cooldown(duration, options) {
486
489
  };
487
490
  const anonId = anonSeq++;
488
491
  const keyOf = (ctx) => `cooldown:${ctx.routeId ?? `anon${anonId}`}:${per}:w${windowMs}:l${options?.limit ?? 1}:${scopeValue(ctx, per)}`;
489
- return require_Silence.defineEffectGate("Cooldown", async (ctx) => {
492
+ return require_cursor.defineEffectGate("Cooldown", async (ctx) => {
490
493
  const result = await ctx.core.rateLimiter.peek(keyOf(ctx), window);
491
494
  if (!result.limited) return;
492
495
  if (options?.notice) throw options.notice(result.resetAt);
493
- throw new require_Silence.OnCooldown(result.resetAt, options?.message?.(result.resetAt));
496
+ throw new require_cursor.OnCooldown(result.resetAt, options?.message?.(result.resetAt));
494
497
  }, async (ctx) => {
495
498
  await ctx.core.rateLimiter.charge(keyOf(ctx), window);
496
499
  });
497
500
  }
498
501
 
502
+ //#endregion
503
+ //#region src/permissions/assert.ts
504
+ /**
505
+ * Refuses when the subject is missing a permission in `scope`, or with `inverse` when it holds one.
506
+ * The Administrator bit passes any scope, and on the inverse arm it reports every bit in `scope`.
507
+ *
508
+ * The caller computes `permissions`, gateway off a discord.js role, member, or channel, and HTTP off the
509
+ * bitfield the interaction payload carries.
510
+ *
511
+ * @throws A {@link Notice} listing the missing or present permissions.
512
+ *
513
+ * @example
514
+ * ```ts
515
+ * import { assertPermissions } from '@seedcord/core';
516
+ * import { PermissionFlagsBits } from 'discord-api-types/v10';
517
+ *
518
+ * // gateway, the bits come off a discord.js member
519
+ * assertPermissions({
520
+ * subject: `<@${member.id}>`,
521
+ * permissions: member.permissions.bitfield,
522
+ * scope: [PermissionFlagsBits.BanMembers]
523
+ * });
524
+ * ```
525
+ *
526
+ * @example
527
+ * ```ts
528
+ * // HTTP, the payload carries the member's bits as a string
529
+ * assertPermissions({
530
+ * subject: `<@${this.event.member.user.id}>`,
531
+ * permissions: BigInt(this.event.member.permissions),
532
+ * scope: [PermissionFlagsBits.ManageMessages]
533
+ * });
534
+ * ```
535
+ *
536
+ * @example
537
+ * ```ts
538
+ * // inverse, refuse a role that carries Administrator
539
+ * assertPermissions({
540
+ * subject: `<@&${role.id}>`,
541
+ * permissions: role.permissions.bitfield,
542
+ * scope: [PermissionFlagsBits.Administrator],
543
+ * inverse: true
544
+ * });
545
+ * ```
546
+ *
547
+ * @example
548
+ * ```ts
549
+ * import { Notice } from '@seedcord/core';
550
+ *
551
+ * // a replacement notice takes the same three arguments the default one does
552
+ * class CannotBan extends Notice {
553
+ * public constructor(
554
+ * message: string | undefined,
555
+ * private readonly subject: string,
556
+ * private readonly missing: readonly string[]
557
+ * ) {
558
+ * super(message ?? 'You cannot ban here.');
559
+ * }
560
+ *
561
+ * public render(): ReplyResponse {
562
+ * return { components: [new BanDenied(this.subject, this.missing).component] };
563
+ * }
564
+ * }
565
+ *
566
+ * assertPermissions({
567
+ * subject: `<@${member.id}>`,
568
+ * permissions: member.permissions.bitfield,
569
+ * scope: [PermissionFlagsBits.BanMembers],
570
+ * missingNotice: CannotBan
571
+ * });
572
+ * ```
573
+ */
574
+ function assertPermissions(check) {
575
+ const { subject, permissions, scope, inverse = false, missingNotice, dangerousNotice } = check;
576
+ if (inverse) {
577
+ const present = require_cursor.presentNames(permissions, scope);
578
+ if (present.length === 0) return;
579
+ throw new (dangerousNotice ?? require_cursor.HasDangerousPermissions)(void 0, subject, present);
580
+ }
581
+ if (require_cursor.hasAll(permissions, scope)) return;
582
+ throw new (missingNotice ?? require_cursor.MissingPermissions)(void 0, subject, require_cursor.missingNames(permissions, scope));
583
+ }
584
+
585
+ //#endregion
586
+ //#region src/permissions/mergeRoles.ts
587
+ /**
588
+ * The role ids a member holds after adding and removing the given sets. Removals win over additions.
589
+ *
590
+ * This does not write the roles. Apply the result with `member.roles.set()` on gateway, or PATCH the member over HTTP.
591
+ *
592
+ * @param current - The role ids the member holds.
593
+ * @param add - The role ids to add.
594
+ * @param remove - The role ids to remove.
595
+ * @returns The merged role ids.
596
+ *
597
+ * @example
598
+ * ```ts
599
+ * import { mergeRoles } from '@seedcord/core';
600
+ *
601
+ * // gateway
602
+ * const ids = mergeRoles(member.roles.cache.map((role) => role.id), ['123'], ['456']);
603
+ * await member.roles.set(ids);
604
+ * ```
605
+ */
606
+ function mergeRoles(current, add, remove) {
607
+ return [...new Set(current).union(new Set(add)).difference(new Set(remove))];
608
+ }
609
+
499
610
  //#endregion
500
611
  //#region src/pagination/paginate.ts
501
612
  /**
@@ -522,37 +633,246 @@ function paginate(items, page, perPage) {
522
633
  };
523
634
  }
524
635
 
636
+ //#endregion
637
+ //#region src/pagination/controls.ts
638
+ const DEFAULT_LABEL = {
639
+ first: "First",
640
+ prev: "Prev",
641
+ indicator: "",
642
+ next: "Next",
643
+ last: "Last"
644
+ };
645
+ const CONTROL_SLOT = {
646
+ first: 0,
647
+ prev: 1,
648
+ indicator: 2,
649
+ next: 3,
650
+ last: 4
651
+ };
652
+ const MAX_BUTTONS_PER_ROW = 5;
653
+ var Controls = class {
654
+ cursor;
655
+ view;
656
+ constructor(cursor, view) {
657
+ this.cursor = cursor;
658
+ this.view = view;
659
+ }
660
+ button(key, cosmetics) {
661
+ const { target, disabled } = this.navState(key);
662
+ const defaultLabel = key === "indicator" ? this.indicatorText() : DEFAULT_LABEL[key];
663
+ const label = cosmetics?.label ?? (key === "indicator" || !cosmetics?.emoji ? defaultLabel : void 0);
664
+ const button = new _discordjs_builders.ButtonBuilder().setCustomId(this.cursor.encode({
665
+ page: Math.min(Math.max(0, target), require_cursor.PAGE_MAX),
666
+ slot: CONTROL_SLOT[key]
667
+ })).setStyle(cosmetics?.style ?? discord_api_types_v10.ButtonStyle.Secondary).setDisabled(disabled);
668
+ if (label !== void 0) button.setLabel(label);
669
+ if (cosmetics?.emoji) button.setEmoji(cosmetics.emoji);
670
+ return button;
671
+ }
672
+ navState(key) {
673
+ const { page, totalPages, hasPrev, hasNext } = this.view;
674
+ const knownTotal = totalPages !== void 0;
675
+ return {
676
+ target: {
677
+ first: 0,
678
+ prev: page - 1,
679
+ indicator: page,
680
+ next: page + 1,
681
+ last: knownTotal ? totalPages - 1 : page
682
+ }[key],
683
+ disabled: {
684
+ first: !hasPrev,
685
+ prev: !hasPrev,
686
+ indicator: true,
687
+ next: !hasNext || page + 1 > 1e6,
688
+ last: !hasNext || !knownTotal || totalPages - 1 > 1e6
689
+ }[key]
690
+ };
691
+ }
692
+ row(...keys) {
693
+ if (keys.length === 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationEmptyControls);
694
+ if (keys.length > MAX_BUTTONS_PER_ROW) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationTooManyControls, [keys.length]);
695
+ const seen = /* @__PURE__ */ new Set();
696
+ for (const key of keys) {
697
+ if (seen.has(key)) throw new _seedcord_errors_internal.SeedcordTypeError(_seedcord_errors.SeedcordErrorCode.PaginationDuplicateControls, [key]);
698
+ seen.add(key);
699
+ }
700
+ return new _discordjs_builders.ActionRowBuilder().addComponents(keys.map((key) => this.button(key)));
701
+ }
702
+ indicatorText() {
703
+ const { page, totalPages } = this.view;
704
+ return totalPages === void 0 ? `Page ${page + 1}` : `Page ${page + 1} of ${totalPages}`;
705
+ }
706
+ };
707
+
708
+ //#endregion
709
+ //#region src/pagination/render.ts
710
+ var PageContainer = class extends require_publishDefault.BuilderComponent {
711
+ constructor(view, renderItem, controlRow) {
712
+ super("container");
713
+ const base = view.page * view.perPage;
714
+ let lines = [];
715
+ const flush = () => {
716
+ if (lines.length === 0) return;
717
+ this.instance.addTextDisplayComponents(new _discordjs_builders.TextDisplayBuilder().setContent(lines.join("\n")));
718
+ lines = [];
719
+ };
720
+ view.items.forEach((item, offset) => {
721
+ const rendered = renderItem(item, base + offset);
722
+ if (typeof rendered === "string") {
723
+ lines.push(rendered);
724
+ return;
725
+ }
726
+ flush();
727
+ this.instance.addSectionComponents(rendered.component);
728
+ });
729
+ flush();
730
+ this.instance.addActionRowComponents(controlRow);
731
+ }
732
+ };
733
+ function toReplyResponse(renderable) {
734
+ if (typeof renderable === "string") return { components: [new _discordjs_builders.TextDisplayBuilder().setContent(renderable)] };
735
+ if (Array.isArray(renderable)) return { components: renderable };
736
+ return renderable;
737
+ }
738
+ const ARRAY_KEYS = [
739
+ "first",
740
+ "prev",
741
+ "indicator",
742
+ "next",
743
+ "last"
744
+ ];
745
+ const CURSOR_KEYS = [
746
+ "prev",
747
+ "indicator",
748
+ "next"
749
+ ];
750
+ /**
751
+ * A `render` override builds the whole tree, otherwise the default container lists the items and appends
752
+ * the controls (all five for a known total, prev/indicator/next for a cursor source).
753
+ */
754
+ function renderPage(view, cursor, config) {
755
+ const controls = new Controls(cursor, view);
756
+ if (config.render) return toReplyResponse(config.render(view, controls));
757
+ return { components: [new PageContainer(view, config.renderItem ?? String, controls.row(...view.totalPages === void 0 ? CURSOR_KEYS : ARRAY_KEYS)).component] };
758
+ }
759
+
760
+ //#endregion
761
+ //#region src/pagination/PaginatorBase.ts
762
+ /**
763
+ * Holds a paginator's cursor and renders one page. Each transport extends this to add the nav handler and
764
+ * the method that sends the first page.
765
+ *
766
+ * @typeParam Item - The item type, inferred from the source.
767
+ * @typeParam Prefix - The route prefix, inferred from `config.prefix`.
768
+ * @typeParam Ctx - The transport's page context.
769
+ */
770
+ var PaginatorBase = class {
771
+ /** The page cursor, pass it to your `@ButtonRoute`. */
772
+ cursor;
773
+ config;
774
+ constructor(config) {
775
+ this.cursor = require_cursor.pageCursor(config.prefix);
776
+ this.config = config;
777
+ }
778
+ /** Render a page as a {@link ReplyResponse}. To post it elsewhere, add `flags: MessageFlags.IsComponentsV2`. */
779
+ async page(ctx, n) {
780
+ return renderPage(await this.config.source.page(ctx, n), this.cursor, this.config);
781
+ }
782
+ };
783
+
784
+ //#endregion
785
+ //#region src/pagination/sources.ts
786
+ const DEFAULT_PER_PAGE = 10;
787
+ function perPageOf(opts) {
788
+ const perPage = opts?.perPage ?? DEFAULT_PER_PAGE;
789
+ if (!Number.isSafeInteger(perPage) || perPage <= 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);
790
+ return perPage;
791
+ }
792
+ /**
793
+ * A source for a bounded list you can load whole. It loads the full list on every click, then slices the
794
+ * page via {@link paginate}, so the real total is known (a real last button, "Page X of Y"). Cache inside
795
+ * your loader if the load is expensive.
796
+ *
797
+ * @typeParam Item - The item type, inferred from the loader's return.
798
+ * @typeParam Ctx - The transport's page context. Each transport exports a subclass that fixes it.
799
+ */
800
+ var ArraySource = class {
801
+ load;
802
+ perPage;
803
+ constructor(load, opts) {
804
+ this.load = load;
805
+ this.perPage = perPageOf(opts);
806
+ }
807
+ async page(ctx, n) {
808
+ return paginate(await this.load(ctx), n, this.perPage);
809
+ }
810
+ };
811
+ /**
812
+ * A source for a large or unknown-length set you fetch one page at a time (SQL LIMIT/OFFSET, a paged API).
813
+ * The fetcher receives the page index and the page size and reports whether a next page exists. A cursor
814
+ * source has no cheap total, so `totalPages` is undefined, the last button is omitted, and the indicator
815
+ * reads "Page X".
816
+ *
817
+ * @typeParam Item - The item type, inferred from the fetcher's slice.
818
+ * @typeParam Ctx - The transport's page context. Each transport exports a subclass that fixes it.
819
+ */
820
+ var CursorSource = class {
821
+ fetch;
822
+ perPage;
823
+ constructor(fetch, opts) {
824
+ this.fetch = fetch;
825
+ this.perPage = perPageOf(opts);
826
+ }
827
+ async page(ctx, n) {
828
+ const page = Math.max(0, Math.trunc(n));
829
+ const { items, hasNext } = await this.fetch(ctx, page, this.perPage);
830
+ return {
831
+ items: [...items],
832
+ page,
833
+ perPage: this.perPage,
834
+ hasPrev: page > 0,
835
+ hasNext
836
+ };
837
+ }
838
+ };
839
+
525
840
  //#endregion
526
841
  //#region src/index.ts
527
842
  /** Package version */
528
- const version = "0.1.0-next.7";
843
+ const version = "0.1.0";
529
844
 
530
845
  //#endregion
846
+ exports.ArraySource = ArraySource;
531
847
  exports.AutocompleteRoute = AutocompleteRoute;
532
848
  exports.BaseHandler = BaseHandler;
533
849
  exports.BuilderComponent = require_publishDefault.BuilderComponent;
534
850
  exports.Bus = require_subscribers.Bus;
535
851
  exports.ButtonRoute = ButtonRoute;
852
+ exports.Commands = require_cursor.Commands;
536
853
  exports.ContextMenuRoute = ContextMenuRoute;
854
+ exports.ContextMenus = require_cursor.ContextMenus;
537
855
  exports.Cooldown = Cooldown;
538
- exports.CustomId = require_Silence.CustomId;
856
+ exports.CursorSource = CursorSource;
857
+ exports.CustomId = require_cursor.CustomId;
539
858
  exports.DispatchContext = DispatchContext;
540
859
  exports.DmOnly = DmOnly;
541
860
  exports.Fault = Fault;
542
861
  exports.GuildOnly = GuildOnly;
543
862
  exports.ModalRoute = ModalRoute;
544
- exports.Notice = require_Silence.Notice;
863
+ exports.Notice = require_cursor.Notice;
545
864
  exports.OwnerOnly = OwnerOnly;
865
+ exports.PaginatorBase = PaginatorBase;
546
866
  exports.RegisterCommand = RegisterCommand;
547
867
  exports.RepliableHandler = RepliableHandler;
548
- exports.RequireBotPermissions = require_Silence.RequireBotPermissions;
549
- exports.RequirePermissions = require_Silence.RequirePermissions;
550
- exports.RequireRole = require_Silence.RequireRole;
868
+ exports.RequireBotPermissions = require_cursor.RequireBotPermissions;
869
+ exports.RequirePermissions = require_cursor.RequirePermissions;
870
+ exports.RequireRole = require_cursor.RequireRole;
551
871
  exports.RowComponent = require_publishDefault.RowComponent;
552
- exports.SelectMenuKind = require_Silence.SelectMenuKind;
872
+ exports.SelectMenuKind = require_cursor.SelectMenuKind;
553
873
  exports.SelectMenuRoute = SelectMenuRoute;
554
874
  exports.ShutdownPhase = require_phases.ShutdownPhase;
555
- exports.Silence = require_Silence.Silence;
875
+ exports.Silence = require_cursor.Silence;
556
876
  exports.SlashRoute = SlashRoute;
557
877
  exports.StartupPhase = require_phases.StartupPhase;
558
878
  exports.Subscribe = require_subscribers.Subscribe;
@@ -560,8 +880,10 @@ exports.Subscriber = require_subscribers.Subscriber;
560
880
  exports.WebhookLog = require_subscribers.WebhookLog;
561
881
  exports.WebhookUrl = require_subscribers.WebhookUrl;
562
882
  exports.and = and;
563
- exports.defineEffectGate = require_Silence.defineEffectGate;
564
- exports.defineGate = require_Silence.defineGate;
883
+ exports.assertPermissions = assertPermissions;
884
+ exports.defineEffectGate = require_cursor.defineEffectGate;
885
+ exports.defineGate = require_cursor.defineGate;
886
+ exports.mergeRoles = mergeRoles;
565
887
  exports.or = or;
566
888
  exports.paginate = paginate;
567
889
  exports.version = version;