@seedcord/core 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/{Bus-DcuUtOEL.mjs → Bus-BVwmKc-A.mjs} +51 -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-Bkeqcjy6.mjs → Plugin-C6oMqWf8.mjs} +32 -7
  7. package/dist/Plugin-C6oMqWf8.mjs.map +1 -0
  8. package/dist/Plugin-CG9pWbPF.d.mts +193 -0
  9. package/dist/{cursor-DKKKUa_7.d.mts → cursor-BcRfmTSo.d.mts} +27 -140
  10. package/dist/{cursor-Cv1Ui6gZ.mjs → cursor-GBUYz3jk.mjs} +377 -176
  11. package/dist/cursor-GBUYz3jk.mjs.map +1 -0
  12. package/dist/{index-uBwh8r7x.d.mts → index-ZzdQafFv.d.mts} +2 -2
  13. package/dist/index.d.mts +132 -19
  14. package/dist/index.mjs +186 -19
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/internal.index.d.mts +11 -4
  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 +10 -146
  20. package/dist/node-internal.index.mjs +43 -470
  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/{Plugin-B6iRzm3g.d.mts → phases-F8yvPd60.d.mts} +111 -207
  26. package/dist/plugin.index.d.mts +2 -2
  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-CGGQkUfM.mjs +139 -0
  33. package/dist/subscribers-CGGQkUfM.mjs.map +1 -0
  34. package/package.json +14 -8
  35. package/dist/Bus-DcuUtOEL.mjs.map +0 -1
  36. package/dist/Plugin-Bkeqcjy6.mjs.map +0 -1
  37. package/dist/cursor-Cv1Ui6gZ.mjs.map +0 -1
  38. package/dist/slashRouteLeaves-De1SxqnX.mjs.map +0 -1
  39. package/dist/subscribers-DuHg-bGT.mjs +0 -3
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
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-DcuUtOEL.mjs";
2
- import { C as NotInDm, D as defineEffectGate, E as OnCooldown, F as storeInteractionRoute, G as ContextMenus, H as Notice, N as selectMenuRouteOf, O as defineGate, P as storeComponentRoute, R as CustomId, S as NotAllowed, T as NotOwner, V as NoticeCard, W as Commands, 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-Cv1Ui6gZ.mjs";
1
+ import { T as InteractionRoutes, _ as RowComponent, b as CommandMetadataKey, d as WebhookLog, f as Subscriber, g as BuilderComponent, l as WebhookUrl, t as Bus, u as Subscribe } from "./Bus-BVwmKc-A.mjs";
2
+ import { n as Commands, r as ContextMenus } from "./subscribers-CGGQkUfM.mjs";
3
+ import { A as translateSerializationError, C as NotInDm, D as defineEffectGate, E as OnCooldown, F as checkAckLegality, H as storeComponentRoute, I as sendTarget, K as CustomId, L as SelectMenuKind, M as publishResponse, O as defineGate, P as AckTrace, S as NotAllowed, T as NotOwner, U as storeInteractionRoute, V as selectMenuRouteOf, X as Notice, Y as NoticeCard, 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, 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-GBUYz3jk.mjs";
3
4
  import { n as StartupPhase, t as ShutdownPhase } from "./phases-BauKJOTT.mjs";
4
- import "./subscribers-DuHg-bGT.mjs";
5
5
  import "reflect-metadata";
6
6
  import { ActionRowBuilder, ButtonBuilder, TextDisplayBuilder } from "@discordjs/builders";
7
7
  import { SeedcordErrorCode } from "@seedcord/errors";
@@ -19,8 +19,8 @@ function RegisterCommand(scope, guilds = []) {
19
19
  existingMeta.scope,
20
20
  scope
21
21
  ]);
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);
22
+ if (scope === "global" && guilds.length > 0) throw new SeedcordTypeError(SeedcordErrorCode.DecoratorCommandGlobalWithGuilds);
23
+ if (scope === "guild" && (!Array.isArray(guilds) || guilds.length === 0)) throw new SeedcordTypeError(SeedcordErrorCode.DecoratorCommandGuildWithoutGuilds);
24
24
  const meta = scope === "global" ? { scope } : {
25
25
  scope,
26
26
  guilds
@@ -235,6 +235,7 @@ var BaseHandler = class {
235
235
  * @typeParam TSender - The transport's reply sender
236
236
  */
237
237
  var RepliableHandler = class extends BaseHandler {
238
+ /** The reply surface for this interaction. Pass the handler to reply from outside its class. */
238
239
  sender;
239
240
  routeId;
240
241
  constructor(event, core, dispatch) {
@@ -242,10 +243,6 @@ var RepliableHandler = class extends BaseHandler {
242
243
  this.routeId = dispatch?.routeId ?? this.constructor.name;
243
244
  this.sender = this.buildSender(event, core, this.routeId);
244
245
  }
245
- /** @internal The dispatcher's fault boundary replies through the live ack state. */
246
- getSender() {
247
- return this.sender;
248
- }
249
246
  /** Send the initial response, exactly once. */
250
247
  reply(response, opts) {
251
248
  return this.sender.reply(response, opts);
@@ -272,6 +269,172 @@ var RepliableHandler = class extends BaseHandler {
272
269
  }
273
270
  };
274
271
 
272
+ //#endregion
273
+ //#region src/reply/BaseReplySender.ts
274
+ /**
275
+ * Tracks acknowledgement state and throws a translated `SeedcordError` on an illegal verb before any
276
+ * transport call. Every verb runs the legality check, calls the transport's wire writer, then updates
277
+ * the state and the ack trace. Each transport binds `TMessage` to its own created-message type and
278
+ * supplies the writers.
279
+ */
280
+ var BaseReplySender = class {
281
+ routeId;
282
+ telemetry;
283
+ state;
284
+ sent = /* @__PURE__ */ new Set();
285
+ ackedBy;
286
+ constructor(routeId, telemetry, initialState = "unacked") {
287
+ this.routeId = routeId;
288
+ this.telemetry = telemetry;
289
+ this.state = initialState;
290
+ }
291
+ report(method, startedAt, messageId) {
292
+ publishResponse(this.telemetry, {
293
+ routeId: this.routeId,
294
+ method,
295
+ startedAt,
296
+ outcome: "sent",
297
+ messageId
298
+ });
299
+ }
300
+ async attempt(method, startedAt, write) {
301
+ return await attemptWrite(this.telemetry, this.routeId, method, startedAt, write);
302
+ }
303
+ async reply(response, opts) {
304
+ this.checkLegality("reply");
305
+ const startedAt = performance.now();
306
+ const created = await this.attempt("reply", startedAt, () => this.writeReply(response, opts));
307
+ this.transition("reply", "replied");
308
+ const message = this.remember(this.requireMessage(created, "reply"));
309
+ this.report("reply", startedAt, message.id);
310
+ return message;
311
+ }
312
+ async defer(opts) {
313
+ this.checkLegality("defer");
314
+ const startedAt = performance.now();
315
+ await this.attempt("defer", startedAt, () => this.writeDefer(opts));
316
+ this.transition("defer", "deferred-reply");
317
+ this.report("defer", startedAt, null);
318
+ }
319
+ async deferUpdate() {
320
+ this.checkLegality("deferUpdate");
321
+ const startedAt = performance.now();
322
+ await this.attempt("deferUpdate", startedAt, () => this.writeDeferUpdate());
323
+ this.transition("deferUpdate", "deferred-update");
324
+ this.report("deferUpdate", startedAt, null);
325
+ }
326
+ /**
327
+ * After a deferUpdate the state stays deferred-update. Calling this again rewrites. The returned message
328
+ * is the source message, editable only through a bare `update` or `edit`. A targeted `edit` or `delete`
329
+ * of it throws the foreign-target error.
330
+ */
331
+ async update(response) {
332
+ this.checkLegality("update");
333
+ const startedAt = performance.now();
334
+ if (this.state === "deferred-update") {
335
+ const edited = await this.attempt("update", startedAt, () => this.editOriginal(response));
336
+ this.report("update", startedAt, edited.id);
337
+ return edited;
338
+ }
339
+ const created = await this.attempt("update", startedAt, () => this.writeUpdate(response));
340
+ this.transition("update", "replied");
341
+ const message = this.remember(this.requireMessage(created, "update"));
342
+ this.report("update", startedAt, message.id);
343
+ return message;
344
+ }
345
+ async followUp(response, opts) {
346
+ this.checkLegality("followUp");
347
+ const startedAt = performance.now();
348
+ const created = await this.attempt("followUp", startedAt, () => this.writeFollowUp(response, opts));
349
+ const message = this.remember(created);
350
+ this.report("followUp", startedAt, message.id);
351
+ return message;
352
+ }
353
+ async edit(targetOrResponse, maybeResponse) {
354
+ this.checkLegality("edit");
355
+ const startedAt = performance.now();
356
+ if (maybeResponse === void 0) {
357
+ const edited = await this.attempt("edit", startedAt, () => this.editOriginal(targetOrResponse));
358
+ this.report("edit", startedAt, edited.id);
359
+ return edited;
360
+ }
361
+ const target = targetOrResponse;
362
+ if (!this.sent.has(target.id)) throw new SeedcordError(SeedcordErrorCode.ReplyForeignEditTarget, [
363
+ "edit",
364
+ target.id,
365
+ this.routeId
366
+ ]);
367
+ const written = await this.attempt("edit", startedAt, () => this.writeEditTarget(target.id, maybeResponse));
368
+ const message = this.remember(written);
369
+ this.report("edit", startedAt, message.id);
370
+ return message;
371
+ }
372
+ async delete(target) {
373
+ this.checkLegality("delete");
374
+ const startedAt = performance.now();
375
+ if (target === void 0) {
376
+ await this.attempt("delete", startedAt, () => this.writeDeleteOriginal());
377
+ this.report("delete", startedAt, null);
378
+ return;
379
+ }
380
+ if (!this.sent.has(target.id)) throw new SeedcordError(SeedcordErrorCode.ReplyForeignEditTarget, [
381
+ "delete",
382
+ target.id,
383
+ this.routeId
384
+ ]);
385
+ await this.attempt("delete", startedAt, () => this.writeDeleteTarget(target.id));
386
+ this.sent.delete(target.id);
387
+ this.report("delete", startedAt, null);
388
+ }
389
+ /** Routes to the verb the current ack state permits. Every state has a route, so the illegal-ack throw is unreachable. */
390
+ async send(response, opts) {
391
+ const target = sendTarget(this.state);
392
+ if (target === "reply") return await this.reply(response, opts);
393
+ if (target === "edit") return await this.edit(response);
394
+ return await this.followUp(response, opts);
395
+ }
396
+ /** Must be the initial response. */
397
+ async showModal(modal) {
398
+ this.checkLegality("showModal");
399
+ this.guardModalSource();
400
+ let data;
401
+ try {
402
+ data = modal.toJSON();
403
+ } catch (error) {
404
+ const name = modal.constructor?.name;
405
+ throw translateSerializationError(error, !name || name === "Object" ? "modal" : name, 0, this.routeId);
406
+ }
407
+ const startedAt = performance.now();
408
+ await this.attempt("showModal", startedAt, () => this.writeModal(data));
409
+ this.transition("showModal", "replied");
410
+ this.report("showModal", startedAt, null);
411
+ }
412
+ serialize(response) {
413
+ return serializeReply(response, this.routeId);
414
+ }
415
+ remember(created) {
416
+ this.sent.add(created.id);
417
+ return created;
418
+ }
419
+ requireMessage(created, method) {
420
+ if (!created) throw new SeedcordError(SeedcordErrorCode.ReplyCallbackMissingMessage, [method, this.routeId]);
421
+ return created;
422
+ }
423
+ async editOriginal(response) {
424
+ const edited = await this.writeEditOriginal(response);
425
+ if (this.state !== "deferred-update") this.remember(edited);
426
+ return edited;
427
+ }
428
+ checkLegality(method) {
429
+ checkAckLegality(method, this.state, this.routeId, this.ackedBy);
430
+ }
431
+ transition(method, state) {
432
+ this.state = state;
433
+ this.ackedBy = new AckTrace(method);
434
+ }
435
+ guardModalSource() {}
436
+ };
437
+
275
438
  //#endregion
276
439
  //#region src/stops/Fault.ts
277
440
  /**
@@ -314,8 +477,12 @@ var Fault = class extends Notice {
314
477
 
315
478
  //#endregion
316
479
  //#region src/gates/combinators.ts
480
+ const ALREADY_JOINED = / [&|] /;
481
+ function joinNames(gates, separator) {
482
+ return gates.map((gate) => ALREADY_JOINED.test(gate.name) ? `(${gate.name})` : gate.name).join(separator);
483
+ }
317
484
  function and(...gates) {
318
- return defineGate(gates.map((gate) => gate.name).join(" & "), async (ctx) => {
485
+ return defineGate(joinNames(gates, " & "), async (ctx) => {
319
486
  for (const gate of gates) await runCheck(gate, ctx);
320
487
  });
321
488
  }
@@ -326,7 +493,7 @@ function or(...args) {
326
493
  const last = args.at(-1);
327
494
  const options = last !== void 0 && isOrOptions(last) ? last : void 0;
328
495
  const gates = options ? args.slice(0, -1) : args;
329
- return defineGate(gates.map((gate) => gate.name).join(" | "), async (ctx) => {
496
+ return defineGate(joinNames(gates, " | "), async (ctx) => {
330
497
  const summaries = [];
331
498
  let everyArmHasSummary = true;
332
499
  for (const gate of gates) {
@@ -660,7 +827,7 @@ var ResolvedEmoji = class {
660
827
  function paginate(items, page, perPage) {
661
828
  if (!Number.isSafeInteger(perPage) || perPage <= 0) throw new SeedcordRangeError(SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);
662
829
  const totalPages = Math.max(1, Math.ceil(items.length / perPage));
663
- const clamped = Math.min(Math.max(Math.trunc(page), 0), totalPages - 1);
830
+ const clamped = Math.min(Math.max(Number.isNaN(page) ? 0 : Math.trunc(page), 0), totalPages - 1);
664
831
  const start = clamped * perPage;
665
832
  return {
666
833
  items: items.slice(start, start + perPage),
@@ -812,8 +979,8 @@ var PaginatorBase = class {
812
979
  this.cursor = pageCursor(config.prefix);
813
980
  this.config = config;
814
981
  }
815
- /** Render a page as a {@link ReplyResponse}. To post it elsewhere, add `flags: MessageFlags.IsComponentsV2`. */
816
- async page(ctx, n) {
982
+ /** Build one page as a {@link ReplyResponse}. Each transport wraps this in a `page()` taking its handler. */
983
+ async buildPage(ctx, n) {
817
984
  const view = await this.config.source.page(ctx, n);
818
985
  return renderPage(view, this.cursor, this.config);
819
986
  }
@@ -835,7 +1002,7 @@ function perPageOf(opts) {
835
1002
  * @typeParam Item - The item type, inferred from the loader's return.
836
1003
  * @typeParam Ctx - The transport's page context. Each transport exports a subclass that fixes it.
837
1004
  */
838
- var ArraySource = class {
1005
+ var ArraySourceBase = class {
839
1006
  load;
840
1007
  perPage;
841
1008
  constructor(load, opts) {
@@ -855,7 +1022,7 @@ var ArraySource = class {
855
1022
  * @typeParam Item - The item type, inferred from the fetcher's slice.
856
1023
  * @typeParam Ctx - The transport's page context. Each transport exports a subclass that fixes it.
857
1024
  */
858
- var CursorSource = class {
1025
+ var CursorSourceBase = class {
859
1026
  fetch;
860
1027
  perPage;
861
1028
  constructor(fetch, opts) {
@@ -863,7 +1030,7 @@ var CursorSource = class {
863
1030
  this.perPage = perPageOf(opts);
864
1031
  }
865
1032
  async page(ctx, n) {
866
- const page = Math.max(0, Math.trunc(n));
1033
+ const page = Number.isFinite(n) ? Math.max(0, Math.trunc(n)) : 0;
867
1034
  const { items, hasNext } = await this.fetch(ctx, page, this.perPage);
868
1035
  return {
869
1036
  items: [...items],
@@ -878,8 +1045,8 @@ var CursorSource = class {
878
1045
  //#endregion
879
1046
  //#region src/index.ts
880
1047
  /** Package version */
881
- const version = "0.3.0";
1048
+ const version = "0.4.0";
882
1049
 
883
1050
  //#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 };
1051
+ 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, version };
885
1052
  //# sourceMappingURL=index.mjs.map