@seedcord/core 0.9.4 → 0.11.0-next.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 +28 -10
- package/dist/CoordinatedShutdown-H5YjZn8A.d.mts +78 -0
- package/dist/CoordinatedStartup-CGnJrqcl.mjs +168 -0
- package/dist/CoordinatedStartup-CGnJrqcl.mjs.map +1 -0
- package/dist/{HmrModuleHandler-DOAtah3j.mjs → HmrModuleHandler-8ZC-UuZh.mjs} +9 -22
- package/dist/HmrModuleHandler-8ZC-UuZh.mjs.map +1 -0
- package/dist/PluginHost-B8kxLOeb.mjs +245 -0
- package/dist/PluginHost-B8kxLOeb.mjs.map +1 -0
- package/dist/{Plugin-Dhx_ivqj.d.mts → PluginHost-DB4j8unu.d.mts} +110 -17
- package/dist/PluginLifecycle-AhQ46NIr.mjs +158 -0
- package/dist/PluginLifecycle-AhQ46NIr.mjs.map +1 -0
- package/dist/ServerHost-CvGQr7oN.mjs +191 -0
- package/dist/ServerHost-CvGQr7oN.mjs.map +1 -0
- package/dist/ServerHost-DYeh0KTk.d.mts +40 -0
- package/dist/hmr.index.d.mts +0 -1
- package/dist/hmr.index.mjs +1 -1
- package/dist/{cursor-BTU5bGoo.d.mts → index-Du71Xx69.d.mts} +253 -6
- package/dist/index.d.mts +19 -84
- package/dist/index.mjs +17 -37
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.d.mts +185 -22
- package/dist/internal.index.mjs +713 -19
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.d.mts +25 -70
- package/dist/node-internal.index.mjs +84 -298
- package/dist/node-internal.index.mjs.map +1 -1
- package/dist/node.index.d.mts +3 -2
- package/dist/node.index.mjs +53 -200
- package/dist/node.index.mjs.map +1 -1
- package/dist/{phases-D_zQBXga.d.mts → phases-FTM69J3a.d.mts} +4 -4
- package/dist/plugin.index.d.mts +2 -2
- package/dist/plugin.index.mjs +2 -2
- package/dist/publishDefault-D3dAyzZ9.mjs +7 -0
- package/dist/publishDefault-D3dAyzZ9.mjs.map +1 -0
- package/dist/subscribers-Ce3SWy23.mjs +1913 -0
- package/dist/subscribers-Ce3SWy23.mjs.map +1 -0
- package/package.json +17 -16
- package/dist/Bus-BxPa-Uvp.mjs +0 -866
- package/dist/Bus-BxPa-Uvp.mjs.map +0 -1
- package/dist/HmrModuleHandler-DOAtah3j.mjs.map +0 -1
- package/dist/Pluggable-CXyUZIvT.mjs +0 -310
- package/dist/Pluggable-CXyUZIvT.mjs.map +0 -1
- package/dist/Pluggable-CwKbLui3.d.mts +0 -162
- package/dist/Plugin-CmNW5n6a.mjs +0 -122
- package/dist/Plugin-CmNW5n6a.mjs.map +0 -1
- package/dist/contextMenuLeaves-COzDYa6c.d.mts +0 -17
- package/dist/cursor-CgNBnJfu.mjs +0 -802
- package/dist/cursor-CgNBnJfu.mjs.map +0 -1
- package/dist/devChannel-CQU7hq7Q.mjs +0 -14
- package/dist/devChannel-CQU7hq7Q.mjs.map +0 -1
- package/dist/index-B_-EjyvS.d.mts +0 -135
- package/dist/publishDefault-BuSZPbjT.mjs +0 -14
- package/dist/publishDefault-BuSZPbjT.mjs.map +0 -1
- package/dist/slashRouteLeaves-k1hJnXDO.mjs +0 -31
- package/dist/slashRouteLeaves-k1hJnXDO.mjs.map +0 -1
- package/dist/subscribers-xGy7a7xR.mjs +0 -167
- package/dist/subscribers-xGy7a7xR.mjs.map +0 -1
- package/dist/types-BG0BimVr.d.mts +0 -13
package/dist/internal.index.mjs
CHANGED
|
@@ -1,15 +1,83 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { At as guardedAccessor, B as NotInGuild, Bt as isBuilderComponentClass, C as PermissionNames, Ct as warnLinks, D as pickNotice, Dt as linkCommands, Et as indexById, F as MissingPermissions, Ft as skippedNote, G as serializeReply, H as OnCooldown, Ht as Notice, I as MissingRole, It as contextMenuLeaves, K as translateSerializationError, L as NeedsAny, Lt as NoticeCard, M as runCommits, Mt as commandsRoute, N as GateNotice, Nt as globalFirst, O as discardCommits, Ot as accessorStore, P as HasDangerousPermissions, Pt as scopeIdsOf, Q as sendTarget, R as NotAllowed, St as installLinks, Tt as commandRefOf, V as NotOwner, Vt as bindBotColor, X as AckTrace, Y as reportedWrite, Z as checkAckLegality, _ as pageCursor, _t as SubscribeMetadataKey, a as RegisterSubscriber, at as ComponentDefsKey, c as VerifyWebhooks, dt as EventMiddlewareMetadataKey, et as interactionMiddlewareMetaOf, f as Subscriber, ft as GatedMetadataKey, g as PAGE_MAX, gt as InteractionRouteKeys, h as sendFlags, ht as InteractionMiddlewareMetadataKey, i as RegisterDefaults, it as storeInteractionRoute, j as runCheck, jt as GLOBAL_ID, kt as clearStore, lt as CommandMetadataKey, m as deferFlags, mt as InteractionMetadataKey, n as busLoggerOf, nt as interactionRoutesOf, o as RegisteredCount, ot as decodeComponentRoute, p as ReportThrottle, r as registrationFor, rt as storeComponentRoute, s as UnregisterSubscriber, st as ConfigGuilds, tt as contextMenuRouteOf, ut as EventMetadataKey, v as ResolvedEmoji, vt as WebhookUrlMetadataKey, wt as commandKey, y as Silence, yt as CommandInjector, z as NotInDm } from "./subscribers-Ce3SWy23.mjs";
|
|
2
|
+
import { t as PublishDefault } from "./publishDefault-D3dAyzZ9.mjs";
|
|
3
|
+
import { c as setDevChannel, i as FixedScope, n as attachmentsOf, r as sealAttachments, s as getDevChannel } from "./PluginHost-B8kxLOeb.mjs";
|
|
4
|
+
import { t as HmrModuleHandler } from "./HmrModuleHandler-8ZC-UuZh.mjs";
|
|
5
|
+
import { i as logUnknownFault, t as PluginLifecycle } from "./PluginLifecycle-AhQ46NIr.mjs";
|
|
6
|
+
import { t as CoordinatedStartup } from "./CoordinatedStartup-CGnJrqcl.mjs";
|
|
6
7
|
import "reflect-metadata";
|
|
8
|
+
import { SlashCommandBuilder } from "@discordjs/builders";
|
|
9
|
+
import { Routes } from "discord-api-types/v10";
|
|
7
10
|
import { SeedcordErrorCode, paint } from "@seedcord/errors";
|
|
8
|
-
import { SeedcordError } from "@seedcord/errors/internal";
|
|
9
|
-
import { Logger } from "@seedcord/logger";
|
|
10
|
-
import {
|
|
11
|
+
import { SeedcordError, SeedcordTypeError, throwSingleOrAggregate, validateDiscordToken } from "@seedcord/errors/internal";
|
|
12
|
+
import { LEVEL_COLOR, Logger, LoggerChannelRegistry } from "@seedcord/logger";
|
|
13
|
+
import { asError, botFolder, isPlainObject, pluralize, routeLeavesOf, unlessAborted } from "@seedcord/utils/internal";
|
|
14
|
+
import { filterCirculars, formatFilePath, importFiles, timestampFromSnowflake } from "@seedcord/utils";
|
|
11
15
|
import { Envapter } from "envapt";
|
|
16
|
+
import { DiscordAPIError, HTTPError, RESTEvents } from "@discordjs/rest";
|
|
17
|
+
import { format } from "node:util";
|
|
18
|
+
import { CONSOLE_LEVEL, consoleRecord, routeConsole, traceError } from "@seedcord/logger/internal";
|
|
12
19
|
|
|
20
|
+
//#region src/subscribers/SubscriberLoader.ts
|
|
21
|
+
var SubscriberLoader = class {
|
|
22
|
+
bus;
|
|
23
|
+
directory;
|
|
24
|
+
isInitialized = false;
|
|
25
|
+
hmrHandler;
|
|
26
|
+
constructor(bus, directory) {
|
|
27
|
+
this.bus = bus;
|
|
28
|
+
this.directory = directory;
|
|
29
|
+
if (!this.directory) return;
|
|
30
|
+
if (!Envapter.isDevelopment && !Envapter.isTest) return;
|
|
31
|
+
this.hmrHandler = new HmrModuleHandler({
|
|
32
|
+
handlersDir: this.directory,
|
|
33
|
+
isHandler: this.isSubscriber.bind(this),
|
|
34
|
+
registerHandler: this.registerSubscriber.bind(this),
|
|
35
|
+
unregisterHandler: this.unregisterSubscriber.bind(this),
|
|
36
|
+
getArtifacts: this.getArtifacts.bind(this),
|
|
37
|
+
logger: busLoggerOf(this.bus)
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** @internal */
|
|
41
|
+
async init() {
|
|
42
|
+
if (this.isInitialized) return;
|
|
43
|
+
this.isInitialized = true;
|
|
44
|
+
this.bus[RegisterDefaults]();
|
|
45
|
+
const { directory } = this;
|
|
46
|
+
if (directory) {
|
|
47
|
+
busLoggerOf(this.bus).debug(paint.mute(directory));
|
|
48
|
+
await this.load(directory);
|
|
49
|
+
busLoggerOf(this.bus).utils.list([`${paint.iris.bold(this.bus[RegisteredCount])} subscribers`], paint.mint.bold("Loaded"), "debug");
|
|
50
|
+
}
|
|
51
|
+
if (!Envapter.isTest) await this.bus[VerifyWebhooks]();
|
|
52
|
+
}
|
|
53
|
+
/** @internal */
|
|
54
|
+
async onHmr(event) {
|
|
55
|
+
if (this.hmrHandler) await this.hmrHandler.handle(event);
|
|
56
|
+
}
|
|
57
|
+
async load(dir) {
|
|
58
|
+
for await (const { fullPath, relativePath, imported } of importFiles(dir)) for (const value of Object.values(imported)) {
|
|
59
|
+
if (!this.isSubscriber(value)) continue;
|
|
60
|
+
this.registerSubscriber(value);
|
|
61
|
+
this.hmrHandler?.trackHandler(fullPath, value);
|
|
62
|
+
busLoggerOf(this.bus).utils.registration(value.name, relativePath, void 0, "trace");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
registerSubscriber(ctor) {
|
|
66
|
+
this.bus[RegisterSubscriber](registrationFor(ctor));
|
|
67
|
+
}
|
|
68
|
+
unregisterSubscriber(ctor, artifacts) {
|
|
69
|
+
this.bus[UnregisterSubscriber](ctor, artifacts);
|
|
70
|
+
}
|
|
71
|
+
getArtifacts(ctor) {
|
|
72
|
+
return [Reflect.getMetadata(SubscribeMetadataKey, ctor).subscriber];
|
|
73
|
+
}
|
|
74
|
+
isSubscriber(value) {
|
|
75
|
+
if (typeof value !== "function") return false;
|
|
76
|
+
return value.prototype instanceof Subscriber && Reflect.hasMetadata(SubscribeMetadataKey, value);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
13
81
|
//#region src/decorators/brands.ts
|
|
14
82
|
const SlashRouteBrand = Symbol("seedcord:brand:slash-route");
|
|
15
83
|
const AutocompleteRouteBrand = Symbol("seedcord:brand:autocomplete-route");
|
|
@@ -49,14 +117,11 @@ var MiddlewareRegistry = class {
|
|
|
49
117
|
/**
|
|
50
118
|
* Adds a decorated middleware class and returns what it registered with. Returns `undefined` for an
|
|
51
119
|
* undecorated class or one already registered.
|
|
52
|
-
*
|
|
53
|
-
* @throws A **SeedcordError** If another class with the same name is registered.
|
|
54
120
|
*/
|
|
55
121
|
register(ctor) {
|
|
56
122
|
const registration = this.registrationOf(ctor);
|
|
57
123
|
if (!registration) return void 0;
|
|
58
124
|
if (this.entries.some((entry) => entry.ctor === ctor)) return void 0;
|
|
59
|
-
if (this.entries.some((entry) => entry.ctor.name === ctor.name)) throw new SeedcordError(SeedcordErrorCode.DuplicateMiddleware, [ctor.name]);
|
|
60
125
|
this.entries.push({
|
|
61
126
|
ctor,
|
|
62
127
|
priority: registration.priority,
|
|
@@ -143,31 +208,534 @@ function isEmojiTuple(value) {
|
|
|
143
208
|
return Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && typeof value[1] === "string";
|
|
144
209
|
}
|
|
145
210
|
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/miscellaneous/currentApplication.ts
|
|
213
|
+
async function currentApplication(rest) {
|
|
214
|
+
return await rest.get(Routes.currentApplication());
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/miscellaneous/readBotToken.ts
|
|
219
|
+
function readBotToken() {
|
|
220
|
+
return validateDiscordToken(Envapter.get("DISCORD_BOT_TOKEN"));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/miscellaneous/resolveEmojis.ts
|
|
225
|
+
function byName(list) {
|
|
226
|
+
const map = /* @__PURE__ */ new Map();
|
|
227
|
+
for (const entry of list) if (entry.name) map.set(entry.name, entry);
|
|
228
|
+
return map;
|
|
229
|
+
}
|
|
230
|
+
var EmojiResolver = class {
|
|
231
|
+
rest;
|
|
232
|
+
applicationId;
|
|
233
|
+
resolved = accessorStore();
|
|
234
|
+
problems = [];
|
|
235
|
+
guilds = /* @__PURE__ */ new Map();
|
|
236
|
+
application;
|
|
237
|
+
constructor(rest, applicationId) {
|
|
238
|
+
this.rest = rest;
|
|
239
|
+
this.applicationId = applicationId;
|
|
240
|
+
}
|
|
241
|
+
async resolve(configured) {
|
|
242
|
+
for (const [key, value] of Object.entries(configured)) if (isEmojiTuple(value)) await this.fromGuild(key, value);
|
|
243
|
+
else if (typeof value === "string") await this.fromApplication(key, value);
|
|
244
|
+
else this.problems.push(new SeedcordTypeError(SeedcordErrorCode.ConfigEmojiInvalid, [key]));
|
|
245
|
+
throwSingleOrAggregate(this.problems, SeedcordErrorCode.ConfigEmojiUnresolved);
|
|
246
|
+
return this.resolved;
|
|
247
|
+
}
|
|
248
|
+
async fromApplication(key, name) {
|
|
249
|
+
this.application ??= await this.applicationEmojis();
|
|
250
|
+
if (Error.isError(this.application)) {
|
|
251
|
+
this.unreadable(key, name, "the application", this.application, `Check that ${paint.bold("DISCORD_BOT_TOKEN")} is still valid.`);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
this.pick(key, name, this.application, "the application");
|
|
255
|
+
}
|
|
256
|
+
async applicationEmojis() {
|
|
257
|
+
try {
|
|
258
|
+
return byName((await this.rest.get(Routes.applicationEmojis(this.applicationId))).items);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return asError(error);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async fromGuild(key, [name, guildId]) {
|
|
264
|
+
let guild = this.guilds.get(guildId);
|
|
265
|
+
if (!guild) {
|
|
266
|
+
guild = await this.guildEmojis(guildId);
|
|
267
|
+
this.guilds.set(guildId, guild);
|
|
268
|
+
}
|
|
269
|
+
if (Error.isError(guild)) {
|
|
270
|
+
this.unreadable(key, name, `guild ${guildId}`, guild, "Check that the bot is in that guild.");
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
this.pick(key, name, guild, `guild ${guildId}`);
|
|
274
|
+
}
|
|
275
|
+
async guildEmojis(guildId) {
|
|
276
|
+
try {
|
|
277
|
+
return byName(await this.rest.get(Routes.guildEmojis(guildId)));
|
|
278
|
+
} catch (error) {
|
|
279
|
+
return asError(error);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
pick(key, name, source, where) {
|
|
283
|
+
const found = source.get(name);
|
|
284
|
+
if (!found?.id) {
|
|
285
|
+
this.problems.push(new SeedcordError(SeedcordErrorCode.ConfigEmojiNotFound, [
|
|
286
|
+
key,
|
|
287
|
+
name,
|
|
288
|
+
where
|
|
289
|
+
]));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
this.resolved[key] = new ResolvedEmoji(name, found.id, found.animated ?? false);
|
|
293
|
+
}
|
|
294
|
+
unreadable(key, name, where, cause, fix) {
|
|
295
|
+
this.problems.push(new SeedcordError(SeedcordErrorCode.ConfigEmojiSourceUnreadable, [
|
|
296
|
+
key,
|
|
297
|
+
name,
|
|
298
|
+
where,
|
|
299
|
+
cause.message,
|
|
300
|
+
fix
|
|
301
|
+
], { cause }));
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
function resolveEmojis(rest, applicationId, configured) {
|
|
305
|
+
return new EmojiResolver(rest, applicationId).resolve(configured);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/bakedIds.ts
|
|
310
|
+
const BAKED_IDS_KEY = "seedcord:core:baked-ids";
|
|
311
|
+
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/commands/guildSummaries.ts
|
|
314
|
+
async function guildName(rest, guildId) {
|
|
315
|
+
try {
|
|
316
|
+
return (await rest.get(Routes.guild(guildId))).name;
|
|
317
|
+
} catch {
|
|
318
|
+
return guildId;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function guildSummaries(rest, guildIds, knownNames = /* @__PURE__ */ new Map()) {
|
|
322
|
+
return Promise.all(Iterator.from(guildIds).map(async (id) => ({
|
|
323
|
+
id,
|
|
324
|
+
name: knownNames.get(id) ?? await guildName(rest, id)
|
|
325
|
+
})));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region src/commands/DeployReporter.ts
|
|
330
|
+
var DeployReporter = class {
|
|
331
|
+
logger;
|
|
332
|
+
guildNames = /* @__PURE__ */ new Map();
|
|
333
|
+
constructor(logger) {
|
|
334
|
+
this.logger = logger;
|
|
335
|
+
}
|
|
336
|
+
async readGuildNames(rest, scopes, signal) {
|
|
337
|
+
if (!getDevChannel()) return;
|
|
338
|
+
const unread = scopes.filter((scope) => scope !== "global" && !this.guildNames.has(scope));
|
|
339
|
+
for (const { id, name } of await unlessAborted(guildSummaries(rest, unread), signal)) this.guildNames.set(id, name);
|
|
340
|
+
}
|
|
341
|
+
skipped(scopes) {
|
|
342
|
+
if (scopes.length > 0) this.logger.info(skippedNote(scopes, (guildId) => this.guildLabel(guildId)));
|
|
343
|
+
}
|
|
344
|
+
deployed(scope, commands) {
|
|
345
|
+
const { utils } = this.logger;
|
|
346
|
+
const count = paint.iris.bold(commands.length);
|
|
347
|
+
const tag = pluralize(commands.length, "command");
|
|
348
|
+
const title = scope === "global" ? `Deployed ${count} global ${tag}` : `Deployed ${count} ${tag} to ${this.guildLabel(scope)}`;
|
|
349
|
+
utils.block(title, utils.wrap(commands.map((command) => command.name)), "info");
|
|
350
|
+
}
|
|
351
|
+
guildLabel(guildId) {
|
|
352
|
+
return paint.sky.bold(this.guildNames.get(guildId) ?? guildId);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
//#endregion
|
|
357
|
+
//#region src/commands/deployTarget.ts
|
|
358
|
+
function resolveDeployTarget(meta, configured, commandName) {
|
|
359
|
+
if (meta.scope === "global") return { scope: "global" };
|
|
360
|
+
if (meta.scope === "config") return configured.length > 0 ? guildTarget(configured) : { scope: "global" };
|
|
361
|
+
if (configured.length === 0 && meta.guilds.includes("config")) throw new SeedcordTypeError(SeedcordErrorCode.CoreCommandGuildsEmpty, [commandName]);
|
|
362
|
+
return guildTarget(meta.guilds.flatMap((target) => target === "config" ? configured : target));
|
|
363
|
+
}
|
|
364
|
+
function guildTarget(guilds) {
|
|
365
|
+
return {
|
|
366
|
+
scope: "guild",
|
|
367
|
+
guilds: [...new Set(guilds)]
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
//#endregion
|
|
372
|
+
//#region src/commands/isCommandClass.ts
|
|
373
|
+
/** @internal */
|
|
374
|
+
function isCommandClass(value) {
|
|
375
|
+
return isBuilderComponentClass(value) && Reflect.hasMetadata(CommandMetadataKey, value);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/commands/LastDeploy.ts
|
|
380
|
+
const ANSWER_TIMEOUT_MS = 5e3;
|
|
381
|
+
var LastDeploy = class {
|
|
382
|
+
saved;
|
|
383
|
+
read(applicationId, signal) {
|
|
384
|
+
this.saved ??= askSeedcordDev(applicationId);
|
|
385
|
+
return unlessAborted(this.saved, signal);
|
|
386
|
+
}
|
|
387
|
+
save(deployed) {
|
|
388
|
+
this.saved = Promise.resolve(deployed);
|
|
389
|
+
getDevChannel()?.send("seedcord:commands-deployed", deployed);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
function askSeedcordDev(applicationId) {
|
|
393
|
+
const cli = getDevChannel();
|
|
394
|
+
if (!cli) return Promise.resolve(void 0);
|
|
395
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
396
|
+
const settle = (deployed) => {
|
|
397
|
+
clearTimeout(timer);
|
|
398
|
+
cli.off("seedcord:last-deploy", onAnswer);
|
|
399
|
+
resolve(deployed);
|
|
400
|
+
};
|
|
401
|
+
const onAnswer = ({ deployed }) => settle(deployed);
|
|
402
|
+
const timer = setTimeout(() => settle(void 0), ANSWER_TIMEOUT_MS);
|
|
403
|
+
cli.on("seedcord:last-deploy", onAnswer);
|
|
404
|
+
cli.send("seedcord:read-deployed", { applicationId });
|
|
405
|
+
return promise;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
//#endregion
|
|
409
|
+
//#region src/commands/planDeploy.ts
|
|
410
|
+
const HEX = 16;
|
|
411
|
+
function byCodePoint(a, b) {
|
|
412
|
+
return a < b ? -1 : Number(a > b);
|
|
413
|
+
}
|
|
414
|
+
async function hashOf(commands) {
|
|
415
|
+
const sorted = commands.toSorted((a, b) => byCodePoint(commandKey(a), commandKey(b)));
|
|
416
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify(sorted)));
|
|
417
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(HEX).padStart(2, "0")).join("");
|
|
418
|
+
}
|
|
419
|
+
async function scopeHashes(scopes) {
|
|
420
|
+
const hashed = [...scopes].map(async ([scope, commands]) => [scope, await hashOf(commands)]);
|
|
421
|
+
return Object.fromEntries(await Promise.all(hashed));
|
|
422
|
+
}
|
|
423
|
+
function sameCommandNames(inCode, deployed) {
|
|
424
|
+
const onDiscord = new Set(deployed?.values().map(commandKey));
|
|
425
|
+
return new Set(inCode.map(commandKey)).symmetricDifference(onDiscord).size === 0;
|
|
426
|
+
}
|
|
427
|
+
async function planDeploy(scopes, last, readUnchanged) {
|
|
428
|
+
const hashes = await scopeHashes(scopes);
|
|
429
|
+
const unchanged = globalFirst(scopes).filter(([scope]) => hashes[scope] === last?.[scope]);
|
|
430
|
+
const onDiscord = await readUnchanged(unchanged.map(([scope]) => scope));
|
|
431
|
+
const skipped = new Set(unchanged.filter(([scope, commands]) => sameCommandNames(commands, onDiscord.get(scope))).map(([scope]) => scope));
|
|
432
|
+
return {
|
|
433
|
+
hashes,
|
|
434
|
+
skippedHashes: Object.fromEntries(Object.entries(hashes).filter(([scope]) => skipped.has(scope))),
|
|
435
|
+
toPut: new Map(scopes.entries().filter(([scope]) => !skipped.has(scope))),
|
|
436
|
+
skipped: [...skipped],
|
|
437
|
+
onDiscord
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
//#endregion
|
|
442
|
+
//#region src/commands/putCommands.ts
|
|
443
|
+
const MS_PER_SECOND = 1e3;
|
|
444
|
+
async function putCommands(scopes, options) {
|
|
445
|
+
const result = /* @__PURE__ */ new Map();
|
|
446
|
+
for (const [scope, commands] of globalFirst(scopes)) {
|
|
447
|
+
result.set(scope, indexById(await putScope(scope, commands, options)));
|
|
448
|
+
options.onDeployed?.(scope, commands);
|
|
449
|
+
}
|
|
450
|
+
return result;
|
|
451
|
+
}
|
|
452
|
+
async function putScope(scope, commands, options) {
|
|
453
|
+
try {
|
|
454
|
+
return await put(commandsRoute(options.applicationId, scope), commands, options);
|
|
455
|
+
} catch (error) {
|
|
456
|
+
if (options.signal?.aborted) throw error;
|
|
457
|
+
const refusal = [asError(error).message, statusOf(error)];
|
|
458
|
+
throw scope === "global" ? new SeedcordError(SeedcordErrorCode.CoreCommandGlobalDeployFailed, refusal, { cause: error }) : new SeedcordError(SeedcordErrorCode.CoreCommandGuildDeployFailed, [scope, ...refusal], { cause: error });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
function statusOf(error) {
|
|
462
|
+
return error instanceof DiscordAPIError || error instanceof HTTPError ? error.status : void 0;
|
|
463
|
+
}
|
|
464
|
+
async function put(route, body, { rest, logger, signal }) {
|
|
465
|
+
const reportWait = (info) => {
|
|
466
|
+
if (info.method !== "PUT" || !info.url.endsWith(route)) return;
|
|
467
|
+
const seconds = Math.ceil(info.timeToReset / MS_PER_SECOND);
|
|
468
|
+
logger.warn(`Discord rate-limited the command deploy. It continues in ${paint.amber(`${seconds}s`)}.`);
|
|
469
|
+
};
|
|
470
|
+
rest.on(RESTEvents.RateLimited, reportWait);
|
|
471
|
+
try {
|
|
472
|
+
return await unlessAborted(rest.put(route, {
|
|
473
|
+
body,
|
|
474
|
+
signal
|
|
475
|
+
}), signal);
|
|
476
|
+
} finally {
|
|
477
|
+
rest.off(RESTEvents.RateLimited, reportWait);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
//#endregion
|
|
482
|
+
//#region src/commands/readCommands.ts
|
|
483
|
+
async function readCommandsAt(scope, { rest, applicationId, signal }) {
|
|
484
|
+
const route = commandsRoute(applicationId, scope);
|
|
485
|
+
try {
|
|
486
|
+
return indexById(await rest.get(route, { signal }));
|
|
487
|
+
} catch (error) {
|
|
488
|
+
if (signal?.aborted) throw error;
|
|
489
|
+
const reason = asError(error).message;
|
|
490
|
+
throw new SeedcordError(SeedcordErrorCode.CoreDiscordReadFailed, scope === "global" ? ["the commands deployed globally", reason] : [
|
|
491
|
+
`the commands deployed to guild ${paint.sky(scope)}`,
|
|
492
|
+
reason,
|
|
493
|
+
`Check that ${paint.bold("DISCORD_BOT_TOKEN")} is still valid and that the bot is in that guild.`
|
|
494
|
+
], { cause: error });
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
async function readCommands(scopes, options) {
|
|
498
|
+
const read = Iterator.from(scopes).map(async (scope) => [scope, await readCommandsAt(scope, options)]);
|
|
499
|
+
return new Map(await Promise.all(read));
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
//#endregion
|
|
503
|
+
//#region src/commands/slashRouteLeaves.ts
|
|
504
|
+
/**
|
|
505
|
+
* dedupes route keys since the same builder appears once per guild.
|
|
506
|
+
* these come from the same {@link routeLeavesOf} that `seedcord codegen` reads, so the boot-time
|
|
507
|
+
* check compares against the generated registry's keys.
|
|
508
|
+
*
|
|
509
|
+
* @internal
|
|
510
|
+
*/
|
|
511
|
+
function slashRouteLeaves(commands) {
|
|
512
|
+
const leaves = /* @__PURE__ */ new Set();
|
|
513
|
+
for (const command of commands) {
|
|
514
|
+
if (!(command instanceof SlashCommandBuilder)) continue;
|
|
515
|
+
for (const leaf of routeLeavesOf(command.toJSON())) leaves.add(leaf.route);
|
|
516
|
+
}
|
|
517
|
+
return leaves;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
//#endregion
|
|
521
|
+
//#region src/commands/CommandRegistry.ts
|
|
522
|
+
const COMMAND_DEPLOY_TIMEOUT_MS = 75e3;
|
|
523
|
+
/**
|
|
524
|
+
* Scans the command directory, builds each command, and PUTs the global and guild scopes to Discord.
|
|
525
|
+
*
|
|
526
|
+
* @internal
|
|
527
|
+
*/
|
|
528
|
+
var CommandRegistry = class {
|
|
529
|
+
core;
|
|
530
|
+
logger = new Logger("Commands", { channel: "commands" });
|
|
531
|
+
isInitialised = false;
|
|
532
|
+
scopedCommands = /* @__PURE__ */ new Map();
|
|
533
|
+
ctorToCommand = /* @__PURE__ */ new Map();
|
|
534
|
+
loading = false;
|
|
535
|
+
loadedCommands = [];
|
|
536
|
+
hmrHandler;
|
|
537
|
+
pendingEvents = /* @__PURE__ */ new Map();
|
|
538
|
+
injector = new CommandInjector();
|
|
539
|
+
lastDeploy = new LastDeploy();
|
|
540
|
+
reporter = new DeployReporter(this.logger);
|
|
541
|
+
dir;
|
|
542
|
+
configuredGuilds;
|
|
543
|
+
constructor(core) {
|
|
544
|
+
this.core = core;
|
|
545
|
+
const { commands } = core.config.bot;
|
|
546
|
+
if (!commands.path) throw new SeedcordError(SeedcordErrorCode.CoreControllerPathMissing, ["CommandRegistry", "commands"]);
|
|
547
|
+
this.dir = commands.path;
|
|
548
|
+
this.configuredGuilds = commands.guilds ?? [];
|
|
549
|
+
if (!Envapter.isDevelopment && !Envapter.isTest) return;
|
|
550
|
+
this.hmrHandler = new HmrModuleHandler({
|
|
551
|
+
handlersDir: this.dir,
|
|
552
|
+
isHandler: isCommandClass,
|
|
553
|
+
registerHandler: this.registerCommand.bind(this),
|
|
554
|
+
unregisterHandler: this.unregisterCommand.bind(this),
|
|
555
|
+
getArtifacts: this.getArtifacts.bind(this),
|
|
556
|
+
logger: this.logger
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
getArtifacts(ctor) {
|
|
560
|
+
return this.ctorToCommand.get(ctor);
|
|
561
|
+
}
|
|
562
|
+
/** @internal */
|
|
563
|
+
async init() {
|
|
564
|
+
if (this.isInitialised) return;
|
|
565
|
+
this.isInitialised = true;
|
|
566
|
+
this.loading = true;
|
|
567
|
+
this.loadedCommands.length = 0;
|
|
568
|
+
try {
|
|
569
|
+
await this.loadCommands(this.dir);
|
|
570
|
+
} finally {
|
|
571
|
+
this.loading = false;
|
|
572
|
+
}
|
|
573
|
+
this.reportLoad();
|
|
574
|
+
getDevChannel()?.on("seedcord:refresh-commands", (data) => {
|
|
575
|
+
this.refresh(data.shouldRefresh);
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
reportLoad() {
|
|
579
|
+
const { utils } = this.logger;
|
|
580
|
+
utils.summary("Loaded commands", {
|
|
581
|
+
global: this.scopedCommands.get("global")?.length ?? 0,
|
|
582
|
+
"guild groups": [...this.scopedCommands.keys()].filter((scope) => scope !== GLOBAL_ID).length
|
|
583
|
+
}, "debug");
|
|
584
|
+
let slash = 0;
|
|
585
|
+
let menu = 0;
|
|
586
|
+
for (const command of this.loadedCommands) if (command.kind === "slash command") slash++;
|
|
587
|
+
else menu++;
|
|
588
|
+
utils.block("Loaded commands", [...utils.entries(this.loadedCommands), ...utils.counts({
|
|
589
|
+
"slash commands": slash,
|
|
590
|
+
"context menus": menu
|
|
591
|
+
})], "debug");
|
|
592
|
+
}
|
|
593
|
+
/** @internal */
|
|
594
|
+
async refresh(shouldRefresh = true) {
|
|
595
|
+
if (!shouldRefresh) {
|
|
596
|
+
this.logger.debug(paint.italic("Command refresh cancelled."));
|
|
597
|
+
this.pendingEvents.clear();
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
this.logger.debug(paint.italic("Refreshing commands..."));
|
|
601
|
+
for (const event of this.pendingEvents.values()) await this.hmrHandler?.handle(event);
|
|
602
|
+
this.pendingEvents.clear();
|
|
603
|
+
await this.setCommands();
|
|
604
|
+
}
|
|
605
|
+
/** @internal */
|
|
606
|
+
async onHmr(event) {
|
|
607
|
+
if (botFolder(this.dir).holds(event.file)) {
|
|
608
|
+
this.pendingEvents.delete(event.file);
|
|
609
|
+
this.pendingEvents.set(event.file, event);
|
|
610
|
+
getDevChannel()?.send("seedcord:commands-update-prompt", { files: [...this.pendingEvents.keys()].map((f) => formatFilePath(f)) });
|
|
611
|
+
} else await this.hmrHandler?.handle(event);
|
|
612
|
+
}
|
|
613
|
+
async loadCommands(dir) {
|
|
614
|
+
for await (const { fullPath, relativePath, imported } of importFiles(dir)) for (const exported of Object.values(imported)) {
|
|
615
|
+
if (!isCommandClass(exported)) continue;
|
|
616
|
+
this.registerCommand(exported, relativePath);
|
|
617
|
+
this.hmrHandler?.trackHandler(fullPath, exported);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
registerCommand(Ctor, rel) {
|
|
621
|
+
const meta = Reflect.getMetadata(CommandMetadataKey, Ctor);
|
|
622
|
+
if (!meta) return;
|
|
623
|
+
const comp = new Ctor().component;
|
|
624
|
+
const kind = comp instanceof SlashCommandBuilder ? "slash command" : "context menu";
|
|
625
|
+
const target = resolveDeployTarget(meta, this.configuredGuilds, Ctor.name);
|
|
626
|
+
for (const scope of scopeIdsOf(target)) {
|
|
627
|
+
const inScope = this.scopedCommands.get(scope);
|
|
628
|
+
if (inScope) inScope.push(comp);
|
|
629
|
+
else this.scopedCommands.set(scope, [comp]);
|
|
630
|
+
}
|
|
631
|
+
this.ctorToCommand.set(Ctor, {
|
|
632
|
+
name: comp.name,
|
|
633
|
+
...target
|
|
634
|
+
});
|
|
635
|
+
if (this.loading) this.loadedCommands.push({
|
|
636
|
+
name: comp.name,
|
|
637
|
+
from: formatFilePath(rel),
|
|
638
|
+
kind
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
unregisterCommand(ctor, artifacts) {
|
|
642
|
+
const info = artifacts ?? this.ctorToCommand.get(ctor);
|
|
643
|
+
if (!info) return;
|
|
644
|
+
for (const scope of scopeIdsOf(info)) {
|
|
645
|
+
const inScope = this.scopedCommands.get(scope) ?? [];
|
|
646
|
+
const idx = inScope.findIndex((c) => c.name === info.name);
|
|
647
|
+
if (idx !== -1) inScope.splice(idx, 1);
|
|
648
|
+
}
|
|
649
|
+
this.ctorToCommand.delete(ctor);
|
|
650
|
+
}
|
|
651
|
+
/** @internal */
|
|
652
|
+
async deploy(signal, { routes, check }) {
|
|
653
|
+
await this.init();
|
|
654
|
+
check?.(this.allCommands());
|
|
655
|
+
await this.setCommands(signal);
|
|
656
|
+
routes?.warnUnhandledRoutes(this.routeLeaves());
|
|
657
|
+
routes?.warnUnhandledContextMenuRoutes(this.contextMenuLeaves());
|
|
658
|
+
}
|
|
659
|
+
/** @internal */
|
|
660
|
+
async setCommands(signal) {
|
|
661
|
+
const { rest, applicationId } = this.core;
|
|
662
|
+
const last = await this.lastDeploy.read(applicationId, signal);
|
|
663
|
+
const plan = await planDeploy(this.commandBodies(), last?.scopes, (unchanged) => readCommands(unchanged, {
|
|
664
|
+
rest,
|
|
665
|
+
applicationId,
|
|
666
|
+
signal
|
|
667
|
+
}));
|
|
668
|
+
await this.reporter.readGuildNames(rest, [...plan.toPut.keys(), ...plan.skipped], signal);
|
|
669
|
+
if (plan.toPut.size > 0) this.lastDeploy.save({
|
|
670
|
+
applicationId,
|
|
671
|
+
scopes: plan.skippedHashes
|
|
672
|
+
});
|
|
673
|
+
const put = await putCommands(plan.toPut, {
|
|
674
|
+
rest,
|
|
675
|
+
applicationId,
|
|
676
|
+
logger: this.logger,
|
|
677
|
+
signal,
|
|
678
|
+
onDeployed: (scope, commands) => this.reporter.deployed(scope, commands)
|
|
679
|
+
});
|
|
680
|
+
this.reporter.skipped(plan.skipped);
|
|
681
|
+
const result = new Map([...plan.onDiscord, ...put]);
|
|
682
|
+
this.lastDeploy.save({
|
|
683
|
+
applicationId,
|
|
684
|
+
scopes: plan.hashes
|
|
685
|
+
});
|
|
686
|
+
this.injector.inject(result, this.allCommands());
|
|
687
|
+
this.core.bus[PublishDefault]("commandsDeployed", deployedPayload(result));
|
|
688
|
+
return result;
|
|
689
|
+
}
|
|
690
|
+
commandBodies() {
|
|
691
|
+
const deployable = this.scopedCommands.entries().filter(([scope, commands]) => scope !== "global" || commands.length > 0);
|
|
692
|
+
return new Map(deployable.map(([scope, commands]) => [scope, commands.map((c) => c.toJSON())]));
|
|
693
|
+
}
|
|
694
|
+
allCommands() {
|
|
695
|
+
return [...this.scopedCommands.values()].flat();
|
|
696
|
+
}
|
|
697
|
+
routeLeaves() {
|
|
698
|
+
return slashRouteLeaves(this.allCommands());
|
|
699
|
+
}
|
|
700
|
+
contextMenuLeaves() {
|
|
701
|
+
return contextMenuLeaves(this.allCommands().map((command) => command.toJSON()));
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
function deployedPayload(result) {
|
|
705
|
+
const guilds = {};
|
|
706
|
+
for (const [scope, deployed] of result) if (scope !== "global") guilds[scope] = [...deployed.values()];
|
|
707
|
+
return {
|
|
708
|
+
global: [...result.get("global")?.values() ?? []],
|
|
709
|
+
guilds
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
|
|
146
713
|
//#endregion
|
|
147
714
|
//#region src/hmr/wrapHot.ts
|
|
148
715
|
/** @internal */
|
|
149
716
|
function wrapHot(hot) {
|
|
150
717
|
return {
|
|
151
718
|
send: (event, data) => hot.send(event, data),
|
|
152
|
-
on: (event, cb) => hot.on(event, cb)
|
|
719
|
+
on: (event, cb) => hot.on(event, cb),
|
|
720
|
+
off: (event, cb) => hot.off(event, cb)
|
|
153
721
|
};
|
|
154
722
|
}
|
|
155
723
|
|
|
156
724
|
//#endregion
|
|
157
725
|
//#region src/hmr/HmrManager.ts
|
|
726
|
+
function connectDevChannel() {
|
|
727
|
+
if (import.meta.hot) setDevChannel(wrapHot(import.meta.hot));
|
|
728
|
+
return getDevChannel();
|
|
729
|
+
}
|
|
158
730
|
var HmrManager = class {
|
|
159
731
|
logger = new Logger("HMR", { channel: "hmr" });
|
|
160
732
|
listeners = /* @__PURE__ */ new Set();
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (!import.meta.hot) return;
|
|
164
|
-
const channel = wrapHot(import.meta.hot);
|
|
165
|
-
setDevChannel(channel);
|
|
733
|
+
init(channel) {
|
|
734
|
+
if (!channel) return;
|
|
166
735
|
if (!(Envapter.isDevelopment || Envapter.isTest)) return;
|
|
167
736
|
this.logger.debug("Enabled");
|
|
168
737
|
channel.on("seedcord:hmr", (payload) => void this.handleUpdate(payload));
|
|
169
738
|
}
|
|
170
|
-
/** @internal */
|
|
171
739
|
register(listener) {
|
|
172
740
|
this.listeners.add(listener);
|
|
173
741
|
}
|
|
@@ -183,6 +751,132 @@ var HmrManager = class {
|
|
|
183
751
|
}
|
|
184
752
|
};
|
|
185
753
|
|
|
754
|
+
//#endregion
|
|
755
|
+
//#region src/hmr/wire.ts
|
|
756
|
+
function toWireError(thrown) {
|
|
757
|
+
return encodeError(thrown, /* @__PURE__ */ new Set());
|
|
758
|
+
}
|
|
759
|
+
function encodeError(thrown, sent) {
|
|
760
|
+
if (!Error.isError(thrown)) return {
|
|
761
|
+
name: "Error",
|
|
762
|
+
message: String(thrown)
|
|
763
|
+
};
|
|
764
|
+
sent.add(thrown);
|
|
765
|
+
const unsent = (value) => !Error.isError(value) || !sent.has(value);
|
|
766
|
+
const { cause } = thrown;
|
|
767
|
+
return {
|
|
768
|
+
name: thrown.name,
|
|
769
|
+
message: thrown.message,
|
|
770
|
+
stack: thrown.stack,
|
|
771
|
+
code: Reflect.get(thrown, "code"),
|
|
772
|
+
cause: cause !== void 0 && unsent(cause) ? encodeError(cause, sent) : void 0,
|
|
773
|
+
errors: thrown instanceof AggregateError ? thrown.errors.filter(unsent).map((member) => encodeError(member, sent)) : void 0
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
function fromWireError({ name, message, stack, code, cause, errors }) {
|
|
777
|
+
const options = cause && { cause: fromWireError(cause) };
|
|
778
|
+
const error = errors ? new AggregateError(errors.map(fromWireError), message, options) : new Error(message, options);
|
|
779
|
+
error.name = name;
|
|
780
|
+
if (stack !== void 0) error.stack = stack;
|
|
781
|
+
if (code !== void 0) Reflect.set(error, "code", code);
|
|
782
|
+
return error;
|
|
783
|
+
}
|
|
784
|
+
function functionText(fn) {
|
|
785
|
+
const parent = Object.getPrototypeOf(fn);
|
|
786
|
+
if (Function.prototype.toString.call(fn).startsWith("class")) {
|
|
787
|
+
const extended = typeof parent === "function" && parent.name ? ` extends ${parent.name}` : "";
|
|
788
|
+
return `[class ${fn.name || "(anonymous)"}${extended}]`;
|
|
789
|
+
}
|
|
790
|
+
const kind = parent === null ? "Function (null prototype)" : fn.constructor.name;
|
|
791
|
+
return fn.name ? `[${kind}: ${fn.name}]` : `[${kind} (anonymous)]`;
|
|
792
|
+
}
|
|
793
|
+
function toWireArg(arg) {
|
|
794
|
+
if (Error.isError(arg)) return { error: toWireError(arg) };
|
|
795
|
+
switch (typeof arg) {
|
|
796
|
+
case "undefined":
|
|
797
|
+
case "symbol": return { value: String(arg) };
|
|
798
|
+
case "bigint": return { value: `${arg}n` };
|
|
799
|
+
case "function": return { value: functionText(arg) };
|
|
800
|
+
default: return { value: filterCirculars(arg, { mode: "json" }) };
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
function fromWireArg(arg) {
|
|
804
|
+
return "error" in arg ? fromWireError(arg.error) : arg.value;
|
|
805
|
+
}
|
|
806
|
+
function toWireLog({ args, ...record }) {
|
|
807
|
+
return {
|
|
808
|
+
...record,
|
|
809
|
+
args: args?.map(toWireArg)
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
function fromWireLog({ args, ...record }) {
|
|
813
|
+
return args ? {
|
|
814
|
+
...record,
|
|
815
|
+
args: args.map(fromWireArg)
|
|
816
|
+
} : record;
|
|
817
|
+
}
|
|
818
|
+
const DEV_LOG_MARKER = "seedcord:log ";
|
|
819
|
+
function toDevLogLine(record) {
|
|
820
|
+
return `${DEV_LOG_MARKER}${JSON.stringify(toWireLog(record))}`;
|
|
821
|
+
}
|
|
822
|
+
function isLogLevel(value) {
|
|
823
|
+
return typeof value === "string" && Object.hasOwn(LEVEL_COLOR, value);
|
|
824
|
+
}
|
|
825
|
+
function isWireError(value) {
|
|
826
|
+
return isPlainObject(value) && typeof value.name === "string" && typeof value.message === "string" && (value.stack === void 0 || typeof value.stack === "string") && (value.cause === void 0 || isWireError(value.cause)) && (value.errors === void 0 || Array.isArray(value.errors) && value.errors.every(isWireError));
|
|
827
|
+
}
|
|
828
|
+
function isWireArg(value) {
|
|
829
|
+
if (!isPlainObject(value)) return false;
|
|
830
|
+
return "error" in value ? isWireError(value.error) : "value" in value;
|
|
831
|
+
}
|
|
832
|
+
function isWireArgs(value) {
|
|
833
|
+
return value === void 0 || Array.isArray(value) && value.every(isWireArg);
|
|
834
|
+
}
|
|
835
|
+
function isWireLogRecord(value) {
|
|
836
|
+
return isPlainObject(value) && isLogLevel(value.level) && typeof value.message === "string" && typeof value.label === "string" && typeof value.channel === "string" && typeof value.timestamp === "number" && isWireArgs(value.args);
|
|
837
|
+
}
|
|
838
|
+
function fromDevLogLine(line) {
|
|
839
|
+
if (!line.startsWith(DEV_LOG_MARKER)) return void 0;
|
|
840
|
+
let parsed;
|
|
841
|
+
try {
|
|
842
|
+
parsed = JSON.parse(line.slice(13));
|
|
843
|
+
} catch {
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
return isWireLogRecord(parsed) ? fromWireLog(parsed) : void 0;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
//#endregion
|
|
850
|
+
//#region src/hmr/forwardLogsToCli.ts
|
|
851
|
+
const WORKER_ROUTED = [
|
|
852
|
+
"debug",
|
|
853
|
+
"dir",
|
|
854
|
+
"dirxml",
|
|
855
|
+
"error",
|
|
856
|
+
"info",
|
|
857
|
+
"log",
|
|
858
|
+
"table",
|
|
859
|
+
"trace",
|
|
860
|
+
"warn"
|
|
861
|
+
];
|
|
862
|
+
function forwardLogsToCli() {
|
|
863
|
+
const registry = LoggerChannelRegistry.instance;
|
|
864
|
+
const print = console.log.bind(console);
|
|
865
|
+
registry.installSink({
|
|
866
|
+
kind: "capture",
|
|
867
|
+
onLog: (record) => print(toDevLogLine(record))
|
|
868
|
+
}, { muteConsole: true });
|
|
869
|
+
routeConsole(WORKER_ROUTED, (method, args, invoked) => {
|
|
870
|
+
const level = CONSOLE_LEVEL[method];
|
|
871
|
+
if (method === "trace") {
|
|
872
|
+
registry.dispatch(consoleRecord(level, "", [traceError(format(...args), invoked)]));
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
const [first, ...rest] = args;
|
|
876
|
+
registry.dispatch(typeof first === "string" ? consoleRecord(level, first, rest) : consoleRecord(level, "", args));
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
|
|
186
880
|
//#endregion
|
|
187
881
|
//#region src/dispatch/dispatchReport.ts
|
|
188
882
|
let dispatchLogger;
|
|
@@ -274,5 +968,5 @@ async function runAfter(ran, result, logger) {
|
|
|
274
968
|
}
|
|
275
969
|
|
|
276
970
|
//#endregion
|
|
277
|
-
export { AckTrace, AutocompleteRouteBrand, CommandMetadataKey, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, EventMetadataKey, EventMiddlewareMetadataKey, GateNotice, GatedMetadataKey, HasDangerousPermissions, HmrManager, InteractionMetadataKey, InteractionMiddlewareMetadataKey, InteractionRouteKeys, MiddlewareKindsBrand, MiddlewareRegistry, MissingPermissions, MissingRole, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, PAGE_MAX, PermissionNames, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, ReportThrottle, SlashRouteBrand, SubscribeMetadataKey, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore,
|
|
971
|
+
export { AckTrace, AutocompleteRouteBrand, BAKED_IDS_KEY, COMMAND_DEPLOY_TIMEOUT_MS, CommandMetadataKey, CommandRegistry, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, CoordinatedStartup, EventMetadataKey, EventMiddlewareMetadataKey, FixedScope, GLOBAL_ID, GateNotice, GatedMetadataKey, HasDangerousPermissions, HmrManager, InteractionMetadataKey, InteractionMiddlewareMetadataKey, InteractionRouteKeys, MiddlewareKindsBrand, MiddlewareRegistry, MissingPermissions, MissingRole, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, PAGE_MAX, PermissionNames, PluginLifecycle, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, ReportThrottle, SlashRouteBrand, SubscribeMetadataKey, SubscriberLoader, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore, attachmentsOf, bindBotColor, busLoggerOf, checkAckLegality, clearStore, commandKey, commandRefOf, commandsRoute, connectDevChannel, contextMenuLeaves, contextMenuRouteOf, currentApplication, decodeComponentRoute, deferFlags, eventResultFor, forwardLogsToCli, fromDevLogLine, fromWireError, getDevChannel, globalFirst, guardedAccessor, guildSummaries, indexById, installLinks, interactionMiddleware, interactionMiddlewareMetaOf, interactionRoutesOf, isCommandClass, isEmojiTuple, linkCommands, logUnknownFault, outcomeFor, pageCursor, pickNotice, planDeploy, putCommands, queuedMsFor, readBotToken, readCommands, readCommandsAt, registrationFor, reportDispatch, reportedWrite, resolveDeployTarget, resolveEmojis, resultFor, runAfter, runGates, runHandlerGates, scopeIdsOf, sealAttachments, sendFlags, sendTarget, serializeReply, setDevChannel, skippedNote, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, toWireError, translateSerializationError, warnLinks, wrapHot };
|
|
278
972
|
//# sourceMappingURL=internal.index.mjs.map
|