@seedcord/core 0.1.0-next.7 → 0.1.0-next.8

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