@seedcord/core 0.3.1 → 0.4.1

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-yJGE6Fs2.mjs} +111 -92
  2. package/dist/Bus-yJGE6Fs2.mjs.map +1 -0
  3. package/dist/Pluggable-BI_LQuPm.mjs +280 -0
  4. package/dist/Pluggable-BI_LQuPm.mjs.map +1 -0
  5. package/dist/Pluggable-bFCmT4oB.d.mts +148 -0
  6. package/dist/{Plugin-D9ykXA5g.mjs → Plugin-B0SRHzMX.mjs} +7 -3
  7. package/dist/Plugin-B0SRHzMX.mjs.map +1 -0
  8. package/dist/{Plugin-CQ9IEj5X.d.mts → Plugin-BW2JH2_h.d.mts} +2 -2
  9. package/dist/{cursor-BANzac53.d.mts → cursor-CO_MTgdZ.d.mts} +8 -252
  10. package/dist/{cursor-CuoA9cRh.mjs → cursor-CaT051ii.mjs} +307 -425
  11. package/dist/cursor-CaT051ii.mjs.map +1 -0
  12. package/dist/{index-rt8rZg4d.d.mts → index-CvneCObW.d.mts} +2 -2
  13. package/dist/index.d.mts +78 -19
  14. package/dist/index.mjs +223 -21
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/internal.index.d.mts +13 -6
  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 +16 -473
  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-C5Steaa2.d.mts} +151 -119
  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-BQmbFm-S.mjs} +10 -2
  31. package/dist/slashRouteLeaves-BQmbFm-S.mjs.map +1 -0
  32. package/dist/{subscribers-wnMyNYZd.mjs → subscribers-DVa-Pr45.mjs} +3 -3
  33. package/dist/{subscribers-wnMyNYZd.mjs.map → subscribers-DVa-Pr45.mjs.map} +1 -1
  34. package/package.json +19 -12
  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
package/dist/index.mjs CHANGED
@@ -1,15 +1,50 @@
1
- import { a as WebhookUrl, b as InteractionRoutes, c as Subscriber, f as BuilderComponent, h as CommandMetadataKey, o as Subscribe, p as RowComponent, s as WebhookLog, t as Bus } from "./Bus-BvF4VShN.mjs";
2
- import { n as Commands, r as ContextMenus } from "./subscribers-wnMyNYZd.mjs";
3
- import { C as NotInDm, D as defineEffectGate, E as OnCooldown, F as storeInteractionRoute, H as Notice, N as selectMenuRouteOf, O as defineGate, P as storeComponentRoute, R as CustomId, S as NotAllowed, T as NotOwner, V as NoticeCard, a as RequirePermissions, c as hasAll, d as pickNotice, h as runCheck, i as RequireBotPermissions, j as contextMenuRouteOf, k as SelectMenuKind, l as missingNames, m as rollbackCommits, n as pageCursor, o as RequireRole, p as markCommits, r as Silence, t as PAGE_MAX, u as presentNames, v as HasDangerousPermissions, w as NotInGuild, x as NeedsAny, y as MissingPermissions } from "./cursor-CuoA9cRh.mjs";
1
+ import { A as translateSerializationError, C as NotInDm, D as defineEffectGate, E as OnCooldown, F as checkAckLegality, H as storeComponentRoute, I as sendTarget, K as NoticeCard, L as SelectMenuKind, M as publishResponse, O as defineGate, P as AckTrace, S as NotAllowed, T as NotOwner, U as storeInteractionRoute, V as selectMenuRouteOf, a as RequirePermissions, c as hasAll, d as pickNotice, h as runCheck, i as RequireBotPermissions, j as attemptWrite, k as serializeReply, l as missingNames, m as rollbackCommits, n as pageCursor, o as RequireRole, p as markCommits, q as Notice, r as Silence, t as PAGE_MAX, u as presentNames, v as HasDangerousPermissions, w as NotInGuild, x as NeedsAny, y as MissingPermissions, z as contextMenuRouteOf } from "./cursor-CaT051ii.mjs";
2
+ import { A as RowComponent, d as WebhookLog, f as Subscriber, g as CommandMetadataKey, k as BuilderComponent, l as WebhookUrl, t as Bus, u as Subscribe, x as InteractionRoutes } from "./Bus-yJGE6Fs2.mjs";
3
+ import { n as Commands, r as ContextMenus } from "./subscribers-DVa-Pr45.mjs";
4
4
  import { n as StartupPhase, t as ShutdownPhase } from "./phases-BauKJOTT.mjs";
