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