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