5
5
  import "reflect-metadata";
6
+ import { CustomId, setCustomIdErrors, setCustomIdErrors as setCustomIdErrors$1 } from "@seedcord/custom-id";
6
7
  import { ActionRowBuilder, ButtonBuilder, TextDisplayBuilder } from "@discordjs/builders";
7
- import { SeedcordErrorCode } from "@seedcord/errors";
8
- import { Logger } from "@seedcord/logger";
9
8
  import { ApplicationCommandType, ButtonStyle } from "discord-api-types/v10";
9
+ import { SeedcordErrorCode } from "@seedcord/errors";
10
10
  import { SeedcordError, SeedcordRangeError, SeedcordTypeError } from "@seedcord/errors/internal";
11
+ import { Logger } from "@seedcord/logger";
11
12
  import { parseDuration } from "@seedcord/utils";
12
13
 
14
+ //#region src/customId/Errors.ts
15
+ /**
16
+ * Thrown when a customId was minted by an older version of its shape.
17
+ *
18
+ * This is normal after the shape changes.
19
+ */
20
+ var StaleCustomId = class extends Notice {
21
+ constructor(prefix) {
22
+ super(`Stale customId for "${prefix}".`);
23
+ }
24
+ render() {
25
+ return { components: [new NoticeCard("This button or menu is from an older version. Please run the command again.", "Outdated").component] };
26
+ }
27
+ };
28
+ /**
29
+ * Thrown when a customId wire is corrupt or tampered with and cannot be trusted.
30
+ *
31
+ * It reports because this is an odd case that should not happen.
32
+ */
33
+ var InvalidCustomId = class extends Notice {
34
+ constructor(detail) {
35
+ super(`Invalid customId. ${detail}`);
36
+ this.report = true;
37
+ }
38
+ render() {
39
+ return { components: [new NoticeCard("Something went wrong. Please try again.").component] };
40
+ }
41
+ };
42
+ setCustomIdErrors$1({
43
+ stale: (prefix) => new StaleCustomId(prefix),
44
+ invalid: (detail) => new InvalidCustomId(detail)
45
+ });
46
+
47
+ //#endregion
13
48
  //#region src/decorators/Command.ts
