@seedcord/http 0.2.3 → 0.3.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.
- package/README.md +37 -5
- package/dist/{edge.index-C7y2HWE0.d.mts → edge.index-8mheWe3d.d.mts} +12 -19
- package/dist/{edge.index-7pBw2C-U.mjs → edge.index-CNLml121.mjs} +19 -51
- package/dist/edge.index-CNLml121.mjs.map +1 -0
- package/dist/edge.index.d.mts +3 -3
- package/dist/edge.index.mjs +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +10 -11
- package/dist/index.mjs.map +1 -1
- package/dist/manifest.index.mjs.map +1 -1
- package/package.json +27 -27
- package/dist/edge.index-7pBw2C-U.mjs.map +0 -1
- package/dist/edge.index-CAvykaH6.cjs +0 -1632
- package/dist/edge.index-CAvykaH6.cjs.map +0 -1
- package/dist/edge.index.cjs +0 -68
- package/dist/edge.index.d.cts +0 -1
- package/dist/index.cjs +0 -496
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -1
- package/dist/manifest.index.cjs +0 -33
- package/dist/manifest.index.cjs.map +0 -1
- package/dist/manifest.index.d.cts +0 -1
|
@@ -1,1632 +0,0 @@
|
|
|
1
|
-
//#region \0rolldown/runtime.js
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __exportAll = (all, no_symbols) => {
|
|
9
|
-
let target = {};
|
|
10
|
-
for (var name in all) {
|
|
11
|
-
__defProp(target, name, {
|
|
12
|
-
get: all[name],
|
|
13
|
-
enumerable: true
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
if (!no_symbols) {
|
|
17
|
-
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
18
|
-
}
|
|
19
|
-
return target;
|
|
20
|
-
};
|
|
21
|
-
var __copyProps = (to, from, except, desc) => {
|
|
22
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
-
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
24
|
-
key = keys[i];
|
|
25
|
-
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
26
|
-
__defProp(to, key, {
|
|
27
|
-
get: ((k) => from[k]).bind(null, key),
|
|
28
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return to;
|
|
34
|
-
};
|
|
35
|
-
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
36
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
37
|
-
value: mod,
|
|
38
|
-
enumerable: true
|
|
39
|
-
}) : target, mod));
|
|
40
|
-
|
|
41
|
-
//#endregion
|
|
42
|
-
require("reflect-metadata");
|
|
43
|
-
let _seedcord_errors_internal = require("@seedcord/errors/internal");
|
|
44
|
-
let envapt = require("envapt");
|
|
45
|
-
let _discordjs_rest = require("@discordjs/rest");
|
|
46
|
-
let _seedcord_core = require("@seedcord/core");
|
|
47
|
-
let _seedcord_core_internal = require("@seedcord/core/internal");
|
|
48
|
-
let _seedcord_logger = require("@seedcord/logger");
|
|
49
|
-
let _seedcord_rate_limiter = require("@seedcord/rate-limiter");
|
|
50
|
-
let discord_api_types_v10 = require("discord-api-types/v10");
|
|
51
|
-
let _discordjs_core_http_only = require("@discordjs/core/http-only");
|
|
52
|
-
let _seedcord_utils_internal = require("@seedcord/utils/internal");
|
|
53
|
-
let _seedcord_errors = require("@seedcord/errors");
|
|
54
|
-
|
|
55
|
-
//#region src/reply/ReplySender.ts
|
|
56
|
-
function wireAllowedMentions(mentions) {
|
|
57
|
-
const { repliedUser, ...wire } = mentions;
|
|
58
|
-
return {
|
|
59
|
-
...wire,
|
|
60
|
-
...repliedUser !== void 0 && { replied_user: repliedUser }
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
function wireAttachments(files) {
|
|
64
|
-
if (!files?.some((file) => file.description ?? file.title)) return null;
|
|
65
|
-
return files.map((file, index) => ({
|
|
66
|
-
id: index,
|
|
67
|
-
filename: file.name,
|
|
68
|
-
...file.description && { description: file.description },
|
|
69
|
-
...file.title && { title: file.title }
|
|
70
|
-
}));
|
|
71
|
-
}
|
|
72
|
-
function callbackData(reply, flags) {
|
|
73
|
-
const attachments = wireAttachments(reply.files);
|
|
74
|
-
return {
|
|
75
|
-
flags,
|
|
76
|
-
components: reply.components,
|
|
77
|
-
...reply.allowedMentions && { allowed_mentions: wireAllowedMentions(reply.allowedMentions) },
|
|
78
|
-
...attachments && { attachments }
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Writes interaction responses over the Discord REST callback and webhook endpoints. Construction is
|
|
83
|
-
* internal to the repliable handler bases and the dispatcher.
|
|
84
|
-
*/
|
|
85
|
-
var ReplySender = class extends _seedcord_core_internal.BaseReplySender {
|
|
86
|
-
ref;
|
|
87
|
-
rest;
|
|
88
|
-
constructor(ref, rest, routeId, bus) {
|
|
89
|
-
super(routeId, {
|
|
90
|
-
bus,
|
|
91
|
-
interactionId: ref.id
|
|
92
|
-
});
|
|
93
|
-
this.ref = ref;
|
|
94
|
-
this.rest = rest;
|
|
95
|
-
}
|
|
96
|
-
async writeReply(response, opts) {
|
|
97
|
-
const reply = this.serialize(response);
|
|
98
|
-
const result = await this.callback(discord_api_types_v10.InteractionResponseType.ChannelMessageWithSource, callbackData(reply, (0, _seedcord_core_internal.sendFlags)(opts)), reply.files);
|
|
99
|
-
return this.createdMessage(result);
|
|
100
|
-
}
|
|
101
|
-
async writeDefer(opts) {
|
|
102
|
-
await this.callback(discord_api_types_v10.InteractionResponseType.DeferredChannelMessageWithSource, { flags: (0, _seedcord_core_internal.deferFlags)(opts) });
|
|
103
|
-
}
|
|
104
|
-
async writeDeferUpdate() {
|
|
105
|
-
await this.callback(discord_api_types_v10.InteractionResponseType.DeferredMessageUpdate);
|
|
106
|
-
}
|
|
107
|
-
async writeUpdate(response) {
|
|
108
|
-
const reply = this.serialize(response);
|
|
109
|
-
const result = await this.callback(discord_api_types_v10.InteractionResponseType.UpdateMessage, callbackData(reply, discord_api_types_v10.MessageFlags.IsComponentsV2), reply.files);
|
|
110
|
-
return this.createdMessage(result);
|
|
111
|
-
}
|
|
112
|
-
async writeFollowUp(response, opts) {
|
|
113
|
-
const reply = this.serialize(response);
|
|
114
|
-
return await this.rest.post(discord_api_types_v10.Routes.webhook(this.ref.application_id, this.ref.token), {
|
|
115
|
-
body: callbackData(reply, (0, _seedcord_core_internal.sendFlags)(opts)),
|
|
116
|
-
query: new URLSearchParams({ wait: "true" }),
|
|
117
|
-
...reply.files && { files: this.rawFiles(reply.files) }
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
writeEditOriginal(response) {
|
|
121
|
-
return this.editMessage(discord_api_types_v10.Routes.webhookMessage(this.ref.application_id, this.ref.token), this.serialize(response));
|
|
122
|
-
}
|
|
123
|
-
writeEditTarget(targetId, response) {
|
|
124
|
-
const route = discord_api_types_v10.Routes.webhookMessage(this.ref.application_id, this.ref.token, targetId);
|
|
125
|
-
return this.editMessage(route, this.serialize(response));
|
|
126
|
-
}
|
|
127
|
-
async writeDeleteOriginal() {
|
|
128
|
-
await this.rest.delete(discord_api_types_v10.Routes.webhookMessage(this.ref.application_id, this.ref.token));
|
|
129
|
-
}
|
|
130
|
-
async writeDeleteTarget(targetId) {
|
|
131
|
-
await this.rest.delete(discord_api_types_v10.Routes.webhookMessage(this.ref.application_id, this.ref.token, targetId));
|
|
132
|
-
}
|
|
133
|
-
async writeModal(data) {
|
|
134
|
-
await this.callback(discord_api_types_v10.InteractionResponseType.Modal, data);
|
|
135
|
-
}
|
|
136
|
-
async callback(type, data, files) {
|
|
137
|
-
const withResponse = type === discord_api_types_v10.InteractionResponseType.ChannelMessageWithSource || type === discord_api_types_v10.InteractionResponseType.UpdateMessage;
|
|
138
|
-
return await this.rest.post(discord_api_types_v10.Routes.interactionCallback(this.ref.id, this.ref.token), {
|
|
139
|
-
body: {
|
|
140
|
-
type,
|
|
141
|
-
...data !== void 0 && { data }
|
|
142
|
-
},
|
|
143
|
-
...withResponse && { query: new URLSearchParams({ with_response: "true" }) },
|
|
144
|
-
...files && { files: this.rawFiles(files) }
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
async editMessage(route, reply) {
|
|
148
|
-
return await this.rest.patch(route, {
|
|
149
|
-
body: callbackData(reply, discord_api_types_v10.MessageFlags.IsComponentsV2),
|
|
150
|
-
...reply.files && { files: this.rawFiles(reply.files) }
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
rawFiles(files) {
|
|
154
|
-
return files.map((file) => ({
|
|
155
|
-
name: file.name,
|
|
156
|
-
data: file.data
|
|
157
|
-
}));
|
|
158
|
-
}
|
|
159
|
-
createdMessage(result) {
|
|
160
|
-
return result.resource?.message;
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
//#endregion
|
|
165
|
-
//#region src/api.ts
|
|
166
|
-
const apis = /* @__PURE__ */ new WeakMap();
|
|
167
|
-
function apiFor(rest) {
|
|
168
|
-
let api = apis.get(rest);
|
|
169
|
-
if (!api) {
|
|
170
|
-
api = new _discordjs_core_http_only.API(rest);
|
|
171
|
-
apis.set(rest, api);
|
|
172
|
-
}
|
|
173
|
-
return api;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
//#endregion
|
|
177
|
-
//#region src/handlers/RepliableHandler.ts
|
|
178
|
-
/**
|
|
179
|
-
* Shared base the repliable HTTP interaction handlers extend.
|
|
180
|
-
*
|
|
181
|
-
* Not a public entry point. Extend {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},
|
|
182
|
-
* {@link SelectMenuHandler}, or {@link ContextMenuHandler} instead. This class binds the core reply base
|
|
183
|
-
* to the http sender.
|
|
184
|
-
*
|
|
185
|
-
* @typeParam Event - The repliable interaction type this handler processes
|
|
186
|
-
*/
|
|
187
|
-
var RepliableHandler = class extends _seedcord_core.RepliableHandler {
|
|
188
|
-
/** @internal */
|
|
189
|
-
getEvent() {
|
|
190
|
-
return this.event;
|
|
191
|
-
}
|
|
192
|
-
buildSender(event, core, routeId) {
|
|
193
|
-
const ref = {
|
|
194
|
-
application_id: event.application_id,
|
|
195
|
-
id: event.id,
|
|
196
|
-
token: event.token
|
|
197
|
-
};
|
|
198
|
-
return new ReplySender(ref, core.rest, routeId, core.bus);
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Typed Discord REST API (`@discordjs/core/http-only`) over `core.rest`, one instance per core.
|
|
202
|
-
* The interaction callbacks on `api.interactions` bypass the reply surface. The
|
|
203
|
-
* `no-raw-interaction-acks` rule flags them in handler classes.
|
|
204
|
-
*/
|
|
205
|
-
get api() {
|
|
206
|
-
return apiFor(this.core.rest);
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
//#endregion
|
|
211
|
-
//#region src/gates/context.ts
|
|
212
|
-
function interactionGateContext(payload, core, routeId) {
|
|
213
|
-
const member = payload.member ?? null;
|
|
214
|
-
const resolved = member?.user ?? payload.user;
|
|
215
|
-
return {
|
|
216
|
-
kind: "interaction",
|
|
217
|
-
core,
|
|
218
|
-
interaction: payload,
|
|
219
|
-
user: resolved,
|
|
220
|
-
member,
|
|
221
|
-
userId: resolved?.id ?? null,
|
|
222
|
-
guildId: payload.guild_id ?? null,
|
|
223
|
-
channelId: payload.channel?.id ?? null,
|
|
224
|
-
memberRoleIds: member?.roles ?? [],
|
|
225
|
-
memberPermissions: member ? BigInt(member.permissions) : null,
|
|
226
|
-
appPermissions: BigInt(payload.app_permissions),
|
|
227
|
-
routeId
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
//#endregion
|
|
232
|
-
//#region src/dispatch/slashRouteOf.ts
|
|
233
|
-
function slashRouteOf(data) {
|
|
234
|
-
const first = data.options?.[0];
|
|
235
|
-
if (first?.type === discord_api_types_v10.ApplicationCommandOptionType.SubcommandGroup) return (0, _seedcord_utils_internal.buildSlashRoute)(data.name, first.options[0]?.name, first.name);
|
|
236
|
-
if (first?.type === discord_api_types_v10.ApplicationCommandOptionType.Subcommand) return (0, _seedcord_utils_internal.buildSlashRoute)(data.name, first.name);
|
|
237
|
-
return data.name;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
//#endregion
|
|
241
|
-
//#region src/dispatch/reportFault.ts
|
|
242
|
-
let faultLogger;
|
|
243
|
-
function logger$1() {
|
|
244
|
-
faultLogger ??= new _seedcord_logger.Logger("Faults", { channel: "errors" });
|
|
245
|
-
return faultLogger;
|
|
246
|
-
}
|
|
247
|
-
function reportFault(error, uuid, payload, core) {
|
|
248
|
-
const throttle = _seedcord_core_internal.FaultThrottle.for(core);
|
|
249
|
-
const key = `${routeKeyOf(payload)}:${nameOf(error)}`;
|
|
250
|
-
if (!throttle.shouldReport(key)) {
|
|
251
|
-
logger$1().debug(`throttled duplicate fault ${_seedcord_logger.paint.mute(key)}`);
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
const source = interactionSource(payload);
|
|
255
|
-
if (error instanceof _seedcord_core.Notice && source) core.bus[_seedcord_core_internal.PublishDefault]("handledException", {
|
|
256
|
-
denial: error,
|
|
257
|
-
uuid,
|
|
258
|
-
source
|
|
259
|
-
});
|
|
260
|
-
else core.bus[_seedcord_core_internal.PublishDefault]("unknownException", {
|
|
261
|
-
uuid,
|
|
262
|
-
error,
|
|
263
|
-
...actors(payload),
|
|
264
|
-
metadata: payload
|
|
265
|
-
});
|
|
266
|
-
throttle.markReported(key);
|
|
267
|
-
}
|
|
268
|
-
function routeKeyOf(payload) {
|
|
269
|
-
if (payload.type === discord_api_types_v10.InteractionType.ApplicationCommandAutocomplete) return `autocomplete:${slashRouteOf(payload.data)}`;
|
|
270
|
-
if (payload.type === discord_api_types_v10.InteractionType.ApplicationCommand) return payload.data.type === discord_api_types_v10.ApplicationCommandType.ChatInput ? slashRouteOf(payload.data) : payload.data.name;
|
|
271
|
-
return (0, _seedcord_core_internal.prefixOf)(payload.data.custom_id) || payload.data.custom_id;
|
|
272
|
-
}
|
|
273
|
-
function nameOf(error) {
|
|
274
|
-
if (error instanceof _seedcord_core.Notice) return error.name;
|
|
275
|
-
if (error instanceof _discordjs_rest.DiscordAPIError) return String(error.code);
|
|
276
|
-
return error.constructor.name;
|
|
277
|
-
}
|
|
278
|
-
function actors(payload) {
|
|
279
|
-
const user = payload.member?.user ?? payload.user;
|
|
280
|
-
return {
|
|
281
|
-
guild: void 0,
|
|
282
|
-
user: user ? {
|
|
283
|
-
id: user.id,
|
|
284
|
-
username: user.username
|
|
285
|
-
} : void 0
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
function interactionSource(payload) {
|
|
289
|
-
const user = payload.member?.user ?? payload.user;
|
|
290
|
-
if (!user) return null;
|
|
291
|
-
const common = {
|
|
292
|
-
kind: "interaction",
|
|
293
|
-
userId: user.id,
|
|
294
|
-
guildId: payload.guild_id ?? null,
|
|
295
|
-
channelId: payload.channel?.id ?? null,
|
|
296
|
-
interactionId: payload.id,
|
|
297
|
-
raw: payload
|
|
298
|
-
};
|
|
299
|
-
if (payload.type === discord_api_types_v10.InteractionType.ApplicationCommand) {
|
|
300
|
-
const isSlash = payload.data.type === discord_api_types_v10.ApplicationCommandType.ChatInput;
|
|
301
|
-
return {
|
|
302
|
-
...common,
|
|
303
|
-
interactionKind: isSlash ? "slash" : "context-menu",
|
|
304
|
-
command: routeKeyOf(payload),
|
|
305
|
-
customId: null
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
if (payload.type === discord_api_types_v10.InteractionType.ModalSubmit) return {
|
|
309
|
-
...common,
|
|
310
|
-
interactionKind: "modal",
|
|
311
|
-
command: null,
|
|
312
|
-
customId: payload.data.custom_id
|
|
313
|
-
};
|
|
314
|
-
if (payload.type === discord_api_types_v10.InteractionType.MessageComponent) {
|
|
315
|
-
const isButton = payload.data.component_type === discord_api_types_v10.ComponentType.Button;
|
|
316
|
-
return {
|
|
317
|
-
...common,
|
|
318
|
-
interactionKind: isButton ? "button" : "select",
|
|
319
|
-
command: null,
|
|
320
|
-
customId: payload.data.custom_id
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
//#endregion
|
|
327
|
-
//#region src/dispatch/dispatchInteraction.ts
|
|
328
|
-
let dispatchLogger;
|
|
329
|
-
function logger() {
|
|
330
|
-
dispatchLogger ??= new _seedcord_logger.Logger("Dispatcher", { channel: "interactions" });
|
|
331
|
-
return dispatchLogger;
|
|
332
|
-
}
|
|
333
|
-
function createCore(config, token) {
|
|
334
|
-
const draft = {
|
|
335
|
-
config,
|
|
336
|
-
rateLimiter: config.store ?? new _seedcord_rate_limiter.MemoryRateLimiter(),
|
|
337
|
-
rest: new _discordjs_rest.REST().setToken(token)
|
|
338
|
-
};
|
|
339
|
-
draft.bus = new _seedcord_core.Bus(draft);
|
|
340
|
-
return draft;
|
|
341
|
-
}
|
|
342
|
-
function isHandlerCtor(value) {
|
|
343
|
-
return typeof value === "function" && value.prototype instanceof _seedcord_core.BaseHandler;
|
|
344
|
-
}
|
|
345
|
-
const HARMLESS_API_CODES = /* @__PURE__ */ new Set([
|
|
346
|
-
discord_api_types_v10.RESTJSONErrorCodes.UnknownInteraction,
|
|
347
|
-
discord_api_types_v10.RESTJSONErrorCodes.InteractionHasAlreadyBeenAcknowledged,
|
|
348
|
-
discord_api_types_v10.RESTJSONErrorCodes.UnknownMessage
|
|
349
|
-
]);
|
|
350
|
-
async function sendGuarded(routeId, send) {
|
|
351
|
-
try {
|
|
352
|
-
await send();
|
|
353
|
-
} catch (error) {
|
|
354
|
-
if (error instanceof _discordjs_rest.DiscordAPIError && HARMLESS_API_CODES.has(error.code)) {
|
|
355
|
-
logger().debug(`boundary send hit harmless code ${_seedcord_logger.paint.amber(String(error.code))}`);
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
logger().error(`boundary send failed for route ${_seedcord_logger.paint.sky.bold(routeId)}`, error);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
async function respondEmptyChoices(scope) {
|
|
362
|
-
const telemetry = {
|
|
363
|
-
bus: scope.core.bus,
|
|
364
|
-
interactionId: scope.payload.id
|
|
365
|
-
};
|
|
366
|
-
await (0, _seedcord_core_internal.reportedWrite)(telemetry, scope.routeId, "respond", () => scope.core.rest.post(discord_api_types_v10.Routes.interactionCallback(scope.payload.id, scope.payload.token), { body: {
|
|
367
|
-
type: discord_api_types_v10.InteractionResponseType.ApplicationCommandAutocompleteResult,
|
|
368
|
-
data: { choices: [] }
|
|
369
|
-
} }));
|
|
370
|
-
}
|
|
371
|
-
function renderContext(core, uuid) {
|
|
372
|
-
const developerUsername = core.config.notifications?.developerUsername;
|
|
373
|
-
return developerUsername === void 0 ? { uuid } : {
|
|
374
|
-
uuid,
|
|
375
|
-
developerUsername
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
async function handleNotice(notice, uuid, scope) {
|
|
379
|
-
if (notice.report) {
|
|
380
|
-
logger().error(`${notice.name}: ${_seedcord_logger.paint.mute(uuid)}`, notice);
|
|
381
|
-
reportFault(notice, uuid, scope.payload, scope.core);
|
|
382
|
-
}
|
|
383
|
-
const { sender } = scope;
|
|
384
|
-
if (!sender) {
|
|
385
|
-
await sendGuarded(scope.routeId, () => respondEmptyChoices(scope));
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
const response = notice.render(renderContext(scope.core, uuid));
|
|
389
|
-
await sendGuarded(scope.routeId, () => sender.send(response, { ephemeral: notice.ephemeral }));
|
|
390
|
-
}
|
|
391
|
-
async function handleRawFault(error, uuid, scope) {
|
|
392
|
-
const { core, sender } = scope;
|
|
393
|
-
if (core.config.errors?.errorStack ?? false) logger().error(_seedcord_logger.paint.mute(uuid), error);
|
|
394
|
-
else logger().error(`${_seedcord_logger.paint.mute(uuid)} | ${error.message}`);
|
|
395
|
-
reportFault(error, uuid, scope.payload, core);
|
|
396
|
-
if (!sender) {
|
|
397
|
-
await sendGuarded(scope.routeId, () => respondEmptyChoices(scope));
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
const override = core.config.errors?.defaultError;
|
|
401
|
-
const response = (override ? new override(uuid) : new _seedcord_core.Fault()).render(renderContext(core, uuid));
|
|
402
|
-
await sendGuarded(scope.routeId, () => sender.send(response, { ephemeral: true }));
|
|
403
|
-
}
|
|
404
|
-
async function handleFault(caught, scope) {
|
|
405
|
-
if (caught instanceof _seedcord_core.Silence) {
|
|
406
|
-
if (caught.reason !== void 0 && (scope.core.config.errors?.logSilences ?? true)) logger().debug(`Silence: ${caught.reason}`);
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
const uuid = crypto.randomUUID();
|
|
410
|
-
if (caught instanceof _seedcord_core.Notice) {
|
|
411
|
-
await handleNotice(caught, uuid, scope);
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
const error = (0, _seedcord_core_internal.asError)(caught);
|
|
415
|
-
const ignore = new Set(scope.core.config.errors?.ignoreApiCodes ?? []);
|
|
416
|
-
if (error instanceof _discordjs_rest.DiscordAPIError && ignore.has(error.code)) {
|
|
417
|
-
logger().debug(`swallowed api code ${_seedcord_logger.paint.amber(String(error.code))}`);
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
await handleRawFault(error, uuid, scope);
|
|
421
|
-
}
|
|
422
|
-
function unhandledRouteId(match) {
|
|
423
|
-
if (match.routeId) return match.routeId;
|
|
424
|
-
const key = match.attemptedKey ?? "";
|
|
425
|
-
return `${match.kind}:${key.length > 0 ? key : "unrouted"}`;
|
|
426
|
-
}
|
|
427
|
-
function freshScope(match, payload, core) {
|
|
428
|
-
const ref = {
|
|
429
|
-
application_id: payload.application_id,
|
|
430
|
-
id: payload.id,
|
|
431
|
-
token: payload.token
|
|
432
|
-
};
|
|
433
|
-
const routeId = unhandledRouteId(match);
|
|
434
|
-
return {
|
|
435
|
-
core,
|
|
436
|
-
payload,
|
|
437
|
-
routeId,
|
|
438
|
-
sender: match.kind === "autocomplete" ? null : new ReplySender(ref, core.rest, routeId, core.bus)
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
function dispatchReporter(match, payload, core) {
|
|
442
|
-
const startedAt = performance.now();
|
|
443
|
-
const queuedMs = (0, _seedcord_core_internal.queuedMsFor)(payload.id);
|
|
444
|
-
return (outcome) => {
|
|
445
|
-
(0, _seedcord_core_internal.reportDispatch)(core.bus, {
|
|
446
|
-
routeId: unhandledRouteId(match),
|
|
447
|
-
interactionId: payload.id,
|
|
448
|
-
kind: match.kind,
|
|
449
|
-
outcome,
|
|
450
|
-
fallback: match.routeId === null,
|
|
451
|
-
startedAt,
|
|
452
|
-
queuedMs
|
|
453
|
-
});
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
async function answer(caught, scope, report, outcome = (0, _seedcord_core_internal.outcomeFor)(caught)) {
|
|
457
|
-
try {
|
|
458
|
-
await handleFault(caught, scope);
|
|
459
|
-
} finally {
|
|
460
|
-
report(outcome);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
async function loadHandlerCtor(match, payload, core, report) {
|
|
464
|
-
const routeId = unhandledRouteId(match);
|
|
465
|
-
let exported;
|
|
466
|
-
try {
|
|
467
|
-
exported = await match.load();
|
|
468
|
-
} catch (caught) {
|
|
469
|
-
logger().error(`Route ${_seedcord_logger.paint.sky.bold(routeId)} failed to load its handler.`, caught);
|
|
470
|
-
await answer(caught, freshScope(match, payload, core), report, "failed");
|
|
471
|
-
return null;
|
|
472
|
-
}
|
|
473
|
-
if (isHandlerCtor(exported)) return exported;
|
|
474
|
-
logger().error(`Route ${_seedcord_logger.paint.sky.bold(routeId)} loaded an export that is not a handler class.`);
|
|
475
|
-
await answer(/* @__PURE__ */ new Error(`route ${routeId} loaded an export that is not a handler class`), freshScope(match, payload, core), report, "failed");
|
|
476
|
-
return null;
|
|
477
|
-
}
|
|
478
|
-
async function dispatchInteraction(args) {
|
|
479
|
-
const { match, payload, core } = args;
|
|
480
|
-
const report = dispatchReporter(match, payload, core);
|
|
481
|
-
const ctor = await loadHandlerCtor(match, payload, core, report);
|
|
482
|
-
if (!ctor) return null;
|
|
483
|
-
const routeId = unhandledRouteId(match);
|
|
484
|
-
logger().debug(`Processing ${_seedcord_logger.paint.mint.bold(routeId)} with ${_seedcord_logger.paint.mute(ctor.name)}`);
|
|
485
|
-
const dispatch = new _seedcord_core.DispatchContext(routeId);
|
|
486
|
-
let handler;
|
|
487
|
-
try {
|
|
488
|
-
handler = new ctor(payload, core, dispatch);
|
|
489
|
-
} catch (caught) {
|
|
490
|
-
await answer(caught, freshScope(match, payload, core), report);
|
|
491
|
-
return null;
|
|
492
|
-
}
|
|
493
|
-
const scope = {
|
|
494
|
-
core,
|
|
495
|
-
payload,
|
|
496
|
-
routeId,
|
|
497
|
-
sender: handler instanceof RepliableHandler ? handler.getSender() : null
|
|
498
|
-
};
|
|
499
|
-
const refusal = await gateRefusal(ctor, match, payload, core);
|
|
500
|
-
if (refusal) {
|
|
501
|
-
await answer(refusal.caught, scope, report);
|
|
502
|
-
return null;
|
|
503
|
-
}
|
|
504
|
-
return async () => {
|
|
505
|
-
try {
|
|
506
|
-
await handler.execute();
|
|
507
|
-
report("handled");
|
|
508
|
-
} catch (caught) {
|
|
509
|
-
await answer(caught, scope, report);
|
|
510
|
-
}
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
async function gateRefusal(ctor, match, payload, core) {
|
|
514
|
-
if (match.kind === "autocomplete" || payload.type === discord_api_types_v10.InteractionType.ApplicationCommandAutocomplete) return null;
|
|
515
|
-
const monitor = (0, _seedcord_core_internal.slowGateMonitor)();
|
|
516
|
-
try {
|
|
517
|
-
await (0, _seedcord_core_internal.runHandlerGates)(ctor, interactionGateContext(payload, core, match.routeId), match.routeId ?? void 0, monitor?.observe);
|
|
518
|
-
return null;
|
|
519
|
-
} catch (caught) {
|
|
520
|
-
return { caught };
|
|
521
|
-
} finally {
|
|
522
|
-
monitor?.report(match.routeId);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
//#endregion
|
|
527
|
-
//#region src/dispatch/registerSubscribers.ts
|
|
528
|
-
function registerSubscribers(bus, manifest) {
|
|
529
|
-
for (const row of manifest.subscriberRoutes) bus.register({
|
|
530
|
-
keys: row.keys,
|
|
531
|
-
frequency: row.frequency,
|
|
532
|
-
resolve: async () => {
|
|
533
|
-
const keys = row.keys.join(", ");
|
|
534
|
-
let module;
|
|
535
|
-
try {
|
|
536
|
-
module = await row.load();
|
|
537
|
-
} catch (caught) {
|
|
538
|
-
throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.RouteModuleLoadFailed, [keys, row.from], { cause: caught });
|
|
539
|
-
}
|
|
540
|
-
if (!Object.hasOwn(module, row.exportName)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionRouteExportMissing, [
|
|
541
|
-
keys,
|
|
542
|
-
row.exportName,
|
|
543
|
-
row.from
|
|
544
|
-
]);
|
|
545
|
-
const exported = module[row.exportName];
|
|
546
|
-
if (typeof exported !== "function" || !(exported.prototype instanceof _seedcord_core.Subscriber)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.SubscriberRouteNotASubscriber, [
|
|
547
|
-
keys,
|
|
548
|
-
row.exportName,
|
|
549
|
-
row.from
|
|
550
|
-
]);
|
|
551
|
-
return exported;
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
//#endregion
|
|
557
|
-
//#region src/inputs/HttpSlashOptions.ts
|
|
558
|
-
var HttpSlashOptions = class {
|
|
559
|
-
leaves = /* @__PURE__ */ new Map();
|
|
560
|
-
resolved;
|
|
561
|
-
constructor(data) {
|
|
562
|
-
this.resolved = data.resolved;
|
|
563
|
-
let options = data.options ?? [];
|
|
564
|
-
const first = options[0];
|
|
565
|
-
if (first?.type === discord_api_types_v10.ApplicationCommandOptionType.SubcommandGroup) options = first.options;
|
|
566
|
-
const second = options[0];
|
|
567
|
-
if (second?.type === discord_api_types_v10.ApplicationCommandOptionType.Subcommand) options = second.options ?? [];
|
|
568
|
-
for (const option of options) if (option.type !== discord_api_types_v10.ApplicationCommandOptionType.Subcommand && option.type !== discord_api_types_v10.ApplicationCommandOptionType.SubcommandGroup) this.leaves.set(option.name, option);
|
|
569
|
-
}
|
|
570
|
-
getString(name) {
|
|
571
|
-
const leaf = this.leaves.get(name);
|
|
572
|
-
return leaf?.type === discord_api_types_v10.ApplicationCommandOptionType.String ? leaf.value : null;
|
|
573
|
-
}
|
|
574
|
-
getInteger(name) {
|
|
575
|
-
const leaf = this.leaves.get(name);
|
|
576
|
-
return leaf?.type === discord_api_types_v10.ApplicationCommandOptionType.Integer ? numeric(leaf.value) : null;
|
|
577
|
-
}
|
|
578
|
-
getNumber(name) {
|
|
579
|
-
const leaf = this.leaves.get(name);
|
|
580
|
-
return leaf?.type === discord_api_types_v10.ApplicationCommandOptionType.Number ? numeric(leaf.value) : null;
|
|
581
|
-
}
|
|
582
|
-
getBoolean(name) {
|
|
583
|
-
const leaf = this.leaves.get(name);
|
|
584
|
-
return leaf?.type === discord_api_types_v10.ApplicationCommandOptionType.Boolean ? leaf.value : null;
|
|
585
|
-
}
|
|
586
|
-
getUser(name) {
|
|
587
|
-
const leaf = this.leaves.get(name);
|
|
588
|
-
if (leaf?.type !== discord_api_types_v10.ApplicationCommandOptionType.User) return null;
|
|
589
|
-
return this.resolved?.users?.[leaf.value] ?? null;
|
|
590
|
-
}
|
|
591
|
-
getMember(name) {
|
|
592
|
-
const leaf = this.leaves.get(name);
|
|
593
|
-
if (leaf?.type !== discord_api_types_v10.ApplicationCommandOptionType.User) return null;
|
|
594
|
-
return this.resolved?.members?.[leaf.value] ?? null;
|
|
595
|
-
}
|
|
596
|
-
getChannel(name) {
|
|
597
|
-
const leaf = this.leaves.get(name);
|
|
598
|
-
if (leaf?.type !== discord_api_types_v10.ApplicationCommandOptionType.Channel) return null;
|
|
599
|
-
return this.resolved?.channels?.[leaf.value] ?? null;
|
|
600
|
-
}
|
|
601
|
-
getRole(name) {
|
|
602
|
-
const leaf = this.leaves.get(name);
|
|
603
|
-
if (leaf?.type !== discord_api_types_v10.ApplicationCommandOptionType.Role) return null;
|
|
604
|
-
return this.resolved?.roles?.[leaf.value] ?? null;
|
|
605
|
-
}
|
|
606
|
-
getAttachment(name) {
|
|
607
|
-
const leaf = this.leaves.get(name);
|
|
608
|
-
if (leaf?.type !== discord_api_types_v10.ApplicationCommandOptionType.Attachment) return null;
|
|
609
|
-
return this.resolved?.attachments?.[leaf.value] ?? null;
|
|
610
|
-
}
|
|
611
|
-
getMentionable(name) {
|
|
612
|
-
const leaf = this.leaves.get(name);
|
|
613
|
-
if (leaf?.type !== discord_api_types_v10.ApplicationCommandOptionType.Mentionable) return null;
|
|
614
|
-
const resolved = this.resolved;
|
|
615
|
-
return resolved?.members?.[leaf.value] ?? resolved?.users?.[leaf.value] ?? resolved?.roles?.[leaf.value] ?? null;
|
|
616
|
-
}
|
|
617
|
-
getFocused() {
|
|
618
|
-
for (const leaf of this.leaves.values()) if ("focused" in leaf && leaf.focused === true) return {
|
|
619
|
-
name: leaf.name,
|
|
620
|
-
value: String(leaf.value)
|
|
621
|
-
};
|
|
622
|
-
return null;
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
function numeric(value) {
|
|
626
|
-
if (typeof value === "number") return value;
|
|
627
|
-
if (value.trim() === "") return null;
|
|
628
|
-
const parsed = Number(value);
|
|
629
|
-
return Number.isNaN(parsed) ? null : parsed;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
//#endregion
|
|
633
|
-
//#region src/handlers/interaction/AutocompleteHandler.ts
|
|
634
|
-
/**
|
|
635
|
-
* Base class for an autocomplete handler on the HTTP transport.
|
|
636
|
-
*
|
|
637
|
-
* Pass the command route(s) from the generated registry as the generic. Branch on the focused field with
|
|
638
|
-
* `this.match`, read already-entered sibling options with `this.options`, and find which command fired
|
|
639
|
-
* with `this.route`.
|
|
640
|
-
*
|
|
641
|
-
* @typeParam Route - One or more route keys from {@link SlashOptionRegistry}, e.g. `'search'`.
|
|
642
|
-
*/
|
|
643
|
-
var AutocompleteHandler = class extends _seedcord_core.BaseHandler {
|
|
644
|
-
constructor(event, core, dispatch) {
|
|
645
|
-
super(event, core, dispatch, "interactions");
|
|
646
|
-
}
|
|
647
|
-
/**
|
|
648
|
-
* Typed Discord REST API (`@discordjs/core/http-only`) over `core.rest`, one instance per core.
|
|
649
|
-
*/
|
|
650
|
-
get api() {
|
|
651
|
-
return apiFor(this.core.rest);
|
|
652
|
-
}
|
|
653
|
-
resolver;
|
|
654
|
-
get reader() {
|
|
655
|
-
this.resolver ??= new HttpSlashOptions(this.event.data);
|
|
656
|
-
return this.resolver;
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* The focused option for this interaction. `value` is always the raw partial string the user is typing,
|
|
660
|
-
* even for an integer or number option, so coerce it yourself when you need a number.
|
|
661
|
-
*/
|
|
662
|
-
get focused() {
|
|
663
|
-
const focused = this.reader.getFocused();
|
|
664
|
-
if (!focused) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.AutocompleteNoFocusedOption);
|
|
665
|
-
return focused;
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* Run the arm for the focused field. An autocomplete always dispatches by which field is focused.
|
|
669
|
-
* Match is how you read it.
|
|
670
|
-
*
|
|
671
|
-
* Provide one arm per autocompletable field across the registered commands, keyed by field name. Each
|
|
672
|
-
* arm receives the focused partial value and a `respond` typed to that field's choice value. The
|
|
673
|
-
* arms must cover every autocompletable field, a missing field or an unknown key is a compile error. A
|
|
674
|
-
* focused field with no arm, only reachable from a stale-deployed command, throws at runtime.
|
|
675
|
-
*
|
|
676
|
-
* @param arms - One handler per autocompletable field, keyed by field name.
|
|
677
|
-
* @returns The result of the arm that ran.
|
|
678
|
-
*/
|
|
679
|
-
async match(arms) {
|
|
680
|
-
const { name, value } = this.focused;
|
|
681
|
-
const respond = (choices) => this.respond(choices);
|
|
682
|
-
const arm = Object.hasOwn(arms, name) ? arms[name] : void 0;
|
|
683
|
-
if (!arm) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.AutocompleteMatchArmMissing, [name]);
|
|
684
|
-
return await arm(value, respond);
|
|
685
|
-
}
|
|
686
|
-
/** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */
|
|
687
|
-
async respond(choices) {
|
|
688
|
-
const routeId = this.dispatch?.routeId ?? "autocomplete";
|
|
689
|
-
await (0, _seedcord_core_internal.reportedWrite)({
|
|
690
|
-
bus: this.core.bus,
|
|
691
|
-
interactionId: this.event.id
|
|
692
|
-
}, routeId, "respond", () => this.core.rest.post(discord_api_types_v10.Routes.interactionCallback(this.event.id, this.event.token), { body: {
|
|
693
|
-
type: discord_api_types_v10.InteractionResponseType.ApplicationCommandAutocompleteResult,
|
|
694
|
-
data: { choices }
|
|
695
|
-
} }));
|
|
696
|
-
}
|
|
697
|
-
/** The firing command route, for a field whose completion differs per registered command. */
|
|
698
|
-
get route() {
|
|
699
|
-
return slashRouteOf(this.event.data);
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* The already-entered options on this command, restricted to the kinds Discord resolves during autocomplete
|
|
703
|
-
* (string, integer, number, boolean) with every read nullable, since a sibling is partial while the user
|
|
704
|
-
* types the focused field. The focused field is on `this.focused`.
|
|
705
|
-
*/
|
|
706
|
-
get options() {
|
|
707
|
-
return this.reader;
|
|
708
|
-
}
|
|
709
|
-
};
|
|
710
|
-
|
|
711
|
-
//#endregion
|
|
712
|
-
//#region src/handlers/defaults/UnhandledAutocomplete.ts
|
|
713
|
-
var UnhandledAutocomplete = class extends AutocompleteHandler {
|
|
714
|
-
async execute() {
|
|
715
|
-
await this.respond([]);
|
|
716
|
-
}
|
|
717
|
-
};
|
|
718
|
-
|
|
719
|
-
//#endregion
|
|
720
|
-
//#region src/handlers/interaction/InteractionHandler.ts
|
|
721
|
-
/**
|
|
722
|
-
* Shared base the repliable interaction handlers extend.
|
|
723
|
-
*
|
|
724
|
-
* Not a public entry point. Extend {@link SlashHandler}, {@link ContextMenuHandler}, {@link ButtonHandler},
|
|
725
|
-
* {@link SelectMenuHandler}, or {@link ModalHandler} instead. This class adds `showModal` on top of the
|
|
726
|
-
* reply members.
|
|
727
|
-
*
|
|
728
|
-
* @typeParam Event - The repliable interaction type this handler processes
|
|
729
|
-
*/
|
|
730
|
-
var InteractionHandler = class extends RepliableHandler {
|
|
731
|
-
constructor(event, core, dispatch) {
|
|
732
|
-
super(event, core, dispatch);
|
|
733
|
-
}
|
|
734
|
-
/**
|
|
735
|
-
* Open a modal. Must be the initial response to this interaction. The modal kind rejects this call at
|
|
736
|
-
* compile time (Discord forbids a modal in response to a modal).
|
|
737
|
-
*/
|
|
738
|
-
showModal(modal) {
|
|
739
|
-
return this.sender.showModal(modal);
|
|
740
|
-
}
|
|
741
|
-
};
|
|
742
|
-
|
|
743
|
-
//#endregion
|
|
744
|
-
//#region src/handlers/defaults/UnhandledRepliable.ts
|
|
745
|
-
var UnhandledRepliable = class extends InteractionHandler {
|
|
746
|
-
async execute() {
|
|
747
|
-
await this.reply("Feature not implemented yet.");
|
|
748
|
-
}
|
|
749
|
-
};
|
|
750
|
-
|
|
751
|
-
//#endregion
|
|
752
|
-
//#region src/dispatch/resolve.ts
|
|
753
|
-
const COMPONENT_KIND = {
|
|
754
|
-
button: "button",
|
|
755
|
-
stringSelect: "stringMenu",
|
|
756
|
-
userSelect: "userMenu",
|
|
757
|
-
roleSelect: "roleMenu",
|
|
758
|
-
channelSelect: "channelMenu",
|
|
759
|
-
mentionableSelect: "mentionableMenu",
|
|
760
|
-
modal: "modal"
|
|
761
|
-
};
|
|
762
|
-
function commandKind(type) {
|
|
763
|
-
switch (type) {
|
|
764
|
-
case discord_api_types_v10.ApplicationCommandType.ChatInput: return "slash";
|
|
765
|
-
case discord_api_types_v10.ApplicationCommandType.User: return "userContextMenu";
|
|
766
|
-
case discord_api_types_v10.ApplicationCommandType.Message: return "messageContextMenu";
|
|
767
|
-
default: return null;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
function componentMapKey(type) {
|
|
771
|
-
switch (type) {
|
|
772
|
-
case discord_api_types_v10.ComponentType.Button: return "button";
|
|
773
|
-
case discord_api_types_v10.ComponentType.StringSelect: return "stringSelect";
|
|
774
|
-
case discord_api_types_v10.ComponentType.UserSelect: return "userSelect";
|
|
775
|
-
case discord_api_types_v10.ComponentType.RoleSelect: return "roleSelect";
|
|
776
|
-
case discord_api_types_v10.ComponentType.ChannelSelect: return "channelSelect";
|
|
777
|
-
case discord_api_types_v10.ComponentType.MentionableSelect: return "mentionableSelect";
|
|
778
|
-
default: return null;
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
function namedExport(routeId, row) {
|
|
782
|
-
return async () => {
|
|
783
|
-
let moduleExports;
|
|
784
|
-
try {
|
|
785
|
-
moduleExports = await row.load();
|
|
786
|
-
} catch (caught) {
|
|
787
|
-
throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.RouteModuleLoadFailed, [routeId, row.from], { cause: caught });
|
|
788
|
-
}
|
|
789
|
-
if (!Object.hasOwn(moduleExports, row.exportName)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionRouteExportMissing, [
|
|
790
|
-
routeId,
|
|
791
|
-
row.exportName,
|
|
792
|
-
row.from
|
|
793
|
-
]);
|
|
794
|
-
return moduleExports[row.exportName];
|
|
795
|
-
};
|
|
796
|
-
}
|
|
797
|
-
function describeRow(row) {
|
|
798
|
-
return `${row.exportName} (${row.from})`;
|
|
799
|
-
}
|
|
800
|
-
/**
|
|
801
|
-
* Builds the per-kind lookup maps.
|
|
802
|
-
*
|
|
803
|
-
* @throws A **SeedcordError** when two rows resolve to the same route.
|
|
804
|
-
*/
|
|
805
|
-
function buildRouteMaps(manifest) {
|
|
806
|
-
const maps = {
|
|
807
|
-
slash: /* @__PURE__ */ new Map(),
|
|
808
|
-
userContextMenu: /* @__PURE__ */ new Map(),
|
|
809
|
-
messageContextMenu: /* @__PURE__ */ new Map(),
|
|
810
|
-
autocomplete: /* @__PURE__ */ new Map(),
|
|
811
|
-
components: {
|
|
812
|
-
button: /* @__PURE__ */ new Map(),
|
|
813
|
-
stringSelect: /* @__PURE__ */ new Map(),
|
|
814
|
-
userSelect: /* @__PURE__ */ new Map(),
|
|
815
|
-
roleSelect: /* @__PURE__ */ new Map(),
|
|
816
|
-
channelSelect: /* @__PURE__ */ new Map(),
|
|
817
|
-
mentionableSelect: /* @__PURE__ */ new Map(),
|
|
818
|
-
modal: /* @__PURE__ */ new Map()
|
|
819
|
-
}
|
|
820
|
-
};
|
|
821
|
-
const owners = /* @__PURE__ */ new Map();
|
|
822
|
-
function set(map, kind, key, row) {
|
|
823
|
-
const routeId = `${kind}:${key}`;
|
|
824
|
-
const owner = owners.get(routeId);
|
|
825
|
-
if (owner) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionDuplicateRoute, [
|
|
826
|
-
routeId,
|
|
827
|
-
describeRow(owner),
|
|
828
|
-
describeRow(row)
|
|
829
|
-
]);
|
|
830
|
-
owners.set(routeId, row);
|
|
831
|
-
map.set(key, {
|
|
832
|
-
kind,
|
|
833
|
-
routeId,
|
|
834
|
-
load: namedExport(routeId, row)
|
|
835
|
-
});
|
|
836
|
-
}
|
|
837
|
-
for (const row of manifest.commandRoutes) {
|
|
838
|
-
const kind = commandKind(row.type);
|
|
839
|
-
if (kind) set(maps[kind], kind, row.name, row);
|
|
840
|
-
}
|
|
841
|
-
for (const row of manifest.autocompleteRoutes) set(maps.autocomplete, "autocomplete", row.name, row);
|
|
842
|
-
for (const row of manifest.componentRoutes) set(maps.components[row.kind], COMPONENT_KIND[row.kind], row.prefix, row);
|
|
843
|
-
return maps;
|
|
844
|
-
}
|
|
845
|
-
function unhandled(kind, attemptedKey) {
|
|
846
|
-
return {
|
|
847
|
-
kind,
|
|
848
|
-
routeId: null,
|
|
849
|
-
attemptedKey,
|
|
850
|
-
load: () => Promise.resolve(kind === "autocomplete" ? UnhandledAutocomplete : UnhandledRepliable)
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
/**
|
|
854
|
-
* Matches a verified non-PING interaction to its manifest row. A known kind with no row resolves to the
|
|
855
|
-
* unhandled default, whose handler replies "Feature not implemented yet." (empty choices on autocomplete).
|
|
856
|
-
* Null is an unrecognized payload shape, which the engine acks with a 202 without dispatching.
|
|
857
|
-
* Components and modals route by the stable customId prefix, so a wire whose layout hash drifted still
|
|
858
|
-
* routes to its handler, where decode refuses with `StaleCustomId`.
|
|
859
|
-
*/
|
|
860
|
-
function resolve(maps, interaction) {
|
|
861
|
-
switch (interaction.type) {
|
|
862
|
-
case discord_api_types_v10.InteractionType.ApplicationCommand: {
|
|
863
|
-
if (interaction.data.type === discord_api_types_v10.ApplicationCommandType.ChatInput) {
|
|
864
|
-
const route = slashRouteOf(interaction.data);
|
|
865
|
-
return maps.slash.get(route) ?? unhandled("slash", route);
|
|
866
|
-
}
|
|
867
|
-
const kind = commandKind(interaction.data.type);
|
|
868
|
-
return kind ? maps[kind].get(interaction.data.name) ?? unhandled(kind, interaction.data.name) : null;
|
|
869
|
-
}
|
|
870
|
-
case discord_api_types_v10.InteractionType.ApplicationCommandAutocomplete: {
|
|
871
|
-
const route = slashRouteOf(interaction.data);
|
|
872
|
-
return maps.autocomplete.get(route) ?? unhandled("autocomplete", route);
|
|
873
|
-
}
|
|
874
|
-
case discord_api_types_v10.InteractionType.MessageComponent: {
|
|
875
|
-
const key = componentMapKey(interaction.data.component_type);
|
|
876
|
-
if (!key) return null;
|
|
877
|
-
const prefix = (0, _seedcord_core_internal.prefixOf)(interaction.data.custom_id);
|
|
878
|
-
return maps.components[key].get(prefix) ?? unhandled(COMPONENT_KIND[key], prefix);
|
|
879
|
-
}
|
|
880
|
-
case discord_api_types_v10.InteractionType.ModalSubmit: {
|
|
881
|
-
const prefix = (0, _seedcord_core_internal.prefixOf)(interaction.data.custom_id);
|
|
882
|
-
return maps.components.modal.get(prefix) ?? unhandled("modal", prefix);
|
|
883
|
-
}
|
|
884
|
-
default: return null;
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
//#endregion
|
|
889
|
-
//#region src/receiver/Ed25519Verifier.ts
|
|
890
|
-
const PUBLIC_KEY_BYTES = 32;
|
|
891
|
-
const SIGNATURE_BYTES = 64;
|
|
892
|
-
const HEX_RADIX = 16;
|
|
893
|
-
const HEX_PATTERN = /^[0-9a-f]+$/i;
|
|
894
|
-
function hexToBytes(hex, byteLength) {
|
|
895
|
-
if (hex.length !== byteLength * 2 || !HEX_PATTERN.test(hex)) return null;
|
|
896
|
-
const bytes = new Uint8Array(byteLength);
|
|
897
|
-
for (let index = 0; index < byteLength; index++) bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), HEX_RADIX);
|
|
898
|
-
return bytes;
|
|
899
|
-
}
|
|
900
|
-
const encoder = new TextEncoder();
|
|
901
|
-
var Ed25519Verifier = class {
|
|
902
|
-
key;
|
|
903
|
-
constructor(publicKeyHex) {
|
|
904
|
-
const publicKey = hexToBytes(publicKeyHex, PUBLIC_KEY_BYTES);
|
|
905
|
-
if (!publicKey) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigIncorrectPublicKey);
|
|
906
|
-
this.key = crypto.subtle.importKey("raw", publicKey, "Ed25519", false, ["verify"]);
|
|
907
|
-
}
|
|
908
|
-
async verify(signatureHex, timestamp, body) {
|
|
909
|
-
const signature = hexToBytes(signatureHex, SIGNATURE_BYTES);
|
|
910
|
-
if (!signature) return false;
|
|
911
|
-
const timestampBytes = encoder.encode(timestamp);
|
|
912
|
-
const message = new Uint8Array(timestampBytes.length + body.length);
|
|
913
|
-
message.set(timestampBytes, 0);
|
|
914
|
-
message.set(body, timestampBytes.length);
|
|
915
|
-
return crypto.subtle.verify("Ed25519", await this.key, signature, message);
|
|
916
|
-
}
|
|
917
|
-
};
|
|
918
|
-
|
|
919
|
-
//#endregion
|
|
920
|
-
//#region src/receiver/ReplayGuard.ts
|
|
921
|
-
const REPLAY_WINDOW_MS = 3e5;
|
|
922
|
-
const SWEEP_GAP_MS = 6e4;
|
|
923
|
-
const MS_PER_SECOND = 1e3;
|
|
924
|
-
const TIMESTAMP_PATTERN = /^\d+$/;
|
|
925
|
-
var ReplayGuard = class {
|
|
926
|
-
seen = /* @__PURE__ */ new Map();
|
|
927
|
-
lastSweep = Date.now();
|
|
928
|
-
fresh(timestampHeader) {
|
|
929
|
-
if (!TIMESTAMP_PATTERN.test(timestampHeader)) return null;
|
|
930
|
-
const timestampMs = Number(timestampHeader) * MS_PER_SECOND;
|
|
931
|
-
return Math.abs(Date.now() - timestampMs) <= REPLAY_WINDOW_MS ? timestampMs : null;
|
|
932
|
-
}
|
|
933
|
-
unseen(signatureHex, timestampMs) {
|
|
934
|
-
const now = Date.now();
|
|
935
|
-
this.maybeSweep(now);
|
|
936
|
-
const rejectUntil = this.seen.get(signatureHex);
|
|
937
|
-
if (rejectUntil !== void 0 && rejectUntil >= now) return false;
|
|
938
|
-
this.seen.set(signatureHex, timestampMs + REPLAY_WINDOW_MS);
|
|
939
|
-
return true;
|
|
940
|
-
}
|
|
941
|
-
maybeSweep(now) {
|
|
942
|
-
if (now - this.lastSweep < SWEEP_GAP_MS) return;
|
|
943
|
-
this.lastSweep = now;
|
|
944
|
-
for (const [signature, rejectUntil] of this.seen) if (rejectUntil < now) this.seen.delete(signature);
|
|
945
|
-
}
|
|
946
|
-
};
|
|
947
|
-
|
|
948
|
-
//#endregion
|
|
949
|
-
//#region src/engine.ts
|
|
950
|
-
const SIGNATURE_HEADER = "x-signature-ed25519";
|
|
951
|
-
const TIMESTAMP_HEADER = "x-signature-timestamp";
|
|
952
|
-
const ACCEPTED = 202;
|
|
953
|
-
const BAD_REQUEST = 400;
|
|
954
|
-
const UNAUTHORIZED = 401;
|
|
955
|
-
const METHOD_NOT_ALLOWED = 405;
|
|
956
|
-
const PING = discord_api_types_v10.InteractionType.Ping;
|
|
957
|
-
const decoder = new TextDecoder();
|
|
958
|
-
function hasInteractionType(payload) {
|
|
959
|
-
return typeof payload === "object" && payload !== null && "type" in payload && typeof payload.type === "number";
|
|
960
|
-
}
|
|
961
|
-
function buildEngine(core, maps) {
|
|
962
|
-
if (!envapt.Envapter.has("DISCORD_PUBLIC_KEY")) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigMissingPublicKey);
|
|
963
|
-
const publicKey = envapt.Envapter.getRequired("DISCORD_PUBLIC_KEY", envapt.Converters.String);
|
|
964
|
-
const verifier = new Ed25519Verifier(publicKey);
|
|
965
|
-
const replays = new ReplayGuard();
|
|
966
|
-
const inFlight = /* @__PURE__ */ new Set();
|
|
967
|
-
const logger = new _seedcord_logger.Logger("Engine", { channel: "bot" });
|
|
968
|
-
async function dispatchMatched(match, payload, ctx) {
|
|
969
|
-
const start = await dispatchInteraction({
|
|
970
|
-
match,
|
|
971
|
-
payload,
|
|
972
|
-
core
|
|
973
|
-
});
|
|
974
|
-
if (!start) return;
|
|
975
|
-
const work = start().catch(rootFault);
|
|
976
|
-
if (ctx) ctx.waitUntil(work);
|
|
977
|
-
else {
|
|
978
|
-
inFlight.add(work);
|
|
979
|
-
work.finally(() => inFlight.delete(work));
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
function rootFault(caught) {
|
|
983
|
-
const error = (0, _seedcord_core_internal.asError)(caught);
|
|
984
|
-
logger.error("dispatch failed past the boundary", error);
|
|
985
|
-
core.bus[_seedcord_core_internal.PublishDefault]("unhandledInteractionError", { error });
|
|
986
|
-
}
|
|
987
|
-
const handle = async (request, ctx) => {
|
|
988
|
-
const [response, outcome] = await respond(request, ctx);
|
|
989
|
-
logger.debug(`${request.method} ${outcome} ${_seedcord_logger.paint.amber(String(response.status))}`);
|
|
990
|
-
return response;
|
|
991
|
-
};
|
|
992
|
-
const respond = async (request, ctx) => {
|
|
993
|
-
if (request.method !== "POST") return [new Response(null, {
|
|
994
|
-
status: METHOD_NOT_ALLOWED,
|
|
995
|
-
headers: { allow: "POST" }
|
|
996
|
-
}), "not-post"];
|
|
997
|
-
const signature = request.headers.get(SIGNATURE_HEADER);
|
|
998
|
-
const timestamp = request.headers.get(TIMESTAMP_HEADER);
|
|
999
|
-
if (signature === null || timestamp === null) return [new Response(null, { status: UNAUTHORIZED }), "unsigned"];
|
|
1000
|
-
const timestampMs = replays.fresh(timestamp);
|
|
1001
|
-
if (timestampMs === null) return [new Response(null, { status: UNAUTHORIZED }), "stale"];
|
|
1002
|
-
const body = new Uint8Array(await request.arrayBuffer());
|
|
1003
|
-
if (!await verifier.verify(signature, timestamp, body)) return [new Response(null, { status: UNAUTHORIZED }), "bad-signature"];
|
|
1004
|
-
let payload;
|
|
1005
|
-
try {
|
|
1006
|
-
payload = JSON.parse(decoder.decode(body));
|
|
1007
|
-
} catch {
|
|
1008
|
-
return [new Response(null, { status: BAD_REQUEST }), "bad-body"];
|
|
1009
|
-
}
|
|
1010
|
-
if (!hasInteractionType(payload)) return [new Response(null, { status: BAD_REQUEST }), "bad-body"];
|
|
1011
|
-
if (payload.type === PING) return [Response.json({ type: discord_api_types_v10.InteractionResponseType.Pong }), "ping"];
|
|
1012
|
-
if (!replays.unseen(signature, timestampMs)) return [new Response(null, { status: UNAUTHORIZED }), "replay"];
|
|
1013
|
-
try {
|
|
1014
|
-
core.bus[_seedcord_core_internal.PublishDefault]("anyInteraction", { interaction: payload });
|
|
1015
|
-
const match = resolve(maps, payload);
|
|
1016
|
-
if (match) await dispatchMatched(match, payload, ctx);
|
|
1017
|
-
} catch (caught) {
|
|
1018
|
-
rootFault(caught);
|
|
1019
|
-
}
|
|
1020
|
-
return [new Response(null, { status: ACCEPTED }), "dispatched"];
|
|
1021
|
-
};
|
|
1022
|
-
return {
|
|
1023
|
-
handle,
|
|
1024
|
-
inFlight
|
|
1025
|
-
};
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
//#endregion
|
|
1029
|
-
//#region src/createSeedcord.ts
|
|
1030
|
-
/**
|
|
1031
|
-
* Builds the HTTP-interactions engine, a `(request, ctx?) => Promise<Response>` handler.
|
|
1032
|
-
*
|
|
1033
|
-
* The handler verifies the Ed25519 signature over the raw request bytes, rejects stale or replayed
|
|
1034
|
-
* requests, answers a PING with an in-body PONG, and acks every other interaction with an empty 202.
|
|
1035
|
-
* The engine dispatches a matched interaction through the manifest before the 202 goes out. Gates run
|
|
1036
|
-
* first and the sender posts any refusal over the REST callback. `execute()` continues past the 202 under
|
|
1037
|
-
* `ctx.waitUntil` when the caller passes one (the edge entry), and an engine-held in-flight set tracks
|
|
1038
|
-
* it otherwise (node paths). It reads only the method, headers, and body. Mount it at any path.
|
|
1039
|
-
*
|
|
1040
|
-
* Reads `DISCORD_PUBLIC_KEY` and `DISCORD_BOT_TOKEN` from the environment through envapt and throws a
|
|
1041
|
-
* `SeedcordError` when either is missing or malformed.
|
|
1042
|
-
*/
|
|
1043
|
-
function createSeedcord(config, manifest) {
|
|
1044
|
-
const token = (0, _seedcord_errors_internal.validateDiscordToken)(envapt.Envapter.get("DISCORD_BOT_TOKEN"));
|
|
1045
|
-
const core = createCore(config, token);
|
|
1046
|
-
core.bus.registerDefaults();
|
|
1047
|
-
registerSubscribers(core.bus, manifest);
|
|
1048
|
-
return buildEngine(core, buildRouteMaps(manifest)).handle;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
//#endregion
|
|
1052
|
-
//#region src/applicationId.ts
|
|
1053
|
-
async function fetchApplicationId(rest) {
|
|
1054
|
-
try {
|
|
1055
|
-
return (await rest.get(discord_api_types_v10.Routes.currentApplication())).id;
|
|
1056
|
-
} catch (error) {
|
|
1057
|
-
throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CoreApplicationUnavailable, { cause: error });
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
//#endregion
|
|
1062
|
-
//#region src/emojis/EmojiInjector.ts
|
|
1063
|
-
var Emoji = class {
|
|
1064
|
-
name;
|
|
1065
|
-
id;
|
|
1066
|
-
animated;
|
|
1067
|
-
constructor(name, id, animated = false) {
|
|
1068
|
-
this.name = name;
|
|
1069
|
-
this.id = id;
|
|
1070
|
-
this.animated = animated;
|
|
1071
|
-
}
|
|
1072
|
-
toString() {
|
|
1073
|
-
return `<${this.animated ? "a" : ""}:${this.name}:${this.id}>`;
|
|
1074
|
-
}
|
|
1075
|
-
};
|
|
1076
|
-
const emojiStorage = (0, _seedcord_core_internal.accessorStore)();
|
|
1077
|
-
function reasonOf(error) {
|
|
1078
|
-
return Error.isError(error) ? error.message : String(error);
|
|
1079
|
-
}
|
|
1080
|
-
function byName(list) {
|
|
1081
|
-
const map = /* @__PURE__ */ new Map();
|
|
1082
|
-
for (const entry of list) if (entry.name) map.set(entry.name, entry);
|
|
1083
|
-
return map;
|
|
1084
|
-
}
|
|
1085
|
-
/**
|
|
1086
|
-
* The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
|
|
1087
|
-
* A read before that throws.
|
|
1088
|
-
*/
|
|
1089
|
-
const Emojis = (0, _seedcord_core_internal.guardedAccessor)("Emojis", emojiStorage);
|
|
1090
|
-
var EmojiInjector = class {
|
|
1091
|
-
core;
|
|
1092
|
-
applicationId;
|
|
1093
|
-
logger = new _seedcord_logger.Logger("Emojis", { channel: "bot" });
|
|
1094
|
-
constructor(core, applicationId = () => fetchApplicationId(core.rest)) {
|
|
1095
|
-
this.core = core;
|
|
1096
|
-
this.applicationId = applicationId;
|
|
1097
|
-
}
|
|
1098
|
-
async init() {
|
|
1099
|
-
(0, _seedcord_core_internal.clearStore)(emojiStorage);
|
|
1100
|
-
const configured = this.core.config.bot.emojis;
|
|
1101
|
-
if (!configured || Object.keys(configured).length === 0) {
|
|
1102
|
-
this.logger.debug("No emojis configured, skipping emoji injection.");
|
|
1103
|
-
return;
|
|
1104
|
-
}
|
|
1105
|
-
const failures = [];
|
|
1106
|
-
const guilds = /* @__PURE__ */ new Map();
|
|
1107
|
-
let application;
|
|
1108
|
-
for (const [key, value] of Object.entries(configured)) if ((0, _seedcord_core_internal.isEmojiTuple)(value)) await this.resolveTuple(key, value, guilds, failures);
|
|
1109
|
-
else if (typeof value === "string") {
|
|
1110
|
-
application ??= await this.applicationEmojis(failures);
|
|
1111
|
-
this.resolve(key, value, application, failures, "the application");
|
|
1112
|
-
} else failures.push(` - "${key}" has an invalid value (expected a name or [name, guildId])`);
|
|
1113
|
-
if (failures.length > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigEmojiUnresolved, [failures.length, failures.join("\n")]);
|
|
1114
|
-
this.logger.utils.summary("Loaded emojis", { emojis: Object.keys(emojiStorage).length });
|
|
1115
|
-
}
|
|
1116
|
-
async applicationEmojis(failures) {
|
|
1117
|
-
try {
|
|
1118
|
-
const appId = await this.applicationId();
|
|
1119
|
-
return byName((await this.core.rest.get(discord_api_types_v10.Routes.applicationEmojis(appId))).items);
|
|
1120
|
-
} catch (error) {
|
|
1121
|
-
failures.push(` - the application emojis could not be read (${reasonOf(error)})`);
|
|
1122
|
-
return /* @__PURE__ */ new Map();
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
async resolveTuple(key, [name, guildId], guilds, failures) {
|
|
1126
|
-
let guild = guilds.get(guildId);
|
|
1127
|
-
if (!guild) {
|
|
1128
|
-
try {
|
|
1129
|
-
guild = byName(await this.core.rest.get(discord_api_types_v10.Routes.guildEmojis(guildId)));
|
|
1130
|
-
} catch (error) {
|
|
1131
|
-
failures.push(` - "${name}" for "${key}" targets guild ${guildId}, which could not be read (${reasonOf(error)})`);
|
|
1132
|
-
return;
|
|
1133
|
-
}
|
|
1134
|
-
guilds.set(guildId, guild);
|
|
1135
|
-
}
|
|
1136
|
-
this.resolve(key, name, guild, failures, `guild ${guildId}`);
|
|
1137
|
-
}
|
|
1138
|
-
resolve(key, name, source, failures, where) {
|
|
1139
|
-
const found = source.get(name);
|
|
1140
|
-
if (!found?.id) {
|
|
1141
|
-
failures.push(` - "${name}" for "${key}" was not found in ${where}`);
|
|
1142
|
-
return;
|
|
1143
|
-
}
|
|
1144
|
-
emojiStorage[key] = new Emoji(name, found.id, found.animated ?? false);
|
|
1145
|
-
}
|
|
1146
|
-
};
|
|
1147
|
-
|
|
1148
|
-
//#endregion
|
|
1149
|
-
//#region src/handlers/interaction/SlashHandler.ts
|
|
1150
|
-
/**
|
|
1151
|
-
* Base class for a chat-input (slash) command handler on the HTTP transport.
|
|
1152
|
-
*
|
|
1153
|
-
* Pass the route from the generated registry as the generic, then read `this.options` for a single
|
|
1154
|
-
* command or `this.match` for several. Command authoring stays plain discord.js, `seedcord codegen`
|
|
1155
|
-
* reads its `toJSON()` to type these options.
|
|
1156
|
-
*
|
|
1157
|
-
* @typeParam Route - One or more route keys from {@link SlashOptionRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.
|
|
1158
|
-
*/
|
|
1159
|
-
var SlashHandler = class extends InteractionHandler {
|
|
1160
|
-
resolver;
|
|
1161
|
-
/**
|
|
1162
|
-
* The typed options for this command's route. Required options drop the null, choices narrow to their
|
|
1163
|
-
* literal union, and only the getters for kinds this command actually uses appear. Rich kinds resolve
|
|
1164
|
-
* from the interaction's `resolved` data.
|
|
1165
|
-
*/
|
|
1166
|
-
get options() {
|
|
1167
|
-
this.resolver ??= new HttpSlashOptions(this.event.data);
|
|
1168
|
-
return this.resolver;
|
|
1169
|
-
}
|
|
1170
|
-
/**
|
|
1171
|
-
* Run the arm for whichever command fired. Use this only when the handler is registered for several
|
|
1172
|
-
* routes.
|
|
1173
|
-
*
|
|
1174
|
-
* Provide one arm per registered route, keyed by its route string, and each arm receives that route's
|
|
1175
|
-
* own narrowed options. The arms must cover every route in the generic, a missing route or an unknown
|
|
1176
|
-
* key is a compile error.
|
|
1177
|
-
*
|
|
1178
|
-
* @param arms - One handler per registered route, keyed by route string.
|
|
1179
|
-
* @returns The result of the arm that ran.
|
|
1180
|
-
*/
|
|
1181
|
-
async match(arms) {
|
|
1182
|
-
const route = slashRouteOf(this.event.data);
|
|
1183
|
-
const arm = Object.hasOwn(arms, route) ? arms[route] : void 0;
|
|
1184
|
-
if (!arm) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.SlashMatchArmMissing, [route]);
|
|
1185
|
-
return await arm(this.options);
|
|
1186
|
-
}
|
|
1187
|
-
};
|
|
1188
|
-
|
|
1189
|
-
//#endregion
|
|
1190
|
-
//#region src/handlers/interaction/ContextMenuHandler.ts
|
|
1191
|
-
/**
|
|
1192
|
-
* Base class for a context-menu command handler on the HTTP transport (right-click a user or a message).
|
|
1193
|
-
*
|
|
1194
|
-
* Pass the kind from {@link ApplicationCommandType} as the generic. Read the right-clicked entity from
|
|
1195
|
-
* `this.target`, an `APIUser` for a user menu and an `APIMessage` for a message menu. Context menus carry
|
|
1196
|
-
* no options, so a handler registered for several names reads `this.target` uniformly with no per-name
|
|
1197
|
-
* branch.
|
|
1198
|
-
*
|
|
1199
|
-
* @typeParam Kind - `ApplicationCommandType.User` or `ApplicationCommandType.Message`.
|
|
1200
|
-
*/
|
|
1201
|
-
var ContextMenuHandler = class extends InteractionHandler {
|
|
1202
|
-
get target() {
|
|
1203
|
-
const data = this.event.data;
|
|
1204
|
-
return data.type === discord_api_types_v10.ApplicationCommandType.User ? data.resolved.users[data.target_id] : data.resolved.messages[data.target_id];
|
|
1205
|
-
}
|
|
1206
|
-
/**
|
|
1207
|
-
* The right-clicked user's guild member, resolved only on user menus fired in a guild. Reading it on a
|
|
1208
|
-
* message menu is a compile error.
|
|
1209
|
-
*/
|
|
1210
|
-
get targetMember() {
|
|
1211
|
-
const data = this.event.data;
|
|
1212
|
-
return data.type === discord_api_types_v10.ApplicationCommandType.User ? data.resolved.members?.[data.target_id] ?? null : null;
|
|
1213
|
-
}
|
|
1214
|
-
};
|
|
1215
|
-
|
|
1216
|
-
//#endregion
|
|
1217
|
-
//#region src/handlers/interaction/components/ComponentHandler.ts
|
|
1218
|
-
/**
|
|
1219
|
-
* Shared base the customId-routed component handlers extend on the HTTP transport.
|
|
1220
|
-
*
|
|
1221
|
-
* Not a public entry point. Extend {@link ButtonHandler}, {@link SelectMenuHandler}, or {@link ModalHandler}
|
|
1222
|
-
* instead. This class defines the customId decode, the route matching, and the source-message verbs
|
|
1223
|
-
* `update` and `deferUpdate`.
|
|
1224
|
-
*
|
|
1225
|
-
* @typeParam Event - The component or modal interaction type this handler processes
|
|
1226
|
-
* @typeParam Defs - The customId route definitions registered on the concrete handler
|
|
1227
|
-
*/
|
|
1228
|
-
var ComponentHandler = class extends InteractionHandler {
|
|
1229
|
-
/** Rewrite the source message this component interaction came from. */
|
|
1230
|
-
update(response) {
|
|
1231
|
-
return this.sender.update(response);
|
|
1232
|
-
}
|
|
1233
|
-
/** Acknowledge the component without changing the source message. */
|
|
1234
|
-
deferUpdate() {
|
|
1235
|
-
return this.sender.deferUpdate();
|
|
1236
|
-
}
|
|
1237
|
-
decoded;
|
|
1238
|
-
get route() {
|
|
1239
|
-
this.decoded ??= (0, _seedcord_core_internal.decodeComponentRoute)(this.constructor, this.event.data.custom_id);
|
|
1240
|
-
return this.decoded;
|
|
1241
|
-
}
|
|
1242
|
-
/**
|
|
1243
|
-
* The decoded params of the single route this handler is registered for.
|
|
1244
|
-
*
|
|
1245
|
-
* Reading this decodes `this.event.data.custom_id` once (cached after the first read) and throws
|
|
1246
|
-
* `StaleCustomId` or `InvalidCustomId` when the wire no longer matches the current shape, which the
|
|
1247
|
-
* dispatcher boundary turns into a reply. On a handler registered for several routes this is
|
|
1248
|
-
* `never`, so use {@link match} instead.
|
|
1249
|
-
*/
|
|
1250
|
-
get params() {
|
|
1251
|
-
return this.route.params;
|
|
1252
|
-
}
|
|
1253
|
-
/**
|
|
1254
|
-
* Run the arm for whichever route the component was minted from. Use this only when the handler is
|
|
1255
|
-
* registered for several routes. A single-route handler reads `this.params` directly. On a multi-route
|
|
1256
|
-
* handler `this.params` is `never`, so match is the only way to read the decoded params.
|
|
1257
|
-
*
|
|
1258
|
-
* Provide one arm per registered route, keyed by its prefix, and each arm receives that route's own
|
|
1259
|
-
* decoded params. The arms cover every registered route prefix, checked at compile time, and a prefix
|
|
1260
|
-
* unmatched at runtime throws `CustomIdMatchArmMissing`. Decoding runs before any arm, so a stale or
|
|
1261
|
-
* corrupt wire throws before an arm body executes.
|
|
1262
|
-
*
|
|
1263
|
-
* @param arms - One callback per registered route, keyed by prefix.
|
|
1264
|
-
* @returns The result of the arm that ran.
|
|
1265
|
-
*
|
|
1266
|
-
* @example
|
|
1267
|
-
* ```ts
|
|
1268
|
-
* \@ButtonRoute(Approve, Reject)
|
|
1269
|
-
* class ReviewButtons extends ButtonHandler<[typeof Approve, typeof Reject]> {
|
|
1270
|
-
* async execute() {
|
|
1271
|
-
* await this.match({
|
|
1272
|
-
* approve: ({ userId }) => this.reply(`approved <@${userId}>`),
|
|
1273
|
-
* reject: ({ userId }) => this.reply(`rejected <@${userId}>`)
|
|
1274
|
-
* });
|
|
1275
|
-
* }
|
|
1276
|
-
* }
|
|
1277
|
-
* ```
|
|
1278
|
-
*/
|
|
1279
|
-
async match(arms) {
|
|
1280
|
-
const { prefix, params } = this.route;
|
|
1281
|
-
const arm = Object.hasOwn(arms, prefix) ? arms[prefix] : void 0;
|
|
1282
|
-
if (!arm) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CustomIdMatchArmMissing, [prefix]);
|
|
1283
|
-
return await arm(params);
|
|
1284
|
-
}
|
|
1285
|
-
};
|
|
1286
|
-
|
|
1287
|
-
//#endregion
|
|
1288
|
-
//#region src/handlers/interaction/components/ButtonHandler.ts
|
|
1289
|
-
/**
|
|
1290
|
-
* Base class for a button interaction handler on the HTTP transport.
|
|
1291
|
-
*
|
|
1292
|
-
* Register the customId definitions this handler decodes with `@ButtonRoute`, list the same ones in the
|
|
1293
|
-
* generic, then read `this.params` for a single route or `this.match` for several. Reply through the
|
|
1294
|
-
* handler members, or rewrite the source message with `this.update`. Passing different definitions to the
|
|
1295
|
-
* decorator and the generic is a compile error.
|
|
1296
|
-
*
|
|
1297
|
-
* @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ApproveId]`.
|
|
1298
|
-
*/
|
|
1299
|
-
var ButtonHandler = class extends ComponentHandler {};
|
|
1300
|
-
|
|
1301
|
-
//#endregion
|
|
1302
|
-
//#region src/handlers/interaction/components/ModalHandler.ts
|
|
1303
|
-
/**
|
|
1304
|
-
* Base class for a modal submit handler on the HTTP transport.
|
|
1305
|
-
*
|
|
1306
|
-
* Register the customId definitions this handler decodes with `@ModalRoute` and list the same ones in the
|
|
1307
|
-
* generic. Read the submitted inputs off `this.event.data.components`. Reply through the handler members.
|
|
1308
|
-
* Discord forbids opening a modal in response to a modal, so `showModal` fails compilation on this kind.
|
|
1309
|
-
*
|
|
1310
|
-
* @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ConfigId]`.
|
|
1311
|
-
*/
|
|
1312
|
-
var ModalHandler = class extends ComponentHandler {
|
|
1313
|
-
/** Rewrite the message this modal was opened from. Throws when a command opened the modal. */
|
|
1314
|
-
update(response) {
|
|
1315
|
-
this.ensureSourceMessage("update");
|
|
1316
|
-
return super.update(response);
|
|
1317
|
-
}
|
|
1318
|
-
/** Acknowledge the submit without changing the source message. Throws when a command opened the modal. */
|
|
1319
|
-
deferUpdate() {
|
|
1320
|
-
this.ensureSourceMessage("deferUpdate");
|
|
1321
|
-
return super.deferUpdate();
|
|
1322
|
-
}
|
|
1323
|
-
ensureSourceMessage(method) {
|
|
1324
|
-
if (this.event.message) return;
|
|
1325
|
-
throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ReplyUpdateWithoutSource, [method, this.routeId]);
|
|
1326
|
-
}
|
|
1327
|
-
};
|
|
1328
|
-
|
|
1329
|
-
//#endregion
|
|
1330
|
-
//#region src/handlers/interaction/components/SelectMenuHandler.ts
|
|
1331
|
-
/**
|
|
1332
|
-
* Base class for a select menu handler on the HTTP transport.
|
|
1333
|
-
*
|
|
1334
|
-
* Pass the select kind first and the customId definitions second, the same order as `@SelectMenuRoute`,
|
|
1335
|
-
* so `this.event.data` and its `values`/`resolved` are narrowed to that kind. Read `this.params` for a
|
|
1336
|
-
* single route or `this.match` for several, and reply through the handler members or rewrite the source
|
|
1337
|
-
* message with `this.update`.
|
|
1338
|
-
*
|
|
1339
|
-
* @typeParam Kind - The select kind from {@link SelectMenuKind}, e.g. `SelectMenuKind.User`.
|
|
1340
|
-
* @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.
|
|
1341
|
-
*/
|
|
1342
|
-
var SelectMenuHandler = class extends ComponentHandler {};
|
|
1343
|
-
|
|
1344
|
-
//#endregion
|
|
1345
|
-
//#region src/subscribers/index.ts
|
|
1346
|
-
/**
|
|
1347
|
-
* Base class for an HTTP subscriber, binding `this.core` to the HTTP {@link Core}.
|
|
1348
|
-
*
|
|
1349
|
-
* @typeParam KeyOfSubscribers - The subscription key this handler receives
|
|
1350
|
-
*/
|
|
1351
|
-
var Subscriber = class extends _seedcord_core.Subscriber {};
|
|
1352
|
-
/**
|
|
1353
|
-
* Base class for an HTTP subscriber that delivers its event to a Discord webhook, binding
|
|
1354
|
-
* `this.core` to the HTTP {@link Core}.
|
|
1355
|
-
*
|
|
1356
|
-
* @typeParam KeyOfSubscribers - The subscription key this reporter receives
|
|
1357
|
-
*/
|
|
1358
|
-
var WebhookLog = class extends _seedcord_core.WebhookLog {};
|
|
1359
|
-
|
|
1360
|
-
//#endregion
|
|
1361
|
-
//#region src/pagination/Paginator.ts
|
|
1362
|
-
function contextOf(interaction, core) {
|
|
1363
|
-
return {
|
|
1364
|
-
interaction,
|
|
1365
|
-
user: interaction.member?.user ?? interaction.user,
|
|
1366
|
-
guildId: interaction.guild_id ?? null,
|
|
1367
|
-
core
|
|
1368
|
-
};
|
|
1369
|
-
}
|
|
1370
|
-
/**
|
|
1371
|
-
* A restart-proof paginator. Each nav button's customId encodes its full target page, so clicks are
|
|
1372
|
-
* idempotent and survive a restart. A persistent `@ButtonRoute` on `Handler` dispatches the clicks.
|
|
1373
|
-
*
|
|
1374
|
-
* @typeParam Item - The item type, inferred from the source.
|
|
1375
|
-
* @typeParam Prefix - The route prefix, inferred from `config.prefix`.
|
|
1376
|
-
*
|
|
1377
|
-
* @example
|
|
1378
|
-
* ```ts
|
|
1379
|
-
* export const Bans = new Paginator({
|
|
1380
|
-
* prefix: 'bans',
|
|
1381
|
-
* source: new ArraySource((ctx) => loadBans(ctx.guildId), { perPage: 10 }),
|
|
1382
|
-
* renderItem: (ban) => ban.user.username
|
|
1383
|
-
* });
|
|
1384
|
-
*
|
|
1385
|
-
* \@ButtonRoute(Bans.cursor)
|
|
1386
|
-
* export class BansNav extends Bans.Handler {}
|
|
1387
|
-
* ```
|
|
1388
|
-
*/
|
|
1389
|
-
var Paginator = class extends _seedcord_core.PaginatorBase {
|
|
1390
|
-
/** The nav handler base. Extend it with an empty body and decorate it, `@ButtonRoute(p.cursor)`. */
|
|
1391
|
-
Handler;
|
|
1392
|
-
constructor(config) {
|
|
1393
|
-
super(config);
|
|
1394
|
-
const loadPage = (ctx, n) => this.page(ctx, n);
|
|
1395
|
-
this.Handler = class Nav extends ButtonHandler {
|
|
1396
|
-
async execute() {
|
|
1397
|
-
await this.deferUpdate();
|
|
1398
|
-
const response = await loadPage(contextOf(this.event, this.core), this.params.page);
|
|
1399
|
-
await this.update(response);
|
|
1400
|
-
}
|
|
1401
|
-
};
|
|
1402
|
-
}
|
|
1403
|
-
/**
|
|
1404
|
-
* Render page 0 and send it through the handler's sender, which picks reply, edit, or followUp from its
|
|
1405
|
-
* ack state.
|
|
1406
|
-
*
|
|
1407
|
-
* @param handler - The handler starting the paginator, normally `this`.
|
|
1408
|
-
*/
|
|
1409
|
-
async start(handler) {
|
|
1410
|
-
const interaction = handler.getEvent();
|
|
1411
|
-
const response = await this.page(contextOf(interaction, handler.core), 0);
|
|
1412
|
-
return handler.getSender().send(response, { ephemeral: this.config.ephemeral ?? false });
|
|
1413
|
-
}
|
|
1414
|
-
};
|
|
1415
|
-
|
|
1416
|
-
//#endregion
|
|
1417
|
-
//#region src/pagination/sources.ts
|
|
1418
|
-
/**
|
|
1419
|
-
* A source for a bounded list you can load whole. It loads the full list on every click, then slices the
|
|
1420
|
-
* page, so the real total is known (a real last button, "Page X of Y"). Cache inside your loader if the
|
|
1421
|
-
* load is expensive.
|
|
1422
|
-
*
|
|
1423
|
-
* @typeParam Item - The item type, inferred from the loader's return.
|
|
1424
|
-
*/
|
|
1425
|
-
var ArraySource = class extends _seedcord_core.ArraySource {};
|
|
1426
|
-
/**
|
|
1427
|
-
* A source for a large or unknown-length set you fetch one page at a time (SQL LIMIT/OFFSET, a paged API).
|
|
1428
|
-
* The fetcher receives the page index and the page size and reports whether a next page exists. A cursor
|
|
1429
|
-
* source has no cheap total, so `totalPages` is undefined, the last button is omitted, and the indicator
|
|
1430
|
-
* reads "Page X".
|
|
1431
|
-
*
|
|
1432
|
-
* @typeParam Item - The item type, inferred from the fetcher's slice.
|
|
1433
|
-
*/
|
|
1434
|
-
var CursorSource = class extends _seedcord_core.CursorSource {};
|
|
1435
|
-
|
|
1436
|
-
//#endregion
|
|
1437
|
-
//#region src/gates/Gated.ts
|
|
1438
|
-
/**
|
|
1439
|
-
* Attaches gates to an HTTP interaction handler. The gates run before `execute`, and a gate refusing stops
|
|
1440
|
-
* the handler with the reply or drop the gate threw. Multiple gates are ANDed. A gate
|
|
1441
|
-
* that requires a context the handler does not provide (a button gate on a slash handler, a gate on an
|
|
1442
|
-
* autocomplete handler, or a `RequirePermissions([...], { in: 'guild' })` gate the http transport cannot
|
|
1443
|
-
* satisfy) is a compile error at this line, and the error names the gate and the handler kind. Place it
|
|
1444
|
-
* above the route decorator.
|
|
1445
|
-
*
|
|
1446
|
-
* Combine gates with {@link and} or {@link or} to build a single gate from multiple arms.
|
|
1447
|
-
*
|
|
1448
|
-
* @example
|
|
1449
|
-
* ```ts
|
|
1450
|
-
* import { PermissionFlagsBits } from 'discord-api-types/v10';
|
|
1451
|
-
*
|
|
1452
|
-
* \@Gated(GuildOnly(), RequirePermissions([PermissionFlagsBits.ManageMessages]))
|
|
1453
|
-
* \@SlashRoute('purge')
|
|
1454
|
-
* class Purge extends SlashHandler<'purge'> {
|
|
1455
|
-
* async execute() {
|
|
1456
|
-
* // both gates passed, so this runs in a guild with a caller who can moderate this channel
|
|
1457
|
-
* }
|
|
1458
|
-
* }
|
|
1459
|
-
* ```
|
|
1460
|
-
*
|
|
1461
|
-
* @typeParam Gates - The non-empty tuple of gates to run, in order, against the handler's context.
|
|
1462
|
-
* @typeParam TCtor - The decorated handler's constructor, checked against every gate's required context.
|
|
1463
|
-
* @param gates - One or more gates to attach, each ANDed so the handler runs only when all pass.
|
|
1464
|
-
* @param ctor - The handler class being decorated, rejected at compile time when a gate requires a context it lacks.
|
|
1465
|
-
*
|
|
1466
|
-
* @see {@link defineGate}
|
|
1467
|
-
* @see {@link defineEffectGate}
|
|
1468
|
-
* @decorator
|
|
1469
|
-
*/
|
|
1470
|
-
function Gated(...gates) {
|
|
1471
|
-
return function(ctor) {
|
|
1472
|
-
const existing = Reflect.getMetadata(_seedcord_core_internal.GatedMetadataKey, ctor) ?? [];
|
|
1473
|
-
Reflect.defineMetadata(_seedcord_core_internal.GatedMetadataKey, [...existing, ...gates], ctor);
|
|
1474
|
-
};
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
//#endregion
|
|
1478
|
-
//#region src/version.ts
|
|
1479
|
-
const version = "0.2.3";
|
|
1480
|
-
|
|
1481
|
-
//#endregion
|
|
1482
|
-
Object.defineProperty(exports, 'ArraySource', {
|
|
1483
|
-
enumerable: true,
|
|
1484
|
-
get: function () {
|
|
1485
|
-
return ArraySource;
|
|
1486
|
-
}
|
|
1487
|
-
});
|
|
1488
|
-
Object.defineProperty(exports, 'AutocompleteHandler', {
|
|
1489
|
-
enumerable: true,
|
|
1490
|
-
get: function () {
|
|
1491
|
-
return AutocompleteHandler;
|
|
1492
|
-
}
|
|
1493
|
-
});
|
|
1494
|
-
Object.defineProperty(exports, 'ButtonHandler', {
|
|
1495
|
-
enumerable: true,
|
|
1496
|
-
get: function () {
|
|
1497
|
-
return ButtonHandler;
|
|
1498
|
-
}
|
|
1499
|
-
});
|
|
1500
|
-
Object.defineProperty(exports, 'ComponentHandler', {
|
|
1501
|
-
enumerable: true,
|
|
1502
|
-
get: function () {
|
|
1503
|
-
return ComponentHandler;
|
|
1504
|
-
}
|
|
1505
|
-
});
|
|
1506
|
-
Object.defineProperty(exports, 'ContextMenuHandler', {
|
|
1507
|
-
enumerable: true,
|
|
1508
|
-
get: function () {
|
|
1509
|
-
return ContextMenuHandler;
|
|
1510
|
-
}
|
|
1511
|
-
});
|
|
1512
|
-
Object.defineProperty(exports, 'CursorSource', {
|
|
1513
|
-
enumerable: true,
|
|
1514
|
-
get: function () {
|
|
1515
|
-
return CursorSource;
|
|
1516
|
-
}
|
|
1517
|
-
});
|
|
1518
|
-
Object.defineProperty(exports, 'EmojiInjector', {
|
|
1519
|
-
enumerable: true,
|
|
1520
|
-
get: function () {
|
|
1521
|
-
return EmojiInjector;
|
|
1522
|
-
}
|
|
1523
|
-
});
|
|
1524
|
-
Object.defineProperty(exports, 'Emojis', {
|
|
1525
|
-
enumerable: true,
|
|
1526
|
-
get: function () {
|
|
1527
|
-
return Emojis;
|
|
1528
|
-
}
|
|
1529
|
-
});
|
|
1530
|
-
Object.defineProperty(exports, 'Gated', {
|
|
1531
|
-
enumerable: true,
|
|
1532
|
-
get: function () {
|
|
1533
|
-
return Gated;
|
|
1534
|
-
}
|
|
1535
|
-
});
|
|
1536
|
-
Object.defineProperty(exports, 'InteractionHandler', {
|
|
1537
|
-
enumerable: true,
|
|
1538
|
-
get: function () {
|
|
1539
|
-
return InteractionHandler;
|
|
1540
|
-
}
|
|
1541
|
-
});
|
|
1542
|
-
Object.defineProperty(exports, 'ModalHandler', {
|
|
1543
|
-
enumerable: true,
|
|
1544
|
-
get: function () {
|
|
1545
|
-
return ModalHandler;
|
|
1546
|
-
}
|
|
1547
|
-
});
|
|
1548
|
-
Object.defineProperty(exports, 'Paginator', {
|
|
1549
|
-
enumerable: true,
|
|
1550
|
-
get: function () {
|
|
1551
|
-
return Paginator;
|
|
1552
|
-
}
|
|
1553
|
-
});
|
|
1554
|
-
Object.defineProperty(exports, 'RepliableHandler', {
|
|
1555
|
-
enumerable: true,
|
|
1556
|
-
get: function () {
|
|
1557
|
-
return RepliableHandler;
|
|
1558
|
-
}
|
|
1559
|
-
});
|
|
1560
|
-
Object.defineProperty(exports, 'SelectMenuHandler', {
|
|
1561
|
-
enumerable: true,
|
|
1562
|
-
get: function () {
|
|
1563
|
-
return SelectMenuHandler;
|
|
1564
|
-
}
|
|
1565
|
-
});
|
|
1566
|
-
Object.defineProperty(exports, 'SlashHandler', {
|
|
1567
|
-
enumerable: true,
|
|
1568
|
-
get: function () {
|
|
1569
|
-
return SlashHandler;
|
|
1570
|
-
}
|
|
1571
|
-
});
|
|
1572
|
-
Object.defineProperty(exports, 'Subscriber', {
|
|
1573
|
-
enumerable: true,
|
|
1574
|
-
get: function () {
|
|
1575
|
-
return Subscriber;
|
|
1576
|
-
}
|
|
1577
|
-
});
|
|
1578
|
-
Object.defineProperty(exports, 'WebhookLog', {
|
|
1579
|
-
enumerable: true,
|
|
1580
|
-
get: function () {
|
|
1581
|
-
return WebhookLog;
|
|
1582
|
-
}
|
|
1583
|
-
});
|
|
1584
|
-
Object.defineProperty(exports, '__exportAll', {
|
|
1585
|
-
enumerable: true,
|
|
1586
|
-
get: function () {
|
|
1587
|
-
return __exportAll;
|
|
1588
|
-
}
|
|
1589
|
-
});
|
|
1590
|
-
Object.defineProperty(exports, '__reExport', {
|
|
1591
|
-
enumerable: true,
|
|
1592
|
-
get: function () {
|
|
1593
|
-
return __reExport;
|
|
1594
|
-
}
|
|
1595
|
-
});
|
|
1596
|
-
Object.defineProperty(exports, '__toESM', {
|
|
1597
|
-
enumerable: true,
|
|
1598
|
-
get: function () {
|
|
1599
|
-
return __toESM;
|
|
1600
|
-
}
|
|
1601
|
-
});
|
|
1602
|
-
Object.defineProperty(exports, 'buildEngine', {
|
|
1603
|
-
enumerable: true,
|
|
1604
|
-
get: function () {
|
|
1605
|
-
return buildEngine;
|
|
1606
|
-
}
|
|
1607
|
-
});
|
|
1608
|
-
Object.defineProperty(exports, 'buildRouteMaps', {
|
|
1609
|
-
enumerable: true,
|
|
1610
|
-
get: function () {
|
|
1611
|
-
return buildRouteMaps;
|
|
1612
|
-
}
|
|
1613
|
-
});
|
|
1614
|
-
Object.defineProperty(exports, 'createSeedcord', {
|
|
1615
|
-
enumerable: true,
|
|
1616
|
-
get: function () {
|
|
1617
|
-
return createSeedcord;
|
|
1618
|
-
}
|
|
1619
|
-
});
|
|
1620
|
-
Object.defineProperty(exports, 'fetchApplicationId', {
|
|
1621
|
-
enumerable: true,
|
|
1622
|
-
get: function () {
|
|
1623
|
-
return fetchApplicationId;
|
|
1624
|
-
}
|
|
1625
|
-
});
|
|
1626
|
-
Object.defineProperty(exports, 'version', {
|
|
1627
|
-
enumerable: true,
|
|
1628
|
-
get: function () {
|
|
1629
|
-
return version;
|
|
1630
|
-
}
|
|
1631
|
-
});
|
|
1632
|
-
//# sourceMappingURL=edge.index-CAvykaH6.cjs.map
|