@seedcord/core 0.1.0-next.6 → 0.1.0-next.8
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/dist/HmrModuleHandler-3sNgKHdf.mjs +163 -0
- package/dist/HmrModuleHandler-3sNgKHdf.mjs.map +1 -0
- package/dist/HmrModuleHandler-CTBng7wV.cjs +170 -0
- package/dist/HmrModuleHandler-CTBng7wV.cjs.map +1 -0
- package/dist/Plugin-BZit5HUI.d.mts +555 -0
- package/dist/{Plugin-C2-VHrsX.cjs → Plugin-BlkhHNRm.cjs} +15 -34
- package/dist/Plugin-BlkhHNRm.cjs.map +1 -0
- package/dist/{Plugin-PcdPjAzc.mjs → Plugin-CDAV7P9l.mjs} +16 -29
- package/dist/Plugin-CDAV7P9l.mjs.map +1 -0
- package/dist/basic-1hebYVcN.d.mts +22 -0
- package/dist/contextMenuLeaves-f2rbyo83.d.mts +19 -0
- package/dist/{permissions-DSW_EZfN.cjs → cursor-BMkFAwO2.cjs} +308 -300
- package/dist/cursor-BMkFAwO2.cjs.map +1 -0
- package/dist/{permissions-RVCBhL2n.mjs → cursor-BvSH8tgw.mjs} +249 -247
- package/dist/cursor-BvSH8tgw.mjs.map +1 -0
- package/dist/{AutocompleteOptions-eKhIe6Qt.d.mts → cursor-D6nBjwaE.d.mts} +207 -116
- package/dist/hmr.index.cjs +2 -164
- package/dist/hmr.index.mjs +2 -162
- package/dist/index-Cr4-VV4u.d.mts +142 -0
- package/dist/index.cjs +362 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +239 -38
- package/dist/index.mjs +313 -26
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.cjs +278 -83
- package/dist/internal.index.cjs.map +1 -1
- package/dist/internal.index.d.mts +111 -42
- package/dist/internal.index.mjs +223 -43
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.cjs +314 -76
- package/dist/node-internal.index.cjs.map +1 -1
- package/dist/node-internal.index.d.mts +123 -2164
- package/dist/node-internal.index.mjs +313 -77
- package/dist/node-internal.index.mjs.map +1 -1
- package/dist/{phases-BfpAG6w3.mjs → phases-BauKJOTT.mjs} +2 -2
- package/dist/phases-BauKJOTT.mjs.map +1 -0
- package/dist/{phases-C_BGNyVo.cjs → phases-DcuDHoQl.cjs} +2 -2
- package/dist/phases-DcuDHoQl.cjs.map +1 -0
- package/dist/plugin.index.cjs +1 -1
- package/dist/plugin.index.d.mts +2 -2
- package/dist/plugin.index.mjs +1 -1
- package/dist/publishDefault-CVlwYUS5.cjs +382 -0
- package/dist/publishDefault-CVlwYUS5.cjs.map +1 -0
- package/dist/publishDefault-CldnxcT2.mjs +287 -0
- package/dist/publishDefault-CldnxcT2.mjs.map +1 -0
- package/dist/slashRouteLeaves-BANDtPFm.mjs +23 -0
- package/dist/slashRouteLeaves-BANDtPFm.mjs.map +1 -0
- package/dist/slashRouteLeaves-nfxOAM7Q.cjs +28 -0
- package/dist/slashRouteLeaves-nfxOAM7Q.cjs.map +1 -0
- package/dist/subscribers-BSJoyKNN.cjs +517 -0
- package/dist/subscribers-BSJoyKNN.cjs.map +1 -0
- package/dist/subscribers-DuxdM2cS.mjs +480 -0
- package/dist/subscribers-DuxdM2cS.mjs.map +1 -0
- package/package.json +7 -6
- package/dist/Plugin-C2-VHrsX.cjs.map +0 -1
- package/dist/Plugin-CNyRKDHT.d.mts +0 -226
- package/dist/Plugin-PcdPjAzc.mjs.map +0 -1
- package/dist/hmr.index.cjs.map +0 -1
- package/dist/hmr.index.mjs.map +0 -1
- package/dist/is-never-Ddaba1Su.d.mts +0 -58
- package/dist/permissions-DSW_EZfN.cjs.map +0 -1
- package/dist/permissions-RVCBhL2n.mjs.map +0 -1
- package/dist/phases-BfpAG6w3.mjs.map +0 -1
- package/dist/phases-C_BGNyVo.cjs.map +0 -1
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
import { FrameworkChannel, Logger } from "@seedcord/logger";
|
|
2
|
+
import { TypedEventEmitter } from "@seedcord/event-emitter";
|
|
3
|
+
import { REST } from "@discordjs/rest";
|
|
4
|
+
import { Config, EventFrequency, HmrAware, HmrUpdateEvent, IRateLimiter, RenderContext, ReplyResponse, Tail, TypedExclude } from "@seedcord/types";
|
|
5
|
+
import { UUID } from "node:crypto";
|
|
6
|
+
|
|
7
|
+
//#region src/metadataKeys.d.ts
|
|
8
|
+
declare enum InteractionRoutes {
|
|
9
|
+
Slash = "slash",
|
|
10
|
+
Button = "button",
|
|
11
|
+
Modal = "modal",
|
|
12
|
+
StringMenu = "stringMenu",
|
|
13
|
+
UserMenu = "userMenu",
|
|
14
|
+
RoleMenu = "roleMenu",
|
|
15
|
+
ChannelMenu = "channelMenu",
|
|
16
|
+
MentionableMenu = "mentionableMenu",
|
|
17
|
+
MessageContextMenu = "messageContextMenu",
|
|
18
|
+
UserContextMenu = "userContextMenu",
|
|
19
|
+
Autocomplete = "autocomplete"
|
|
20
|
+
}
|
|
21
|
+
declare const InteractionMetadataKey: unique symbol;
|
|
22
|
+
declare const InteractionRouteKeys: Record<InteractionRoutes, symbol>;
|
|
23
|
+
declare const MiddlewareMetadataKey: unique symbol;
|
|
24
|
+
declare const EventMetadataKey: unique symbol;
|
|
25
|
+
declare const CommandMetadataKey: unique symbol;
|
|
26
|
+
declare const GatedMetadataKey: unique symbol;
|
|
27
|
+
declare const SubscribeMetadataKey: unique symbol;
|
|
28
|
+
declare const WebhookUrlMetadataKey: unique symbol;
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/subscribers/publishDefault.d.ts
|
|
31
|
+
/**
|
|
32
|
+
* The key of the Bus method that emits the framework's own subscriptions. `publish` excludes these keys
|
|
33
|
+
* through `PublishableKey`, and this symbol is exported only from the internal entry.
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
declare const PublishDefault: unique symbol;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/reply/AckTrace.d.ts
|
|
40
|
+
/** The first ack's stack, passed as the throw cause so a double-ack points to both lines. */
|
|
41
|
+
declare class AckTrace extends Error {
|
|
42
|
+
constructor(method: ReplyMethod);
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/reply/ackLegality.d.ts
|
|
46
|
+
type AckState = 'unacked' | 'deferred-reply' | 'deferred-update' | 'replied';
|
|
47
|
+
type ReplyMethod = 'reply' | 'defer' | 'deferUpdate' | 'update' | 'followUp' | 'edit' | 'delete' | 'send' | 'showModal';
|
|
48
|
+
/** Throws before any API call. `ackedBy` becomes the throw cause, its stack points at the first ack. */
|
|
49
|
+
declare function checkAckLegality(method: ReplyMethod, state: AckState, routeId: string, ackedBy?: AckTrace): void;
|
|
50
|
+
declare function sendTarget(state: AckState): 'reply' | 'edit' | 'followUp';
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/reply/responseReport.d.ts
|
|
53
|
+
/** What `publishResponse` requires. Without it the call returns without publishing. */
|
|
54
|
+
interface ReplyTelemetry {
|
|
55
|
+
readonly bus: Bus;
|
|
56
|
+
readonly interactionId: string;
|
|
57
|
+
}
|
|
58
|
+
/** The reply verbs plus the autocomplete choices callback, which runs outside the ack state machine. */
|
|
59
|
+
type WriteMethod = ReplyMethod | 'respond';
|
|
60
|
+
/**
|
|
61
|
+
* Runs a write that returns no message and publishes whichever arm it takes.
|
|
62
|
+
*
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
declare function reportedWrite<Result>(telemetry: ReplyTelemetry | undefined, routeId: string, method: WriteMethod, write: () => Promise<Result>): Promise<Result>;
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/stops/Notice.d.ts
|
|
68
|
+
/**
|
|
69
|
+
* Base class for a user-facing refusal or a reported fault.
|
|
70
|
+
*
|
|
71
|
+
* Throw a `Notice` to stop a handler and reply to the user. The framework catches it at the controller
|
|
72
|
+
* boundary and renders {@link Notice.render}, which always decides what the user sees. With `report`
|
|
73
|
+
* false that render is all that happens. With `report` true the framework also logs the fault and
|
|
74
|
+
* publishes it to the `handledException` bus. A raw, non-Notice throw shows the generic message.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* import { Notice, BuilderComponent, type RenderContext, type ReplyResponse } from '@seedcord/gateway';
|
|
79
|
+
* import { TextDisplayBuilder } from '@discordjs/builders';
|
|
80
|
+
*
|
|
81
|
+
* // reading `.component` applies the configured bot color to the container accent
|
|
82
|
+
* class TooPoorCard extends BuilderComponent<'container'> {
|
|
83
|
+
* constructor(balance: number) {
|
|
84
|
+
* super('container');
|
|
85
|
+
* this.instance.addTextDisplayComponents(
|
|
86
|
+
* new TextDisplayBuilder().setContent(`### Insufficient balance\nYou need more than ${balance} coins.`)
|
|
87
|
+
* );
|
|
88
|
+
* }
|
|
89
|
+
* }
|
|
90
|
+
*
|
|
91
|
+
* class TooPoor extends Notice {
|
|
92
|
+
* constructor(private readonly balance: number) {
|
|
93
|
+
* super(`balance ${balance} is below the cost`);
|
|
94
|
+
* }
|
|
95
|
+
*
|
|
96
|
+
* render(_ctx: RenderContext): ReplyResponse {
|
|
97
|
+
* return { components: [new TooPoorCard(this.balance).component] };
|
|
98
|
+
* }
|
|
99
|
+
* }
|
|
100
|
+
*
|
|
101
|
+
* // in a handler, throwing stops the handler and replies with render(ctx)
|
|
102
|
+
* if (wallet.balance < cost) throw new TooPoor(wallet.balance);
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
declare abstract class Notice extends Error {
|
|
106
|
+
/**
|
|
107
|
+
* Whether this denial is a reported fault. True also logs it and publishes it to the `handledException`
|
|
108
|
+
* bus. The user always sees {@link Notice.render} either way.
|
|
109
|
+
*
|
|
110
|
+
* @defaultValue `false`
|
|
111
|
+
*/
|
|
112
|
+
report: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Whether the reply is ephemeral, so only the invoking user sees it. Set it false for a refusal the
|
|
115
|
+
* whole channel should see.
|
|
116
|
+
*
|
|
117
|
+
* @defaultValue `true`
|
|
118
|
+
*/
|
|
119
|
+
ephemeral: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* A short one-line reason. When every arm of an `or` gate refuses and each refusal sets this, `or`
|
|
122
|
+
* lists them instead of showing a neutral message.
|
|
123
|
+
*/
|
|
124
|
+
summary?: string;
|
|
125
|
+
protected constructor(message: string, options?: ErrorOptions);
|
|
126
|
+
/**
|
|
127
|
+
* Builds what the user sees. Called fresh each time the denial is shown, so the builders are new
|
|
128
|
+
* and the bot color resolves at render time rather than at construction time.
|
|
129
|
+
*/
|
|
130
|
+
abstract render(ctx: RenderContext): ReplyResponse;
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/subscribers/types/Subscriptions.d.ts
|
|
134
|
+
/**
|
|
135
|
+
* How a dispatch finished. The thrown value's type sets this, wherever it was thrown. A `Silence` and a
|
|
136
|
+
* `Notice` with `report` false are `refused`. A `Notice` with `report` true, which includes a default
|
|
137
|
+
* `Fault`, is `failed`, as is any other throw.
|
|
138
|
+
*/
|
|
139
|
+
type DispatchOutcome = 'handled' | 'refused' | 'failed';
|
|
140
|
+
/** Whether a write through the reply surface reached Discord. */
|
|
141
|
+
type ResponseOutcome = 'sent' | 'failed';
|
|
142
|
+
/**
|
|
143
|
+
* Where a reported fault came from.
|
|
144
|
+
*/
|
|
145
|
+
type FaultSource = InteractionFaultSource | EventFaultSource;
|
|
146
|
+
/**
|
|
147
|
+
* A fault that came from a discord interaction, JSON-safe for a webhook payload.
|
|
148
|
+
*/
|
|
149
|
+
interface InteractionFaultSource {
|
|
150
|
+
kind: 'interaction';
|
|
151
|
+
interactionKind: 'slash' | 'context-menu' | 'button' | 'select' | 'modal';
|
|
152
|
+
/** Slash or context-menu command name, null for component and modal interactions. */
|
|
153
|
+
command: string | null;
|
|
154
|
+
/** Component or modal customId, null for slash and context-menu interactions. */
|
|
155
|
+
customId: string | null;
|
|
156
|
+
userId: string;
|
|
157
|
+
/** Guild the interaction came from, null in DMs. */
|
|
158
|
+
guildId: string | null;
|
|
159
|
+
/** Channel the interaction came from, null when unavailable. */
|
|
160
|
+
channelId: string | null;
|
|
161
|
+
interactionId: string;
|
|
162
|
+
/** The raw interaction, made JSON-safe at serialization with filterCirculars. */
|
|
163
|
+
raw: unknown;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* A fault that came from a client event, JSON-safe for a webhook payload. The actor, guild, and channel
|
|
167
|
+
* are best-effort, derived from the event args, so each is nullable. The raw args carry the full detail.
|
|
168
|
+
*/
|
|
169
|
+
interface EventFaultSource {
|
|
170
|
+
kind: 'event';
|
|
171
|
+
eventName: string;
|
|
172
|
+
handler: string;
|
|
173
|
+
userId: string | null;
|
|
174
|
+
/** Null outside a guild. */
|
|
175
|
+
guildId: string | null;
|
|
176
|
+
channelId: string | null;
|
|
177
|
+
/** The raw event args, made JSON-safe at serialization with filterCirculars. */
|
|
178
|
+
raw: unknown;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The framework's own subscription keys. A transport adds a key whose payload type references a type
|
|
182
|
+
* outside this package, through declaration merging on its own module.
|
|
183
|
+
*
|
|
184
|
+
* Every key here is publish-protected. Subscribe to them. The framework is the
|
|
185
|
+
* only publisher.
|
|
186
|
+
*/
|
|
187
|
+
interface DefaultSubscriptions {
|
|
188
|
+
/** Triggered when an unhandled exception (a raw non-Notice throw) occurs. */
|
|
189
|
+
unknownException: {
|
|
190
|
+
uuid: UUID;
|
|
191
|
+
error: Error;
|
|
192
|
+
guild?: {
|
|
193
|
+
id: string;
|
|
194
|
+
name: string;
|
|
195
|
+
} | undefined;
|
|
196
|
+
user?: {
|
|
197
|
+
id: string;
|
|
198
|
+
username: string;
|
|
199
|
+
} | undefined;
|
|
200
|
+
metadata?: unknown;
|
|
201
|
+
};
|
|
202
|
+
/** Triggered when a reported Notice (`report: true`) is caught. */
|
|
203
|
+
handledException: {
|
|
204
|
+
denial: Notice;
|
|
205
|
+
uuid: UUID;
|
|
206
|
+
source: FaultSource;
|
|
207
|
+
};
|
|
208
|
+
/** Triggered when an interaction dispatch throws past the fault boundary. */
|
|
209
|
+
unhandledInteractionError: {
|
|
210
|
+
error: Error;
|
|
211
|
+
};
|
|
212
|
+
/** Triggered once per interaction dispatch, after the handler chain settles. */
|
|
213
|
+
interactionDispatched: {
|
|
214
|
+
routeId: string; /** The interaction this dispatch ran, for joining against `responseAttempted`. */
|
|
215
|
+
interactionId: string;
|
|
216
|
+
kind: `${InteractionRoutes}`;
|
|
217
|
+
outcome: DispatchOutcome; /** True when no route matched and the unhandled default ran. */
|
|
218
|
+
fallback: boolean; /** Dispatch entry until the user has a response, replies included. A clock change never affects it. */
|
|
219
|
+
durationMs: number;
|
|
220
|
+
/**
|
|
221
|
+
* Discord's interaction creation until dispatch entry, read from the snowflake. This covers
|
|
222
|
+
* network transit plus any time your bot was busy before starting, so a rise points at either
|
|
223
|
+
* Discord or your own backlog. It subtracts a Discord timestamp from the host clock, so a host
|
|
224
|
+
* running behind Discord reports a negative value.
|
|
225
|
+
*/
|
|
226
|
+
queuedMs: number;
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Triggered on every write through the reply surface, several times per interaction. A write that
|
|
230
|
+
* throws reports here too, with `outcome` `failed`. A write that reaches Discord and then returns an
|
|
231
|
+
* unexpected shape publishes nothing, since the callback succeeded and carries no message to name.
|
|
232
|
+
*/
|
|
233
|
+
responseAttempted: {
|
|
234
|
+
routeId: string; /** The interaction this write belongs to, for joining against `interactionDispatched`. */
|
|
235
|
+
interactionId: string; /** `send` routes to another verb, so it reports the verb that ran. */
|
|
236
|
+
method: WriteMethod;
|
|
237
|
+
outcome: ResponseOutcome;
|
|
238
|
+
durationMs: number; /** Null for every ack-only or choice-only verb, and for a failed write. */
|
|
239
|
+
messageId: string | null; /** Present when `outcome` is `failed`. */
|
|
240
|
+
error?: Error;
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Custom subscribers defined by the application. Augment it via declaration merging to type your
|
|
245
|
+
* own subscriber payloads.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* // the transport package your bot installs, '@seedcord/gateway' or '@seedcord/http'
|
|
250
|
+
* declare module '@seedcord/gateway' {
|
|
251
|
+
* interface Subscriptions {
|
|
252
|
+
* 'userJoin': { userId: string; guildId: string };
|
|
253
|
+
* 'levelUp': { userId: string; level: number };
|
|
254
|
+
* }
|
|
255
|
+
* }
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
interface Subscriptions {}
|
|
259
|
+
interface AllSubscriptions extends DefaultSubscriptions, Subscriptions {}
|
|
260
|
+
/** All subscriber event names available to subscribe to and augment. */
|
|
261
|
+
type SubscriptionKey = keyof AllSubscriptions;
|
|
262
|
+
/**
|
|
263
|
+
* The keys `publish` accepts. The framework's own keys are excluded, since only the framework
|
|
264
|
+
* produces them.
|
|
265
|
+
*/
|
|
266
|
+
type PublishableKey = TypedExclude<SubscriptionKey, keyof DefaultSubscriptions>;
|
|
267
|
+
/**
|
|
268
|
+
* The payload type for a subscriber event.
|
|
269
|
+
*
|
|
270
|
+
* @typeParam KeyOfSubscribers - The subscriber event name
|
|
271
|
+
*/
|
|
272
|
+
type SubscriptionData<KeyOfSubscribers extends SubscriptionKey> = AllSubscriptions[KeyOfSubscribers];
|
|
273
|
+
type SubscriptionTuples = { [K in SubscriptionKey]: [AllSubscriptions[K]] };
|
|
274
|
+
//#endregion
|
|
275
|
+
//#region src/subscribers/Subscriber.d.ts
|
|
276
|
+
/**
|
|
277
|
+
* Abstract base class for handling application subscribers.
|
|
278
|
+
*
|
|
279
|
+
* Provides type-safe access to subscribers data and the core framework instance.
|
|
280
|
+
* Extend this class to create custom subscriber handlers.
|
|
281
|
+
*
|
|
282
|
+
* @typeParam KeyOfSubscribers - The specific subscriber type this handler processes
|
|
283
|
+
* @typeParam TCore - The transport's Core
|
|
284
|
+
*/
|
|
285
|
+
declare abstract class Subscriber<KeyOfSubscribers extends SubscriptionKey, TCore extends CoreBase> {
|
|
286
|
+
protected readonly data: AllSubscriptions[KeyOfSubscribers];
|
|
287
|
+
protected readonly core: TCore;
|
|
288
|
+
protected readonly logger: Logger;
|
|
289
|
+
/**
|
|
290
|
+
* Instantiates the subscriber with event data and core.
|
|
291
|
+
*
|
|
292
|
+
* @param data - The subscriber event data
|
|
293
|
+
* @param core - The core framework instance
|
|
294
|
+
*/
|
|
295
|
+
constructor(data: AllSubscriptions[KeyOfSubscribers], core: TCore);
|
|
296
|
+
/**
|
|
297
|
+
* Executes the subscriber handler logic.
|
|
298
|
+
*/
|
|
299
|
+
abstract execute(): Promise<void>;
|
|
300
|
+
}
|
|
301
|
+
//#endregion
|
|
302
|
+
//#region src/subscribers/Bus.d.ts
|
|
303
|
+
/**
|
|
304
|
+
* A subscriber class as the Bus stores it. Both parameters are `never` because construct-signature
|
|
305
|
+
* parameters check contravariantly, and a concrete subscriber narrows each to its own payload and
|
|
306
|
+
* transport `Core`. One cast at dispatch restores both.
|
|
307
|
+
*
|
|
308
|
+
* @internal
|
|
309
|
+
*/
|
|
310
|
+
type StoredSubscriberCtor = new (data: never, core: never) => Subscriber<SubscriptionKey, CoreBase>;
|
|
311
|
+
/**
|
|
312
|
+
* One registration. `resolve` is lazy so an edge host registers from a manifest row without
|
|
313
|
+
* importing the module until that key first publishes.
|
|
314
|
+
*
|
|
315
|
+
* @internal
|
|
316
|
+
*/
|
|
317
|
+
interface SubscriberRegistration {
|
|
318
|
+
readonly keys: readonly SubscriptionKey[];
|
|
319
|
+
readonly frequency: EventFrequency;
|
|
320
|
+
readonly resolve: () => StoredSubscriberCtor | Promise<StoredSubscriberCtor>;
|
|
321
|
+
/** The resolved class. A server-host registration carries it from the start, a lazy one fills it on first resolve. */
|
|
322
|
+
ctor?: StoredSubscriberCtor | undefined;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Registers and dispatches subscribers. Subscribers run on a programmatic or framework publish.
|
|
326
|
+
* Accessed via `core.bus`. Do not construct it directly.
|
|
327
|
+
*/
|
|
328
|
+
declare class Bus extends TypedEventEmitter<SubscriptionTuples> {
|
|
329
|
+
protected core: CoreBase;
|
|
330
|
+
/** @internal */
|
|
331
|
+
readonly logger: Logger;
|
|
332
|
+
private readonly subscribersMap;
|
|
333
|
+
private readonly executedOnce;
|
|
334
|
+
private readonly webhookProbes;
|
|
335
|
+
constructor(core: CoreBase);
|
|
336
|
+
/**
|
|
337
|
+
* Registers the two shipped webhook reporters. The host calls this during startup, keeping the
|
|
338
|
+
* url validation inside `start()` where a failure can still release the singleton guard.
|
|
339
|
+
*
|
|
340
|
+
* @internal
|
|
341
|
+
*/
|
|
342
|
+
registerDefaults(): void;
|
|
343
|
+
/** @internal */
|
|
344
|
+
register(registration: SubscriberRegistration): void;
|
|
345
|
+
/** @internal */
|
|
346
|
+
unregister(ctor: StoredSubscriberCtor, keys?: readonly SubscriptionKey[]): void;
|
|
347
|
+
/** @internal */
|
|
348
|
+
get registeredCount(): number;
|
|
349
|
+
/** @internal Only eagerly registered reporters are probed, which is every server-host one. */
|
|
350
|
+
verifyWebhooks(): Promise<void>;
|
|
351
|
+
/** Returns whether the class should register. */
|
|
352
|
+
private probeWebhook;
|
|
353
|
+
/**
|
|
354
|
+
* Publishes an event to its subscribers and native listeners.
|
|
355
|
+
*
|
|
356
|
+
* Fire-and-forget, so subscriber handlers run asynchronously and this returns before they
|
|
357
|
+
* complete, and callers must not assume side effects have completed. Errors thrown by a subscriber
|
|
358
|
+
* or by an `on()` listener are caught and logged, never surfaced here. One throwing listener
|
|
359
|
+
* does not stop the others. Subscribers on one key run concurrently and carry no ordering
|
|
360
|
+
* guarantee. A `'once'` subscriber is marked as executed when it starts (even if it throws), so
|
|
361
|
+
* it never runs twice.
|
|
362
|
+
*
|
|
363
|
+
* The framework's own keys are excluded. Subscribe to those and listen with `on`. The framework
|
|
364
|
+
* is their only publisher.
|
|
365
|
+
*
|
|
366
|
+
* @param event - The subscription key to publish
|
|
367
|
+
* @param data - Payload passed to each subscriber
|
|
368
|
+
* @returns Whether the underlying emitter had native listeners for the event
|
|
369
|
+
*/
|
|
370
|
+
publish<KeyOfSubscribers extends PublishableKey>(event: KeyOfSubscribers, data: AllSubscriptions[KeyOfSubscribers]): boolean;
|
|
371
|
+
/** @internal the framework's own publish path, keyed by a symbol no public entry exports */
|
|
372
|
+
[PublishDefault]<KeyOfSubscribers extends keyof DefaultSubscriptions>(event: KeyOfSubscribers, data: AllSubscriptions[KeyOfSubscribers]): boolean;
|
|
373
|
+
private dispatchKey;
|
|
374
|
+
protected onListenerError(error: unknown, event: string | symbol): void;
|
|
375
|
+
private processSubscriber;
|
|
376
|
+
private runSubscriber;
|
|
377
|
+
}
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/interfaces/CoreBase.d.ts
|
|
380
|
+
/**
|
|
381
|
+
* The transport-agnostic slice of the running framework, what core code (gates, notices, plugins'
|
|
382
|
+
* agnostic surface) accesses through `ctx.core`. Each transport's `Core` extends it with its own
|
|
383
|
+
* members.
|
|
384
|
+
*/
|
|
385
|
+
interface CoreBase {
|
|
386
|
+
/** Transports narrow this to their own config type. */
|
|
387
|
+
readonly config: Config;
|
|
388
|
+
/**
|
|
389
|
+
* Discord REST client. On gateway this is the discord.js client's own, and it carries no token
|
|
390
|
+
* until the Login phase. On http the token is set by `start()`.
|
|
391
|
+
*/
|
|
392
|
+
readonly rest: REST;
|
|
393
|
+
/** Sliding-window rate limiting, the `Cooldown` gate's backend and a public direct-use API. */
|
|
394
|
+
readonly rateLimiter: IRateLimiter;
|
|
395
|
+
/** Publish and subscribe to framework and application events. */
|
|
396
|
+
readonly bus: Bus;
|
|
397
|
+
}
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region src/lifecycle/phases.d.ts
|
|
400
|
+
/**
|
|
401
|
+
* Startup phases, in run order.
|
|
402
|
+
*/
|
|
403
|
+
declare enum StartupPhase {
|
|
404
|
+
/** Nothing connected. Plugin init runs here by default. */
|
|
405
|
+
Configuration = 1,
|
|
406
|
+
/** The gateway session opens, and http runs its REST-only startup work. */
|
|
407
|
+
Login = 2,
|
|
408
|
+
/** Handlers are live and interactions dispatch. */
|
|
409
|
+
Ready = 3
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Shutdown phases, in run order.
|
|
413
|
+
*/
|
|
414
|
+
declare enum ShutdownPhase {
|
|
415
|
+
/** Stop taking new interactions and requests. */
|
|
416
|
+
Unbind = 1,
|
|
417
|
+
/** Waits for in-flight work to settle and stops internal services. */
|
|
418
|
+
Drain = 2,
|
|
419
|
+
/** External resources close. Plugin dispose runs here by default. */
|
|
420
|
+
Disconnect = 3,
|
|
421
|
+
/** The client disconnects last so in-flight handlers can still reference it during drain. */
|
|
422
|
+
Logout = 4
|
|
423
|
+
}
|
|
424
|
+
//#endregion
|
|
425
|
+
//#region src/plugin/lifecycle.d.ts
|
|
426
|
+
/**
|
|
427
|
+
* The lifecycle a plugin declares through `super(host, spec)`. Unset fields resolve to the base
|
|
428
|
+
* defaults. `ready` runs at a fixed `StartupPhase.Ready`, so it takes only a timeout.
|
|
429
|
+
*/
|
|
430
|
+
interface PluginLifecycleSpec {
|
|
431
|
+
init?: {
|
|
432
|
+
phase?: StartupPhase;
|
|
433
|
+
timeout?: number;
|
|
434
|
+
};
|
|
435
|
+
ready?: {
|
|
436
|
+
timeout?: number;
|
|
437
|
+
};
|
|
438
|
+
dispose?: {
|
|
439
|
+
phase?: ShutdownPhase;
|
|
440
|
+
timeout?: number;
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
/** @internal */
|
|
444
|
+
interface ResolvedPluginLifecycleSpec {
|
|
445
|
+
readonly init: {
|
|
446
|
+
readonly phase: StartupPhase;
|
|
447
|
+
readonly timeout: number;
|
|
448
|
+
};
|
|
449
|
+
readonly ready: {
|
|
450
|
+
readonly timeout: number;
|
|
451
|
+
};
|
|
452
|
+
readonly dispose: {
|
|
453
|
+
readonly phase: ShutdownPhase;
|
|
454
|
+
readonly timeout: number;
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/plugin/options.d.ts
|
|
459
|
+
/** The options a plugin declares as its `Plugin<Opts>` type argument. */
|
|
460
|
+
interface PluginOptions {
|
|
461
|
+
/**
|
|
462
|
+
* Which transport the plugin runs on. `'any'` attaches to either one.
|
|
463
|
+
* @defaultValue 'any'
|
|
464
|
+
*/
|
|
465
|
+
transport?: 'gateway' | 'http' | 'any';
|
|
466
|
+
/**
|
|
467
|
+
* Which runtime the plugin runs on. `'any'` attaches to either one.
|
|
468
|
+
* @defaultValue 'any'
|
|
469
|
+
*/
|
|
470
|
+
runtime?: 'server' | 'edge' | 'any';
|
|
471
|
+
}
|
|
472
|
+
/** @internal */
|
|
473
|
+
type TransportOf<Opts extends PluginOptions> = undefined extends Opts['transport'] ? 'any' : NonNullable<Opts['transport']>;
|
|
474
|
+
/** @internal */
|
|
475
|
+
type RuntimeOf<Opts extends PluginOptions> = undefined extends Opts['runtime'] ? 'any' : NonNullable<Opts['runtime']>;
|
|
476
|
+
/** @internal the transport a bot runs */
|
|
477
|
+
type Transport = TypedExclude<NonNullable<PluginOptions['transport']>, 'any'>;
|
|
478
|
+
/** @internal the runtime a bot runs */
|
|
479
|
+
type Runtime = TypedExclude<NonNullable<PluginOptions['runtime']>, 'any'>;
|
|
480
|
+
type TransportMismatch<PluginT extends string, BotT extends string> = Record<`this plugin declares transport '${PluginT}' but this bot runs '${BotT}'`, never>;
|
|
481
|
+
type RuntimeMismatch<PluginRt extends string, BotRt extends string> = Record<`this plugin declares runtime '${PluginRt}' but this bot runs '${BotRt}'`, never>;
|
|
482
|
+
type EdgePluginsUnsupported = Record<'an edge bot takes no plugins until edge support ships after v1', never>;
|
|
483
|
+
/** @internal */
|
|
484
|
+
type ChannelKeyAssert<Key extends string> = Key extends FrameworkChannel ? `'${Key}' is a channel the framework logs on. Pick another plugin key.` : Key;
|
|
485
|
+
type BrandTransport<Plug> = Plug extends {
|
|
486
|
+
readonly __transport?: infer T extends string;
|
|
487
|
+
} ? T : 'any';
|
|
488
|
+
type BrandRuntime<Plug> = Plug extends {
|
|
489
|
+
readonly __runtime?: infer R extends string;
|
|
490
|
+
} ? R : 'any';
|
|
491
|
+
/** @internal */
|
|
492
|
+
type TransportAssert<Plug, BotT extends Transport> = BrandTransport<Plug> extends 'any' | BotT ? unknown : TransportMismatch<BrandTransport<Plug>, BotT>;
|
|
493
|
+
/** @internal */
|
|
494
|
+
type RuntimeAssert<Plug, BotRt extends Runtime> = 'edge' extends BotRt ? EdgePluginsUnsupported : BrandRuntime<Plug> extends 'any' | BotRt ? unknown : RuntimeMismatch<BrandRuntime<Plug>, BotRt>;
|
|
495
|
+
//#endregion
|
|
496
|
+
//#region src/plugin/Plugin.d.ts
|
|
497
|
+
interface Initializeable {
|
|
498
|
+
init(): Promise<void>;
|
|
499
|
+
}
|
|
500
|
+
/** @internal */
|
|
501
|
+
declare const resolvedSpecSlot: unique symbol;
|
|
502
|
+
/**
|
|
503
|
+
* Base class for Seedcord plugins. A subclass declares its options as the `Opts` type argument,
|
|
504
|
+
* implements `init()`, and reads the framework surface through `this.core`.
|
|
505
|
+
*
|
|
506
|
+
* @typeParam Opts - The plugin's declared {@link PluginOptions}.
|
|
507
|
+
* @typeParam TCore - The host type `this.core` resolves to. Each transport binds it to its own `Core`.
|
|
508
|
+
*/
|
|
509
|
+
declare abstract class Plugin<Opts extends PluginOptions = {}, TCore extends CoreBase = CoreBase> implements Initializeable, HmrAware {
|
|
510
|
+
private readonly host;
|
|
511
|
+
/** @internal phantom, never set at runtime */
|
|
512
|
+
readonly __transport?: TransportOf<Opts>;
|
|
513
|
+
/** @internal phantom, never set at runtime */
|
|
514
|
+
readonly __runtime?: RuntimeOf<Opts>;
|
|
515
|
+
/** @internal */
|
|
516
|
+
readonly [resolvedSpecSlot]: ResolvedPluginLifecycleSpec;
|
|
517
|
+
/** Logs under the plugin's class name, on the channel its attach key sets. */
|
|
518
|
+
readonly logger: Logger;
|
|
519
|
+
constructor(host: CoreBase, spec?: PluginLifecycleSpec);
|
|
520
|
+
/** The host, typed to the transport whose `Plugin` base this class extends. */
|
|
521
|
+
protected get core(): TCore;
|
|
522
|
+
abstract init(): Promise<void>;
|
|
523
|
+
/**
|
|
524
|
+
* Runs in the startup Ready phase, after `init()`. The gateway client is logged in by this phase,
|
|
525
|
+
* and the http host runs its server-bind and health tasks in the same phase.
|
|
526
|
+
*/
|
|
527
|
+
ready?(): Promise<void>;
|
|
528
|
+
/** Runs during teardown, only when `init()` resolved. */
|
|
529
|
+
dispose?(): Promise<void>;
|
|
530
|
+
/**
|
|
531
|
+
* Throws a `SeedcordError` that contains the plugin class name and the reason.
|
|
532
|
+
*
|
|
533
|
+
* @param reason - Why the options are invalid.
|
|
534
|
+
*/
|
|
535
|
+
protected rejectOptions(reason: string): never;
|
|
536
|
+
/** Override to reload plugin state on an HMR update. */
|
|
537
|
+
onHmr(_event: HmrUpdateEvent): Promise<void>;
|
|
538
|
+
/**
|
|
539
|
+
* Registers critical file patterns that should trigger a full restart when changed in Dev HMR.
|
|
540
|
+
* @param patterns - Glob patterns relative to the project root
|
|
541
|
+
*/
|
|
542
|
+
protected registerCriticalFiles(patterns: string[]): void;
|
|
543
|
+
}
|
|
544
|
+
/** @internal */
|
|
545
|
+
type PluginLike = Pick<Plugin, 'init' | 'ready' | 'dispose' | 'logger' | 'onHmr' | typeof resolvedSpecSlot>;
|
|
546
|
+
/** @internal */
|
|
547
|
+
type PluginCtor<TPlugin extends PluginLike = PluginLike> = new (...args: any[]) => TPlugin;
|
|
548
|
+
/** @internal */
|
|
549
|
+
type PluginArgs<Ctor extends PluginCtor> = Tail<ConstructorParameters<Ctor>>;
|
|
550
|
+
type CoreParamTooNarrow = Record<'this plugin constructor must take CoreBase as its first parameter and read the transport Core off this.core', never>;
|
|
551
|
+
/** @internal */
|
|
552
|
+
type CoreParamAssert<Ctor extends PluginCtor> = CoreBase extends ConstructorParameters<Ctor>[0] ? unknown : CoreParamTooNarrow;
|
|
553
|
+
//#endregion
|
|
554
|
+
export { AckState as A, InteractionRouteKeys as B, FaultSource as C, Notice as D, Subscriptions as E, PublishDefault as F, MiddlewareMetadataKey as H, CommandMetadataKey as I, EventMetadataKey as L, checkAckLegality as M, sendTarget as N, ReplyTelemetry as O, AckTrace as P, GatedMetadataKey as R, DispatchOutcome as S, SubscriptionKey as T, SubscribeMetadataKey as U, InteractionRoutes as V, WebhookUrlMetadataKey as W, Bus as _, PluginCtor as a, Subscriber as b, PluginOptions as c, Transport as d, TransportAssert as f, CoreBase as g, StartupPhase as h, PluginArgs as i, ReplyMethod as j, reportedWrite as k, Runtime as l, ShutdownPhase as m, Initializeable as n, PluginLike as o, PluginLifecycleSpec as p, Plugin as r, ChannelKeyAssert as s, CoreParamAssert as t, RuntimeAssert as u, StoredSubscriberCtor as v, SubscriptionData as w, DefaultSubscriptions as x, SubscriberRegistration as y, InteractionMetadataKey as z };
|
|
555
|
+
//# sourceMappingURL=Plugin-BZit5HUI.d.mts.map
|
|
@@ -1,23 +1,9 @@
|
|
|
1
|
-
const require_phases = require('./phases-
|
|
1
|
+
const require_phases = require('./phases-DcuDHoQl.cjs');
|
|
2
2
|
const require_devChannel = require('./devChannel-CoCmAVDx.cjs');
|
|
3
|
+
let _seedcord_logger = require("@seedcord/logger");
|
|
3
4
|
let _seedcord_errors = require("@seedcord/errors");
|
|
4
5
|
let _seedcord_errors_internal = require("@seedcord/errors/internal");
|
|
5
6
|
|
|
6
|
-
//#region src/plugin/context.ts
|
|
7
|
-
/** @internal unset until attach installs the built ctx */
|
|
8
|
-
const pluginContextSlot = Symbol("seedcord.plugin.ctx");
|
|
9
|
-
/** @internal */
|
|
10
|
-
function readPluginContext(holder) {
|
|
11
|
-
const ctx = holder[pluginContextSlot];
|
|
12
|
-
if (!ctx) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.PluginContextBeforeInit, [holder.constructor.name]);
|
|
13
|
-
return ctx;
|
|
14
|
-
}
|
|
15
|
-
/** @internal */
|
|
16
|
-
function finalizePluginContext(holder, ctx) {
|
|
17
|
-
holder[pluginContextSlot] = ctx;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
//#endregion
|
|
21
7
|
//#region src/plugin/lifecycle.ts
|
|
22
8
|
const DEFAULT_INIT_TIMEOUT_MS = 15e3;
|
|
23
9
|
const DEFAULT_READY_TIMEOUT_MS = 15e3;
|
|
@@ -48,27 +34,28 @@ function resolveLifecycleSpec(spec, pluginName) {
|
|
|
48
34
|
const resolvedSpecSlot = Symbol("seedcord.plugin.spec");
|
|
49
35
|
/**
|
|
50
36
|
* Base class for Seedcord plugins. A subclass declares its options as the `Opts` type argument,
|
|
51
|
-
* implements `init()`, and reads the framework surface through `this.
|
|
37
|
+
* implements `init()`, and reads the framework surface through `this.core`.
|
|
52
38
|
*
|
|
53
39
|
* @typeParam Opts - The plugin's declared {@link PluginOptions}.
|
|
40
|
+
* @typeParam TCore - The host type `this.core` resolves to. Each transport binds it to its own `Core`.
|
|
54
41
|
*/
|
|
55
42
|
var Plugin = class {
|
|
56
|
-
|
|
43
|
+
host;
|
|
57
44
|
/** @internal */
|
|
58
45
|
[resolvedSpecSlot];
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
constructor(
|
|
62
|
-
this.
|
|
46
|
+
/** Logs under the plugin's class name, on the channel its attach key sets. */
|
|
47
|
+
logger;
|
|
48
|
+
constructor(host, spec) {
|
|
49
|
+
this.host = host;
|
|
50
|
+
this.logger = new _seedcord_logger.Logger(this.constructor.name);
|
|
63
51
|
this[resolvedSpecSlot] = resolveLifecycleSpec(spec, this.constructor.name);
|
|
64
52
|
}
|
|
65
|
-
/** The
|
|
66
|
-
get
|
|
67
|
-
return
|
|
53
|
+
/** The host, typed to the transport whose `Plugin` base this class extends. */
|
|
54
|
+
get core() {
|
|
55
|
+
return this.host;
|
|
68
56
|
}
|
|
69
57
|
/**
|
|
70
|
-
*
|
|
71
|
-
* plugin class and the reason.
|
|
58
|
+
* Throws a `SeedcordError` that contains the plugin class name and the reason.
|
|
72
59
|
*
|
|
73
60
|
* @param reason - Why the options are invalid.
|
|
74
61
|
*/
|
|
@@ -99,16 +86,10 @@ Object.defineProperty(exports, 'Plugin', {
|
|
|
99
86
|
return Plugin;
|
|
100
87
|
}
|
|
101
88
|
});
|
|
102
|
-
Object.defineProperty(exports, 'finalizePluginContext', {
|
|
103
|
-
enumerable: true,
|
|
104
|
-
get: function () {
|
|
105
|
-
return finalizePluginContext;
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
89
|
Object.defineProperty(exports, 'resolvedLifecycleSpecOf', {
|
|
109
90
|
enumerable: true,
|
|
110
91
|
get: function () {
|
|
111
92
|
return resolvedLifecycleSpecOf;
|
|
112
93
|
}
|
|
113
94
|
});
|
|
114
|
-
//# sourceMappingURL=Plugin-
|
|
95
|
+
//# sourceMappingURL=Plugin-BlkhHNRm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Plugin-BlkhHNRm.cjs","names":["SeedcordError","SeedcordErrorCode","Logger","SeedcordError","SeedcordErrorCode"],"sources":["../src/plugin/lifecycle.ts","../src/plugin/Plugin.ts"],"sourcesContent":["import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { ShutdownPhase, StartupPhase } from '@src/lifecycle/phases';\n\n/**\n * The lifecycle a plugin declares through `super(host, spec)`. Unset fields resolve to the base\n * defaults. `ready` runs at a fixed `StartupPhase.Ready`, so it takes only a timeout.\n */\nexport interface PluginLifecycleSpec {\n init?: { phase?: StartupPhase; timeout?: number };\n ready?: { timeout?: number };\n dispose?: { phase?: ShutdownPhase; timeout?: number };\n}\n\n/** @internal */\nexport interface ResolvedPluginLifecycleSpec {\n readonly init: { readonly phase: StartupPhase; readonly timeout: number };\n readonly ready: { readonly timeout: number };\n readonly dispose: { readonly phase: ShutdownPhase; readonly timeout: number };\n}\n\nconst DEFAULT_INIT_TIMEOUT_MS = 15_000;\nconst DEFAULT_READY_TIMEOUT_MS = 15_000;\nconst DEFAULT_DISPOSE_TIMEOUT_MS = 10_000;\n\nfunction timeoutOf(\n field: 'init' | 'ready' | 'dispose',\n declared: number | undefined,\n fallback: number,\n pluginName: string\n): number {\n if (declared === undefined) return fallback;\n if (!Number.isFinite(declared) || declared <= 0) {\n throw new SeedcordError(SeedcordErrorCode.PluginOptionsRejected, [\n pluginName,\n `lifecycle ${field}.timeout must be a positive number of milliseconds`\n ]);\n }\n return declared;\n}\n\n/** @internal */\nexport function resolveLifecycleSpec(\n spec: PluginLifecycleSpec | undefined,\n pluginName: string\n): ResolvedPluginLifecycleSpec {\n return {\n init: {\n phase: spec?.init?.phase ?? StartupPhase.Configuration,\n timeout: timeoutOf('init', spec?.init?.timeout, DEFAULT_INIT_TIMEOUT_MS, pluginName)\n },\n ready: {\n timeout: timeoutOf('ready', spec?.ready?.timeout, DEFAULT_READY_TIMEOUT_MS, pluginName)\n },\n dispose: {\n phase: spec?.dispose?.phase ?? ShutdownPhase.Disconnect,\n timeout: timeoutOf('dispose', spec?.dispose?.timeout, DEFAULT_DISPOSE_TIMEOUT_MS, pluginName)\n }\n };\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\n\nimport { getDevChannel } from '@hmr/devChannel';\n\nimport { resolveLifecycleSpec } from './lifecycle';\n\nimport type { ResolvedPluginLifecycleSpec, PluginLifecycleSpec } from './lifecycle';\nimport type { TransportOf, PluginOptions, RuntimeOf } from './options';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { Tail, HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\nexport interface Initializeable {\n init(): Promise<void>;\n}\n\n/** @internal */\nconst resolvedSpecSlot = Symbol('seedcord.plugin.spec');\n\n/**\n * Base class for Seedcord plugins. A subclass declares its options as the `Opts` type argument,\n * implements `init()`, and reads the framework surface through `this.core`.\n *\n * @typeParam Opts - The plugin's declared {@link PluginOptions}.\n * @typeParam TCore - The host type `this.core` resolves to. Each transport binds it to its own `Core`.\n */\nexport abstract class Plugin<Opts extends PluginOptions = {}, TCore extends CoreBase = CoreBase>\n implements Initializeable, HmrAware\n{\n /** @internal phantom, never set at runtime */\n declare readonly __transport?: TransportOf<Opts>;\n /** @internal phantom, never set at runtime */\n declare readonly __runtime?: RuntimeOf<Opts>;\n\n /** @internal */\n readonly [resolvedSpecSlot]: ResolvedPluginLifecycleSpec;\n\n /** Logs under the plugin's class name, on the channel its attach key sets. */\n public readonly logger: Logger;\n\n // CoreBase here keeps the augmented Core out of ConstructorParameters, which attach reads\n constructor(\n private readonly host: CoreBase,\n spec?: PluginLifecycleSpec\n ) {\n this.logger = new Logger(this.constructor.name);\n this[resolvedSpecSlot] = resolveLifecycleSpec(spec, this.constructor.name);\n }\n\n /** The host, typed to the transport whose `Plugin` base this class extends. */\n protected get core(): TCore {\n // justified: each transport binds TCore to the Core its own host satisfies\n return this.host as TCore;\n }\n\n abstract init(): Promise<void>;\n\n /**\n * Runs in the startup Ready phase, after `init()`. The gateway client is logged in by this phase,\n * and the http host runs its server-bind and health tasks in the same phase.\n */\n ready?(): Promise<void>;\n\n /** Runs during teardown, only when `init()` resolved. */\n dispose?(): Promise<void>;\n\n /**\n * Throws a `SeedcordError` that contains the plugin class name and the reason.\n *\n * @param reason - Why the options are invalid.\n */\n protected rejectOptions(reason: string): never {\n throw new SeedcordError(SeedcordErrorCode.PluginOptionsRejected, [this.constructor.name, reason]);\n }\n\n /** Override to reload plugin state on an HMR update. */\n public onHmr(_event: HmrUpdateEvent): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Registers critical file patterns that should trigger a full restart when changed in Dev HMR.\n * @param patterns - Glob patterns relative to the project root\n */\n protected registerCriticalFiles(patterns: string[]): void {\n getDevChannel()?.send('seedcord:register-critical-files', { patterns });\n }\n}\n\n/** @internal */\nexport function resolvedLifecycleSpecOf(plugin: PluginLike): ResolvedPluginLifecycleSpec {\n return plugin[resolvedSpecSlot];\n}\n\nexport type { PluginLifecycleSpec } from './lifecycle';\nexport type { PluginOptions } from './options';\n\n// a bound of Plugin<{}> rejects every plugin that declares an option, since Plugin<A> and Plugin<B>\n// are mutually unassignable. every member here must stay Opts-independent.\n/** @internal */\nexport type PluginLike = Pick<Plugin, 'init' | 'ready' | 'dispose' | 'logger' | 'onHmr' | typeof resolvedSpecSlot>;\n\n// a `CoreBase` first parameter rejects a narrowing ctor on its own, and it also collapses\n// `InstanceType<Ctor>` to `PluginLike` at every attach call. CoreParamAssert checks it instead.\n/** @internal */\nexport type PluginCtor<TPlugin extends PluginLike = PluginLike> = new (...args: any[]) => TPlugin;\n\n/** @internal */\nexport type PluginArgs<Ctor extends PluginCtor> = Tail<ConstructorParameters<Ctor>>;\n\ntype CoreParamTooNarrow = Record<\n 'this plugin constructor must take CoreBase as its first parameter and read the transport Core off this.core',\n never\n>;\n\n// a narrowed first parameter fails `CoreBase extends Param`\n/** @internal */\nexport type CoreParamAssert<Ctor extends PluginCtor> = CoreBase extends ConstructorParameters<Ctor>[0]\n ? unknown\n : CoreParamTooNarrow;\n"],"mappings":";;;;;;;AAsBA,MAAM,0BAA0B;AAChC,MAAM,2BAA2B;AACjC,MAAM,6BAA6B;AAEnC,SAAS,UACL,OACA,UACA,UACA,YACM;CACN,IAAI,aAAa,QAAW,OAAO;CACnC,IAAI,CAAC,OAAO,SAAS,QAAQ,KAAK,YAAY,GAC1C,MAAM,IAAIA,wCAAcC,mCAAkB,uBAAuB,CAC7D,YACA,aAAa,MAAM,mDACvB,CAAC;CAEL,OAAO;AACX;;AAGA,SAAgB,qBACZ,MACA,YAC2B;CAC3B,OAAO;EACH,MAAM;GACF,OAAO,MAAM,MAAM;GACnB,SAAS,UAAU,QAAQ,MAAM,MAAM,SAAS,yBAAyB,UAAU;EACvF;EACA,OAAO,EACH,SAAS,UAAU,SAAS,MAAM,OAAO,SAAS,0BAA0B,UAAU,EAC1F;EACA,SAAS;GACL,OAAO,MAAM,SAAS;GACtB,SAAS,UAAU,WAAW,MAAM,SAAS,SAAS,4BAA4B,UAAU;EAChG;CACJ;AACJ;;;;;AC1CA,MAAM,mBAAmB,OAAO,sBAAsB;;;;;;;;AAStD,IAAsB,SAAtB,MAEA;CAcyB;;CAPrB,CAAU;;CAGV,AAAgB;CAGhB,YACI,AAAiB,MACjB,MACF;EAFmB;EAGjB,KAAK,SAAS,IAAIC,wBAAO,KAAK,YAAY,IAAI;EAC9C,KAAK,oBAAoB,qBAAqB,MAAM,KAAK,YAAY,IAAI;CAC7E;;CAGA,IAAc,OAAc;EAExB,OAAO,KAAK;CAChB;;;;;;CAkBA,AAAU,cAAc,QAAuB;EAC3C,MAAM,IAAIC,wCAAcC,mCAAkB,uBAAuB,CAAC,KAAK,YAAY,MAAM,MAAM,CAAC;CACpG;;CAGA,AAAO,MAAM,QAAuC;EAChD,OAAO,QAAQ,QAAQ;CAC3B;;;;;CAMA,AAAU,sBAAsB,UAA0B;EACtD,iCAAc,CAAC,EAAE,KAAK,oCAAoC,EAAE,SAAS,CAAC;CAC1E;AACJ;;AAGA,SAAgB,wBAAwB,QAAiD;CACrF,OAAO,OAAO;AAClB"}
|