14
49
  function RegisterCommand(scope, guilds = []) {
15
50
  return (ctor) => {
@@ -19,8 +54,8 @@ function RegisterCommand(scope, guilds = []) {
19
54
  existingMeta.scope,
20
55
  scope
21
56
  ]);
22
- if (scope === "global" && guilds.length > 0) throw new SeedcordError(SeedcordErrorCode.DecoratorCommandGlobalWithGuilds);
23
- if (scope === "guild" && (!Array.isArray(guilds) || guilds.length === 0)) throw new SeedcordError(SeedcordErrorCode.DecoratorCommandGuildWithoutGuilds);
57
+ if (scope === "global" && guilds.length > 0) throw new SeedcordTypeError(SeedcordErrorCode.DecoratorCommandGlobalWithGuilds);
58
+ if (scope === "guild" && (!Array.isArray(guilds) || guilds.length === 0)) throw new SeedcordTypeError(SeedcordErrorCode.DecoratorCommandGuildWithoutGuilds);
24
59
  const meta = scope === "global" ? { scope } : {
25
60
  scope,
26
61
  guilds
@@ -235,6 +270,7 @@ var BaseHandler = class {
235
270
  * @typeParam TSender - The transport's reply sender
236
271
  */
237
272
  var RepliableHandler = class extends BaseHandler {
273
+ /** The reply surface for this interaction. Pass the handler to reply from outside its class. */
238
274
  sender;
239
275
  routeId;
240
276
  constructor(event, core, dispatch) {
@@ -242,10 +278,6 @@ var RepliableHandler = class extends BaseHandler {
242
278
  this.routeId = dispatch?.routeId ?? this.constructor.name;
243
279
  this.sender = this.buildSender(event, core, this.routeId);
244
280
  }
245
- /** @internal The dispatcher's fault boundary replies through the live ack state. */
246
- getSender() {
247
- return this.sender;
248
- }
249
281
  /** Send the initial response, exactly once. */
250
282
  reply(response, opts) {
251
283
  return this.sender.reply(response, opts);
@@ -272,6 +304,172 @@ var RepliableHandler = class extends BaseHandler {
272
304
  }
273
305
  };
274
306
 
307
+ //#endregion
308
+ //#region src/reply/BaseReplySender.ts
309
+ /**
310
+ * Tracks acknowledgement state and throws a translated `SeedcordError` on an illegal verb before any
311
+ * transport call. Every verb runs the legality check, calls the transport's wire writer, then updates
312
+ * the state and the ack trace. Each transport binds `TMessage` to its own created-message type and
313
+ * supplies the writers.
314
+ */
315
+ var BaseReplySender = class {
316
+ routeId;
317
+ telemetry;
318
+ state;
319
+ sent = /* @__PURE__ */ new Set();
320
+ ackedBy;
321
+ constructor(routeId, telemetry, initialState = "unacked") {
322
+ this.routeId = routeId;
323
+ this.telemetry = telemetry;
324
+ this.state = initialState;
325
+ }
326
+ report(method, startedAt, messageId) {
327
+ publishResponse(this.telemetry, {
328
+ routeId: this.routeId,
329
+ method,
330
+ startedAt,
331
+ outcome: "sent",
332
+ messageId
333
+ });
334
+ }
335
+ async attempt(method, startedAt, write) {
336
+ return await attemptWrite(this.telemetry, this.routeId, method, startedAt, write);
337
+ }
338
+ async reply(response, opts) {
339
+ this.checkLegality("reply");
340
+ const startedAt = performance.now();
341
+ const created = await this.attempt("reply", startedAt, () => this.writeReply(response, opts));
342
+ this.transition("reply", "replied");
343
+ const message = this.remember(this.requireMessage(created, "reply"));
344
+ this.report("reply", startedAt, message.id);
345
+ return message;
346
+ }
347
+ async defer(opts) {
348
+ this.checkLegality("defer");
349
+ const startedAt = performance.now();
350
+ await this.attempt("defer", startedAt, () => this.writeDefer(opts));
351
+ this.transition("defer", "deferred-reply");
352
+ this.report("defer", startedAt, null);
353
+ }
354
+ async deferUpdate() {
355
+ this.checkLegality("deferUpdate");
356
+ const startedAt = performance.now();
357
+ await this.attempt("deferUpdate", startedAt, () => this.writeDeferUpdate());
358
+ this.transition("deferUpdate", "deferred-update");
359
+ this.report("deferUpdate", startedAt, null);
360
+ }
361
+ /**
362
+ * After a deferUpdate the state stays deferred-update. Calling this again rewrites. The returned message
363
+ * is the source message, editable only through a bare `update` or `edit`. A targeted `edit` or `delete`
364
+ * of it throws the foreign-target error.
365
+ */
366
+ async update(response) {
367
+ this.checkLegality("update");
368
+ const startedAt = performance.now();
369
+ if (this.state === "deferred-update") {
370
+ const edited = await this.attempt("update", startedAt, () => this.editOriginal(response));
371
+ this.report("update", startedAt, edited.id);
372
+ return edited;
373
+ }
374
+ const created = await this.attempt("update", startedAt, () => this.writeUpdate(response));
375
+ this.transition("update", "replied");
376
+ const message = this.remember(this.requireMessage(created, "update"));
377
+ this.report("update", startedAt, message.id);
378
+ return message;
379
+ }
380
+ async followUp(response, opts) {
381
+ this.checkLegality("followUp");
382
+ const startedAt = performance.now();
383
+ const created = await this.attempt("followUp", startedAt, () => this.writeFollowUp(response, opts));
384
+ const message = this.remember(created);
385
+ this.report("followUp", startedAt, message.id);
386
+ return message;
387
+ }
388
+ async edit(targetOrResponse, maybeResponse) {
389
+ this.checkLegality("edit");
390
+ const startedAt = performance.now();
391
+ if (maybeResponse === void 0) {
392
+ const edited = await this.attempt("edit", startedAt, () => this.editOriginal(targetOrResponse));
393
+ this.report("edit", startedAt, edited.id);
394
+ return edited;
395
+ }
396
+ const target = targetOrResponse;
397
+ if (!this.sent.has(target.id)) throw new SeedcordError(SeedcordErrorCode.ReplyForeignEditTarget, [
398
+ "edit",
399
+ target.id,
400
+ this.routeId
401
+ ]);
402
+ const written = await this.attempt("edit", startedAt, () => this.writeEditTarget(target.id, maybeResponse));
403
+ const message = this.remember(written);
404
+ this.report("edit", startedAt, message.id);
405
+ return message;
406
+ }
407
+ async delete(target) {
408
+ this.checkLegality("delete");
409
+ const startedAt = performance.now();
410
+ if (target === void 0) {
411
+ await this.attempt("delete", startedAt, () => this.writeDeleteOriginal());
412
+ this.report("delete", startedAt, null);
413
+ return;
414
+ }
415
+ if (!this.sent.has(target.id)) throw new SeedcordError(SeedcordErrorCode.ReplyForeignEditTarget, [
416
+ "delete",
417
+ target.id,
418
+ this.routeId
419
+ ]);
420
+ await this.attempt("delete", startedAt, () => this.writeDeleteTarget(target.id));
421
+ this.sent.delete(target.id);
422
+ this.report("delete", startedAt, null);
423
+ }
424
+ /** Routes to the verb the current ack state permits. Every state has a route, so the illegal-ack throw is unreachable. */
425
+ async send(response, opts) {
426
+ const target = sendTarget(this.state);
427
+ if (target === "reply") return await this.reply(response, opts);
428
+ if (target === "edit") return await this.edit(response);
429
+ return await this.followUp(response, opts);
430
+ }
431
+ /** Must be the initial response. */
432
+ async showModal(modal) {
433
+ this.checkLegality("showModal");
434
+ this.guardModalSource();
435
+ let data;
436
+ try {
437
+ data = modal.toJSON();
438
+ } catch (error) {
439
+ const name = modal.constructor?.name;
440
+ throw translateSerializationError(error, !name || name === "Object" ? "modal" : name, 0, this.routeId);
441
+ }
442
+ const startedAt = performance.now();
443
+ await this.attempt("showModal", startedAt, () => this.writeModal(data));
444
+ this.transition("showModal", "replied");
445
+ this.report("showModal", startedAt, null);
446
+ }
447
+ serialize(response) {
448
+ return serializeReply(response, this.routeId);
449
+ }
450
+ remember(created) {
451
+ this.sent.add(created.id);
452
+ return created;
453
+ }
454
+ requireMessage(created, method) {
455
+ if (!created) throw new SeedcordError(SeedcordErrorCode.ReplyCallbackMissingMessage, [method, this.routeId]);
456
+ return created;
457
+ }
458
+ async editOriginal(response) {
459
+ const edited = await this.writeEditOriginal(response);
460
+ if (this.state !== "deferred-update") this.remember(edited);
461
+ return edited;
462
+ }
463
+ checkLegality(method) {
464
+ checkAckLegality(method, this.state, this.routeId, this.ackedBy);
465
+ }
466
+ transition(method, state) {
467
+ this.state = state;
468
+ this.ackedBy = new AckTrace(method);
469
+ }
470
+ guardModalSource() {}
471
+ };
472
+
275
473
  //#endregion
276
474
  //#region src/stops/Fault.ts
277
475
  /**
@@ -314,8 +512,12 @@ var Fault = class extends Notice {
314
512
 
315
513
  //#endregion
316
514
  //#region src/gates/combinators.ts
515
+ const ALREADY_JOINED = / [&|] /;
516
+ function joinNames(gates, separator) {
517
+ return gates.map((gate) => ALREADY_JOINED.test(gate.name) ? `(${gate.name})` : gate.name).join(separator);
518
+ }
317
519
  function and(...gates) {
318
- return defineGate(gates.map((gate) => gate.name).join(" & "), async (ctx) => {
520
+ return defineGate(joinNames(gates, " & "), async (ctx) => {
319
521
  for (const gate of gates) await runCheck(gate, ctx);
320
522
  });
321
523
  }
@@ -326,7 +528,7 @@ function or(...args) {
326
528
  const last = args.at(-1);
327
529
  const options = last !== void 0 && isOrOptions(last) ? last : void 0;
328
530
  const gates = options ? args.slice(0, -1) : args;
329
- return defineGate(gates.map((gate) => gate.name).join(" | "), async (ctx) => {
531
+ return defineGate(joinNames(gates, " | "), async (ctx) => {
330
532
  const summaries = [];
331
533
  let everyArmHasSummary = true;
332
534
  for (const gate of gates) {
@@ -660,7 +862,7 @@ var ResolvedEmoji = class {
660
862
  function paginate(items, page, perPage) {
661
863
  if (!Number.isSafeInteger(perPage) || perPage <= 0) throw new SeedcordRangeError(SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);
662
864
  const totalPages = Math.max(1, Math.ceil(items.length / perPage));
663
- const clamped = Math.min(Math.max(Math.trunc(page), 0), totalPages - 1);
865
+ const clamped = Math.min(Math.max(Number.isNaN(page) ? 0 : Math.trunc(page), 0), totalPages - 1);
664
866
  const start = clamped * perPage;
665
867
  return {
666
868
  items: items.slice(start, start + perPage),
@@ -812,8 +1014,8 @@ var PaginatorBase = class {
812
1014
  this.cursor = pageCursor(config.prefix);
813
1015
  this.config = config;
814
1016
  }
815
- /** Render a page as a {@link ReplyResponse}. To post it elsewhere, add `flags: MessageFlags.IsComponentsV2`. */
816
- async page(ctx, n) {
1017
+ /** Build one page as a {@link ReplyResponse}. Each transport wraps this in a `page()` taking its handler. */
1018
+ async buildPage(ctx, n) {
817
1019
  const view = await this.config.source.page(ctx, n);
818
1020
  return renderPage(view, this.cursor, this.config);
819
1021
  }
@@ -835,7 +1037,7 @@ function perPageOf(opts) {
835
1037
  * @typeParam Item - The item type, inferred from the loader's return.
836
1038
  * @typeParam Ctx - The transport's page context. Each transport exports a subclass that fixes it.
837
1039
  */
838
- var ArraySource = class {
1040
+ var ArraySourceBase = class {
839
1041
  load;
840
1042
  perPage;
841
1043
  constructor(load, opts) {
@@ -855,7 +1057,7 @@ var ArraySource = class {
855
1057
  * @typeParam Item - The item type, inferred from the fetcher's slice.
856
1058
  * @typeParam Ctx - The transport's page context. Each transport exports a subclass that fixes it.
857
1059
  */
858
- var CursorSource = class {
1060
+ var CursorSourceBase = class {
859
1061
  fetch;
860
1062
  perPage;
861
1063
  constructor(fetch, opts) {
@@ -863,7 +1065,7 @@ var CursorSource = class {
863
1065
  this.perPage = perPageOf(opts);
864
1066
  }
865
1067
  async page(ctx, n) {
866
- const page = Math.max(0, Math.trunc(n));
1068
+ const page = Number.isFinite(n) ? Math.max(0, Math.trunc(n)) : 0;
867
1069
  const { items, hasNext } = await this.fetch(ctx, page, this.perPage);
868
1070
  return {
869
1071
  items: [...items],
@@ -878,8 +1080,8 @@ var CursorSource = class {
878
1080
  //#endregion
879
1081
  //#region src/index.ts
880
1082
  /** Package version */
881
- const version = "0.3.1";
1083
+ const version = "0.4.1";
882
1084
 
883
1085
  //#endregion
884
- export { ArraySource, AutocompleteRoute, BaseHandler, BuilderComponent, Bus, ButtonRoute, Commands, ContextMenus, Cooldown, CursorSource, CustomId, DispatchContext, DmOnly, Fault, GuildOnly, MessageContextMenuRoute, ModalRoute, Notice, OwnerOnly, PaginatorBase, RegisterCommand, RepliableHandler, RequireBotPermissions, RequirePermissions, RequireRole, ResolvedEmoji, RowComponent, SelectMenuKind, SelectMenuRoute, ShutdownPhase, Silence, SlashRoute, StartupPhase, Subscribe, Subscriber, UserContextMenuRoute, WebhookLog, WebhookUrl, and, assertPermissions, defineEffectGate, defineGate, mergeRoles, or, paginate, version };
1086
+ export { ArraySourceBase, AutocompleteRoute, BaseHandler, BaseReplySender, BuilderComponent, Bus, ButtonRoute, Commands, ContextMenus, Cooldown, CursorSourceBase, CustomId, DispatchContext, DmOnly, Fault, GuildOnly, MessageContextMenuRoute, ModalRoute, Notice, OwnerOnly, PaginatorBase, RegisterCommand, RepliableHandler, RequireBotPermissions, RequirePermissions, RequireRole, ResolvedEmoji, RowComponent, SelectMenuKind, SelectMenuRoute, ShutdownPhase, Silence, SlashRoute, StartupPhase, Subscribe, Subscriber, UserContextMenuRoute, WebhookLog, WebhookUrl, and, assertPermissions, defineEffectGate, defineGate, mergeRoles, or, paginate, setCustomIdErrors, version };
885
1087
  //# sourceMappingURL=index.mjs.map