@seedcord/core 0.3.1 → 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 (38) hide show
  1. package/dist/{Bus-BvF4VShN.mjs → Bus-BVwmKc-A.mjs} +36 -17
  2. package/dist/Bus-BVwmKc-A.mjs.map +1 -0
  3. package/dist/Pluggable-Cf5-WWmW.mjs +280 -0
  4. package/dist/Pluggable-Cf5-WWmW.mjs.map +1 -0
  5. package/dist/Pluggable-DFNUIlJM.d.mts +148 -0
  6. package/dist/{Plugin-D9ykXA5g.mjs → Plugin-C6oMqWf8.mjs} +7 -3
  7. package/dist/Plugin-C6oMqWf8.mjs.map +1 -0
  8. package/dist/{Plugin-CQ9IEj5X.d.mts → Plugin-CG9pWbPF.d.mts} +2 -2
  9. package/dist/{cursor-BANzac53.d.mts → cursor-BcRfmTSo.d.mts} +26 -72
  10. package/dist/{cursor-CuoA9cRh.mjs → cursor-GBUYz3jk.mjs} +378 -30
  11. package/dist/cursor-GBUYz3jk.mjs.map +1 -0
  12. package/dist/{index-rt8rZg4d.d.mts → index-ZzdQafFv.d.mts} +2 -2
  13. package/dist/index.d.mts +77 -19
  14. package/dist/index.mjs +186 -19
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/internal.index.d.mts +11 -5
  17. package/dist/internal.index.mjs +8 -455
  18. package/dist/internal.index.mjs.map +1 -1
  19. package/dist/node-internal.index.d.mts +6 -133
  20. package/dist/node-internal.index.mjs +15 -472
  21. package/dist/node-internal.index.mjs.map +1 -1
  22. package/dist/node.index.d.mts +2 -0
  23. package/dist/node.index.mjs +256 -0
  24. package/dist/node.index.mjs.map +1 -0
  25. package/dist/{phases-hBxMCD2V.d.mts → phases-F8yvPd60.d.mts} +104 -72
  26. package/dist/plugin.index.d.mts +1 -1
  27. package/dist/plugin.index.mjs +1 -1
  28. package/dist/publishDefault-D826XXDQ.mjs +14 -0
  29. package/dist/publishDefault-D826XXDQ.mjs.map +1 -0
  30. package/dist/{slashRouteLeaves-De1SxqnX.mjs → slashRouteLeaves-CbYRwqMY.mjs} +10 -2
  31. package/dist/slashRouteLeaves-CbYRwqMY.mjs.map +1 -0
  32. package/dist/{subscribers-wnMyNYZd.mjs → subscribers-CGGQkUfM.mjs} +2 -2
  33. package/dist/{subscribers-wnMyNYZd.mjs.map → subscribers-CGGQkUfM.mjs.map} +1 -1
  34. package/package.json +14 -8
  35. package/dist/Bus-BvF4VShN.mjs.map +0 -1
  36. package/dist/Plugin-D9ykXA5g.mjs.map +0 -1
  37. package/dist/cursor-CuoA9cRh.mjs.map +0 -1
  38. package/dist/slashRouteLeaves-De1SxqnX.mjs.map +0 -1
@@ -1,9 +1,11 @@
1
- import { b as InteractionRoutes, f as BuilderComponent, v as InteractionMetadataKey, y as InteractionRouteKeys } from "./Bus-BvF4VShN.mjs";
1
+ import { C as InteractionMetadataKey, T as InteractionRoutes, g as BuilderComponent, w as InteractionRouteKeys } from "./Bus-BVwmKc-A.mjs";
2
+ import { n as asError, t as PublishDefault } from "./publishDefault-D826XXDQ.mjs";
2
3
  import "reflect-metadata";
3
4
  import { TextDisplayBuilder } from "@discordjs/builders";
4
- import { SeedcordErrorCode } from "@seedcord/errors";
5
+ import { SeedcordErrorCode, paint } from "@seedcord/errors";
6
+ import { Logger } from "@seedcord/logger";
5
7
  import { ApplicationCommandType, PermissionFlagsBits } from "discord-api-types/v10";
6
- import { SeedcordError, SeedcordRangeError } from "@seedcord/errors/internal";
8
+ import { SeedcordError, SeedcordRangeError, SeedcordTypeError } from "@seedcord/errors/internal";
7
9
  import { prettify, toEpochSeconds } from "@seedcord/utils";
8
10
 
9
11
  //#region src/stops/Notice.ts
@@ -60,8 +62,9 @@ var Notice = class extends Error {
60
62
  */
61
63
  ephemeral = true;
62
64
  /**
63
- * A short one-line reason. When every arm of an `or` gate refuses and each refusal sets this, `or`
64
- * lists them as the reply.
65
+ * A short one-line reason. When every arm of an {@link or} gate refuses and each one sets this, `or`
66
+ * throws a refusal listing them under `You need to meet at least one of these:`. Write a verb phrase that
67
+ * finishes that line, for example, "be the bot owner".
65
68
  */
66
69
  summary;
67
70
  constructor(message, options) {
@@ -181,6 +184,9 @@ function isBounded(field) {
181
184
  return field.kind === "snowflake" || field.kind === "uuid" || field.kind === "bool" || field.kind === "oneOf";
182
185
  }
183
186
  function radixOf(field) {
187
+ return kindRadix(field) + (field.nullable === true ? 1n : 0n);
188
+ }
189
+ function kindRadix(field) {
184
190
  switch (field.kind) {
185
191
  case "snowflake": return 1n << 64n;
186
192
  case "uuid": return 1n << 128n;
@@ -195,6 +201,13 @@ function radixOf(field) {
195
201
  }
196
202
  }
197
203
  function boundedToBigint(field, name, value) {
204
+ if (field.nullable === true) {
205
+ if (value === null) return 0n;
206
+ return boundedToBigint({
207
+ ...field,
208
+ nullable: false
209
+ }, name, value) + 1n;
210
+ }
198
211
  const slot = boundedSlot(field, name, value);
199
212
  if (slot < 0n || slot >= radixOf(field)) outOfRange(name, value);
200
213
  return slot;
@@ -225,6 +238,13 @@ function outOfRange(name, value) {
225
238
  throw new SeedcordRangeError(SeedcordErrorCode.CustomIdValueOutOfRange, [name, String(value)]);
226
239
  }
227
240
  function bigintToBoundedValue(field, slot) {
241
+ if (field.nullable === true) return slot === 0n ? null : bigintToBoundedValue({
242
+ ...field,
243
+ nullable: false
244
+ }, slot - 1n);
245
+ return kindValue(field, slot);
246
+ }
247
+ function kindValue(field, slot) {
228
248
  switch (field.kind) {
229
249
  case "snowflake": return slot.toString();
230
250
  case "uuid": return bigintToUuid(slot);
@@ -238,7 +258,16 @@ function bigintToUuid(value) {
238
258
  const hex = value.toString(16).padStart(32, "0");
239
259
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
240
260
  }
261
+ const ABSENT = "0";
262
+ const PRESENT = "1";
241
263
  function encodeUnboundedToken(field, name, value) {
264
+ if (field.nullable === true) {
265
+ if (value === null) return ABSENT;
266
+ return PRESENT + encodeUnboundedToken({
267
+ ...field,
268
+ nullable: false
269
+ }, name, value);
270
+ }
242
271
  if (field.kind === "int") {
243
272
  if (!Number.isSafeInteger(value)) outOfRange(name, value);
244
273
  return bigintToBase64(zigzagEncode(value));
@@ -246,6 +275,15 @@ function encodeUnboundedToken(field, name, value) {
246
275
  return escapeToken(value);
247
276
  }
248
277
  function decodeUnboundedToken(field, piece) {
278
+ if (field.nullable === true) {
279
+ const marker = piece.charAt(0);
280
+ if (marker === ABSENT && piece.length === 1) return null;
281
+ if (marker !== PRESENT) throw new InvalidCustomId(`bad presence marker ${JSON.stringify(marker)}`);
282
+ return decodeUnboundedToken({
283
+ ...field,
284
+ nullable: false
285
+ }, piece.slice(1));
286
+ }
249
287
  if (field.kind !== "int") return unescapeToken(piece);
250
288
  if (piece === "") throw new InvalidCustomId("empty integer token");
251
289
  const decoded = zigzagDecode(base64ToBigint(piece));
@@ -258,6 +296,7 @@ function computeLayoutHash(shape) {
258
296
  name,
259
297
  field.kind,
260
298
  isBounded(field),
299
+ field.nullable === true,
261
300
  field.kind === "oneOf" ? field.choices ?? [] : null,
262
301
  field.kind === "int" ? [field.min ?? null, field.max ?? null] : null
263
302
  ]));
@@ -363,14 +402,14 @@ var CustomId = class CustomId {
363
402
  /** The prefix plus a short hash of the shape, the part of the wire before the colon. */
364
403
  routeKey;
365
404
  constructor(prefix, shape = {}) {
366
- if (!prefix || /[:\x1B\x1F]/.test(prefix)) throw new SeedcordError(SeedcordErrorCode.CustomIdInvalidPrefix, [prefix]);
405
+ if (!prefix || /[:\x1B\x1F]/.test(prefix)) throw new SeedcordTypeError(SeedcordErrorCode.CustomIdInvalidPrefix, [prefix]);
367
406
  this.prefix = prefix;
368
407
  this.shape = shape;
369
408
  this.routeKey = prefix + computeLayoutHash(shape);
370
409
  }
371
410
  add(name, field) {
372
- if (/^(?:0|[1-9]\d*)$/.test(name)) throw new SeedcordError(SeedcordErrorCode.CustomIdReservedFieldName, [name]);
373
- if (name in this.shape) throw new SeedcordError(SeedcordErrorCode.CustomIdDuplicateFieldName, [name]);
411
+ if (/^(?:0|[1-9]\d*)$/.test(name)) throw new SeedcordTypeError(SeedcordErrorCode.CustomIdReservedFieldName, [name]);
412
+ if (name in this.shape) throw new SeedcordTypeError(SeedcordErrorCode.CustomIdDuplicateFieldName, [name]);
374
413
  const shape = {
375
414
  ...this.shape,
376
415
  [name]: field
@@ -385,8 +424,11 @@ var CustomId = class CustomId {
385
424
  * new CustomId('ban').snowflake('userId');
386
425
  * ```
387
426
  */
388
- snowflake(name) {
389
- return this.add(name, { kind: "snowflake" });
427
+ snowflake(name, opts) {
428
+ return this.add(name, {
429
+ ...opts,
430
+ kind: "snowflake"
431
+ });
390
432
  }
391
433
  /**
392
434
  * Add a UUID field, decoded as a lowercase uuid string.
@@ -396,21 +438,29 @@ var CustomId = class CustomId {
396
438
  * new CustomId('ticket').uuid('ticketId');
397
439
  * ```
398
440
  */
399
- uuid(name) {
400
- return this.add(name, { kind: "uuid" });
441
+ uuid(name, opts) {
442
+ return this.add(name, {
443
+ ...opts,
444
+ kind: "uuid"
445
+ });
401
446
  }
402
- int(name, min, max) {
403
- if (min !== void 0 && max !== void 0 && min > max) throw new SeedcordError(SeedcordErrorCode.CustomIdInvalidBounds, [
447
+ int(name, minOrOpts, max, opts) {
448
+ const min = typeof minOrOpts === "number" ? minOrOpts : void 0;
449
+ if (min !== void 0 && max !== void 0 && min > max) throw new SeedcordRangeError(SeedcordErrorCode.CustomIdInvalidBounds, [
404
450
  name,
405
451
  min,
406
452
  max
407
453
  ]);
408
- const field = min === void 0 || max === void 0 ? { kind: "int" } : {
409
- kind: "int",
454
+ const options = typeof minOrOpts === "number" ? opts : minOrOpts;
455
+ const bounds = min === void 0 || max === void 0 ? {} : {
410
456
  min,
411
457
  max
412
458
  };
413
- return this.add(name, field);
459
+ return this.add(name, {
460
+ ...options,
461
+ kind: "int",
462
+ ...bounds
463
+ });
414
464
  }
415
465
  /**
416
466
  * Add a boolean flag.
@@ -420,8 +470,11 @@ var CustomId = class CustomId {
420
470
  * new CustomId('settings').bool('silent');
421
471
  * ```
422
472
  */
423
- bool(name) {
424
- return this.add(name, { kind: "bool" });
473
+ bool(name, opts) {
474
+ return this.add(name, {
475
+ ...opts,
476
+ kind: "bool"
477
+ });
425
478
  }
426
479
  /**
427
480
  * Add a field that is one value from a fixed list, decoded as the literal union. No `as const` needed.
@@ -431,9 +484,10 @@ var CustomId = class CustomId {
431
484
  * new CustomId('poll').oneOf('choice', ['yes', 'no', 'abstain']);
432
485
  * ```
433
486
  */
434
- oneOf(name, choices) {
435
- if (choices.length === 0) throw new SeedcordError(SeedcordErrorCode.CustomIdEmptyChoices, [name]);
487
+ oneOf(name, choices, opts) {
488
+ if (choices.length === 0) throw new SeedcordRangeError(SeedcordErrorCode.CustomIdEmptyChoices, [name]);
436
489
  return this.add(name, {
490
+ ...opts,
437
491
  kind: "oneOf",
438
492
  choices
439
493
  });
@@ -447,8 +501,11 @@ var CustomId = class CustomId {
447
501
  * new CustomId('note').str('message');
448
502
  * ```
449
503
  */
450
- str(name) {
451
- return this.add(name, { kind: "string" });
504
+ str(name, opts) {
505
+ return this.add(name, {
506
+ ...opts,
507
+ kind: "string"
508
+ });
452
509
  }
453
510
  /**
454
511
  * Mint a wire string from values. Throws if a value is out of its field's range or the wire is over 100 chars.
@@ -566,6 +623,285 @@ function contextMenuRouteOf(kind) {
566
623
  return kind === ApplicationCommandType.User ? "userContextMenu" : "messageContextMenu";
567
624
  }
568
625
 
626
+ //#endregion
627
+ //#region src/reply/ackLegality.ts
628
+ const showModalReason = {
629
+ reason: "this interaction was already acknowledged",
630
+ alternative: "showModal() must be the initial response."
631
+ };
632
+ function deferReason(what) {
633
+ return {
634
+ reason: `this interaction was already acknowledged with ${what}`,
635
+ alternative: "A deferral can only be the initial response."
636
+ };
637
+ }
638
+ const ILLEGAL = {
639
+ reply: {
640
+ "deferred-reply": {
641
+ reason: "this interaction was already deferred",
642
+ alternative: "Use edit() to fill the deferred reply or followUp() for a new message."
643
+ },
644
+ "deferred-update": {
645
+ reason: "this interaction was already acknowledged with a deferred update",
646
+ alternative: "Use followUp() for a new message."
647
+ },
648
+ replied: {
649
+ reason: "this interaction was already replied to",
650
+ alternative: "Use followUp() for a new message or edit() to rewrite the reply."
651
+ }
652
+ },
653
+ defer: {
654
+ "deferred-reply": deferReason("a deferred reply"),
655
+ "deferred-update": deferReason("a deferred update"),
656
+ replied: deferReason("a reply")
657
+ },
658
+ deferUpdate: {
659
+ "deferred-reply": deferReason("a deferred reply"),
660
+ "deferred-update": deferReason("a deferred update"),
661
+ replied: deferReason("a reply")
662
+ },
663
+ update: {
664
+ "deferred-reply": {
665
+ reason: "this interaction was deferred as a reply",
666
+ alternative: "The placeholder is yours via edit(). update() rewrites a source message."
667
+ },
668
+ replied: {
669
+ reason: "this interaction was already replied to",
670
+ alternative: "update() rewrites a source message. Use edit() or followUp() instead."
671
+ }
672
+ },
673
+ followUp: { unacked: {
674
+ reason: "this interaction has not been acknowledged yet",
675
+ alternative: "Ack first with reply() or defer()."
676
+ } },
677
+ edit: { unacked: {
678
+ reason: "nothing has been sent yet",
679
+ alternative: "Send with reply() or defer() first, then edit() rewrites it."
680
+ } },
681
+ delete: { unacked: {
682
+ reason: "nothing has been sent yet",
683
+ alternative: "Acknowledge first with reply() or defer(), then delete() removes it."
684
+ } },
685
+ showModal: {
686
+ "deferred-reply": showModalReason,
687
+ "deferred-update": showModalReason,
688
+ replied: showModalReason
689
+ }
690
+ };
691
+ const SEND_TARGET = {
692
+ unacked: "reply",
693
+ "deferred-reply": "edit",
694
+ "deferred-update": "followUp",
695
+ replied: "followUp"
696
+ };
697
+ function checkAckLegality(method, state, routeId, ackedBy) {
698
+ if (method === "send") return;
699
+ const illegal = ILLEGAL[method][state];
700
+ if (!illegal) return;
701
+ throw new SeedcordError(SeedcordErrorCode.ReplyIllegalAckState, [
702
+ method,
703
+ illegal.reason,
704
+ illegal.alternative,
705
+ routeId
706
+ ], { cause: ackedBy });
707
+ }
708
+ function sendTarget(state) {
709
+ return SEND_TARGET[state];
710
+ }
711
+
712
+ //#endregion
713
+ //#region src/reply/AckTrace.ts
714
+ var AckTrace = class extends Error {
715
+ constructor(method) {
716
+ super(`${method}() acknowledged this interaction`);
717
+ this.name = "AckTrace";
718
+ }
719
+ };
720
+
721
+ //#endregion
722
+ //#region src/reply/responseReport.ts
723
+ let replyLogger;
724
+ function logger() {
725
+ replyLogger ??= new Logger("Reply", { channel: "interactions" });
726
+ return replyLogger;
727
+ }
728
+ /** @internal */
729
+ function publishResponse(telemetry, report) {
730
+ const durationMs = performance.now() - report.startedAt;
731
+ const write = {
732
+ routeId: report.routeId,
733
+ interactionId: telemetry.interactionId,
734
+ method: report.method,
735
+ durationMs
736
+ };
737
+ telemetry.bus[PublishDefault]("responseAttempted", report.outcome === "failed" ? {
738
+ ...write,
739
+ outcome: "failed",
740
+ messageId: null,
741
+ error: report.error
742
+ } : {
743
+ ...write,
744
+ outcome: "sent",
745
+ messageId: report.messageId
746
+ });
747
+ logger().trace(`${paint.sky.bold(report.routeId)} ${report.method} ${report.outcome} ${paint.mute("in")} ${Math.round(durationMs)}ms`);
748
+ }
749
+ async function reportedWrite(telemetry, routeId, method, write) {
750
+ const startedAt = performance.now();
751
+ const result = await attemptWrite(telemetry, routeId, method, startedAt, write);
752
+ publishResponse(telemetry, {
753
+ routeId,
754
+ method,
755
+ startedAt,
756
+ outcome: "sent",
757
+ messageId: null
758
+ });
759
+ return result;
760
+ }
761
+ async function attemptWrite(telemetry, routeId, method, startedAt, write) {
762
+ try {
763
+ return await write();
764
+ } catch (caught) {
765
+ publishResponse(telemetry, {
766
+ routeId,
767
+ method,
768
+ startedAt,
769
+ outcome: "failed",
770
+ error: asError(caught)
771
+ });
772
+ throw caught;
773
+ }
774
+ }
775
+
776
+ //#endregion
777
+ //#region src/reply/translateSerialization.ts
778
+ const MAX_DEPTH = 8;
779
+ const OPERATOR_WORD = {
780
+ "<=": "at most",
781
+ ">=": "at least",
782
+ "<": "fewer than",
783
+ ">": "more than"
784
+ };
785
+ const KIND_WORD = {
786
+ string: "a string",
787
+ number: "a number",
788
+ boolean: "a boolean",
789
+ bigint: "a bigint",
790
+ array: "a list",
791
+ tuple: "a list",
792
+ date: "a date",
793
+ object: "an object"
794
+ };
795
+ function asRecord(value) {
796
+ return typeof value === "object" && value !== null ? value : null;
797
+ }
798
+ function firstLine(message) {
799
+ if (typeof message !== "string") return "unknown validation failure";
800
+ return message.split("\n")[0] ?? message;
801
+ }
802
+ const GIVEN_PREVIEW_LIMIT = 40;
803
+ function gotPhrase(given) {
804
+ if (given === void 0) return "nothing";
805
+ if (given === null) return "null";
806
+ if (typeof given === "string") {
807
+ if (given.length === 0) return "an empty string";
808
+ if (given.length > GIVEN_PREVIEW_LIMIT) return `${given.length} characters`;
809
+ return JSON.stringify(given);
810
+ }
811
+ if (Array.isArray(given)) return given.length === 0 ? "an empty list" : `${given.length} items`;
812
+ if (typeof given === "number" || typeof given === "bigint" || typeof given === "boolean") return String(given);
813
+ if (typeof given === "object") return given.constructor?.name ?? "an object";
814
+ return typeof given;
815
+ }
816
+ function needPhrase(expected, given) {
817
+ const bounds = [...expected.matchAll(/expected(\.length)?\s*(<=|>=|<|>)\s*(\d+)/g)];
818
+ if (bounds.length === 0) return null;
819
+ return bounds.map(([, lengthPart, operator, limit]) => {
820
+ const word = OPERATOR_WORD[operator ?? ""] ?? operator ?? "";
821
+ if (!lengthPart) return `${word} ${limit}`;
822
+ const unit = typeof given === "string" ? "character" : "item";
823
+ return `${word} ${limit} ${Number(limit) === 1 ? unit : `${unit}s`}`;
824
+ }).join(" and ");
825
+ }
826
+ function instanceName(expected) {
827
+ if (typeof expected === "function" && expected.name) return expected.name;
828
+ return "specific class instance";
829
+ }
830
+ function describeLeaf(record) {
831
+ if (typeof record.constraint === "string") {
832
+ const need = typeof record.expected === "string" ? needPhrase(record.expected, record.given) : null;
833
+ if (need) return `need ${need}, got ${gotPhrase(record.given)}`;
834
+ return `${firstLine(record.message)}${"given" in record ? ` (got ${gotPhrase(record.given)})` : ""}`;
835
+ }
836
+ if (typeof record.expected === "function") return `need a ${instanceName(record.expected)}, got ${gotPhrase(record.given)}`;
837
+ if (typeof record.validator === "string") {
838
+ const kind = KIND_WORD[record.validator.replace(/^s\./, "").replace(/\(.*$/, "")];
839
+ if (kind) return `need ${kind}, got ${gotPhrase(record.given)}`;
840
+ return `${firstLine(record.message)}, got ${gotPhrase(record.given)}`;
841
+ }
842
+ return firstLine(record.message);
843
+ }
844
+ function describeUnion(branches, depth) {
845
+ const records = [];
846
+ for (const branch of branches) {
847
+ const record = asRecord(branch);
848
+ if (record) records.push(record);
849
+ }
850
+ const bounded = records.find((record) => typeof record.constraint === "string");
851
+ if (bounded) return describeLeaf(bounded);
852
+ const names = [];
853
+ let allInstances = records.length > 0;
854
+ for (const record of records) if (typeof record.expected === "function") names.push(instanceName(record.expected));
855
+ else allInstances = false;
856
+ if (allInstances) return `need a ${[...new Set(names)].join(" or ")}, got ${gotPhrase(records[0]?.given)}`;
857
+ return detailOf(branches[0], depth + 1);
858
+ }
859
+ function detailOf(value, depth) {
860
+ const record = asRecord(value);
861
+ if (!record || depth >= MAX_DEPTH) return firstLine(record?.message);
862
+ if (Array.isArray(record.errors) && record.errors.length > 0) {
863
+ const errors = record.errors;
864
+ const [first] = errors;
865
+ if (Array.isArray(first)) {
866
+ const [key, inner] = first;
867
+ return `${String(key)}: ${detailOf(inner, depth + 1)}`;
868
+ }
869
+ return describeUnion(errors, depth);
870
+ }
871
+ return describeLeaf(record);
872
+ }
873
+ function translateSerializationError(error, componentClass, index, routeId) {
874
+ const detail = detailOf(error, 0);
875
+ return new SeedcordError(SeedcordErrorCode.ReplyComponentSerialization, [
876
+ componentClass,
877
+ index,
878
+ detail,
879
+ routeId
880
+ ], { cause: error });
881
+ }
882
+
883
+ //#endregion
884
+ //#region src/reply/serializeReply.ts
885
+ function serializeReply(response, routeId) {
886
+ if (typeof response === "string") try {
887
+ return { components: [new TextDisplayBuilder().setContent(response).toJSON()] };
888
+ } catch (error) {
889
+ throw translateSerializationError(error, "TextDisplayBuilder", 0, routeId);
890
+ }
891
+ return {
892
+ components: response.components.map((component, index) => {
893
+ try {
894
+ return component.toJSON();
895
+ } catch (error) {
896
+ const name = component.constructor?.name;
897
+ throw translateSerializationError(error, !name || name === "Object" ? "component" : name, index, routeId);
898
+ }
899
+ }),
900
+ ...response.allowedMentions && { allowedMentions: response.allowedMentions },
901
+ ...response.files && { files: response.files }
902
+ };
903
+ }
904
+
569
905
  //#endregion
570
906
  //#region src/gates/Gate.ts
571
907
  /**
@@ -660,23 +996,30 @@ var GateNotice = class extends Notice {
660
996
  var NotOwner = class extends GateNotice {
661
997
  constructor(message = "Only the bot owner can use this.") {
662
998
  super(message);
999
+ this.summary = "be the bot owner";
663
1000
  }
664
1001
  };
665
1002
  var NotInGuild = class extends GateNotice {
666
1003
  constructor(message = "This can only be used in a server.") {
667
1004
  super(message);
1005
+ this.summary = "be in a server";
668
1006
  }
669
1007
  };
670
1008
  var NotInDm = class extends GateNotice {
671
1009
  constructor(message = "This can only be used in a direct message.") {
672
1010
  super(message);
1011
+ this.summary = "be in a direct message";
673
1012
  }
674
1013
  };
1014
+ function retryAt(resetAt) {
1015
+ return `<t:${toEpochSeconds(resetAt)}:R>`;
1016
+ }
675
1017
  var OnCooldown = class extends GateNotice {
676
1018
  resetAt;
677
1019
  constructor(resetAt, message) {
678
- super(message ?? `You are doing that too fast. Try again <t:${toEpochSeconds(resetAt)}:R>.`);
1020
+ super(message ?? `You are doing that too fast. Try again ${retryAt(resetAt)}.`);
679
1021
  this.resetAt = resetAt;
1022
+ this.summary = `try again ${retryAt(resetAt)}`;
680
1023
  }
681
1024
  };
682
1025
  var NotAllowed = class extends Notice {
@@ -691,16 +1034,17 @@ var NeedsAny = class extends Notice {
691
1034
  summaries;
692
1035
  constructor(summaries) {
693
1036
  super("not allowed");
694
- this.summaries = summaries;
1037
+ this.summaries = [...new Set(summaries)];
695
1038
  }
696
1039
  render() {
697
1040
  const bullets = this.summaries.map((summary) => `• ${summary}`).join("\n");
698
- return { components: [new NoticeCard(`You need any of:\n${bullets}`).component] };
1041
+ return { components: [new NoticeCard(`You need to meet at least one of these:\n${bullets}`).component] };
699
1042
  }
700
1043
  };
701
1044
  var MissingRole = class extends GateNotice {
702
1045
  constructor(message, roleId) {
703
1046
  super(message ?? (roleId ? `You need the <@&${roleId}> role to use this.` : "You do not have the required role."));
1047
+ this.summary = roleId ? `hold the <@&${roleId}> role` : "hold the required role";
704
1048
  }
705
1049
  };
706
1050
  var MissingPermissions = class extends Notice {
@@ -712,6 +1056,8 @@ var MissingPermissions = class extends Notice {
712
1056
  this.subject = subject;
713
1057
  this.missingPerms = missingPerms;
714
1058
  this.customLead = message;
1059
+ const names = missingPerms.join(", ");
1060
+ this.summary = subject === null ? `hold ${names}` : `${subject} needs ${names}`;
715
1061
  }
716
1062
  render() {
717
1063
  const bullets = this.missingPerms.map((perm) => `• ${perm}`).join("\n");
@@ -728,6 +1074,7 @@ var HasDangerousPermissions = class extends Notice {
728
1074
  this.subject = subject;
729
1075
  this.dangerousPerms = dangerousPerms;
730
1076
  this.customLead = message;
1077
+ this.summary = `${subject} must not hold ${dangerousPerms.join(", ")}`;
731
1078
  }
732
1079
  render() {
733
1080
  const bullets = this.dangerousPerms.map((perm) => `• ${perm}`).join("\n");
@@ -875,8 +1222,9 @@ function RequireRole(roleId, options) {
875
1222
  * ```ts
876
1223
  * import { Silence } from '@seedcord/gateway';
877
1224
  *
878
- * // before any reply or defer, drop the interaction with no reply and no report
879
- * if (await isBlacklisted(interaction.user.id)) throw new Silence('blacklisted user');
1225
+ * // a helper under a guildMemberAdd handler, stopping when this guild configured nothing to send
1226
+ * const template = await settings.welcome(member.guild.id);
1227
+ * if (!template) throw new Silence('no welcome message set');
880
1228
  * ```
881
1229
  */
882
1230
  var Silence = class extends Error {
@@ -901,5 +1249,5 @@ function pageCursor(prefix) {
901
1249
  }
902
1250
 
903
1251
  //#endregion
904
- export { areRoutes as A, prefixOf as B, NotInDm as C, defineEffectGate as D, OnCooldown as E, storeInteractionRoute as F, Notice as H, ComponentDefsKey as I, decodeComponentRoute as L, interactionRoutesOf as M, selectMenuRouteOf as N, defineGate as O, storeComponentRoute as P, CustomId as R, NotAllowed as S, NotOwner as T, NoticeCard as V, GateNotice as _, RequirePermissions as a, MissingRole as b, hasAll as c, pickNotice as d, discardCommits as f, runCommits as g, runCheck as h, RequireBotPermissions as i, contextMenuRouteOf as j, SelectMenuKind as k, missingNames as l, rollbackCommits as m, pageCursor as n, RequireRole as o, markCommits as p, Silence as r, PermissionNames as s, PAGE_MAX as t, presentNames as u, HasDangerousPermissions as v, NotInGuild as w, NeedsAny as x, MissingPermissions as y, decodeFor as z };
905
- //# sourceMappingURL=cursor-CuoA9cRh.mjs.map
1252
+ export { translateSerializationError as A, interactionRoutesOf as B, NotInDm as C, defineEffectGate as D, OnCooldown as E, checkAckLegality as F, decodeComponentRoute as G, storeComponentRoute as H, sendTarget as I, prefixOf as J, CustomId as K, SelectMenuKind as L, publishResponse as M, reportedWrite as N, defineGate as O, AckTrace as P, areRoutes as R, NotAllowed as S, NotOwner as T, storeInteractionRoute as U, selectMenuRouteOf as V, ComponentDefsKey as W, Notice as X, NoticeCard as Y, GateNotice as _, RequirePermissions as a, MissingRole as b, hasAll as c, pickNotice as d, discardCommits as f, runCommits as g, runCheck as h, RequireBotPermissions as i, attemptWrite as j, serializeReply as k, missingNames as l, rollbackCommits as m, pageCursor as n, RequireRole as o, markCommits as p, decodeFor as q, Silence as r, PermissionNames as s, PAGE_MAX as t, presentNames as u, HasDangerousPermissions as v, NotInGuild as w, NeedsAny as x, MissingPermissions as y, contextMenuRouteOf as z };
1253
+ //# sourceMappingURL=cursor-GBUYz3jk.mjs.map