shardwire 1.1.0 → 1.2.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 CHANGED
@@ -108,8 +108,14 @@ Apps subscribe to events with `app.on(...)`. The bridge forwards only what each
108
108
  - `messageDelete`
109
109
  - `messageReactionAdd`
110
110
  - `messageReactionRemove`
111
+ - `guildCreate`
112
+ - `guildDelete`
111
113
  - `guildMemberAdd`
112
114
  - `guildMemberRemove`
115
+ - `guildMemberUpdate`
116
+ - `threadCreate`
117
+ - `threadUpdate`
118
+ - `threadDelete`
113
119
 
114
120
  Supported filters:
115
121
 
@@ -117,13 +123,16 @@ Supported filters:
117
123
  - `channelId`
118
124
  - `userId`
119
125
  - `commandName` (for `interactionCreate`)
126
+ - `customId` (for `interactionCreate`)
127
+ - `interactionKind` (for `interactionCreate`)
120
128
 
121
129
  ### Intent Notes
122
130
 
123
131
  - `ready` and `interactionCreate`: no specific event intent requirement
124
132
  - `messageCreate`, `messageUpdate`, `messageDelete`: `GuildMessages`
125
133
  - `messageReactionAdd`, `messageReactionRemove`: `GuildMessageReactions`
126
- - `guildMemberAdd`, `guildMemberRemove`: `GuildMembers`
134
+ - `guildCreate`, `guildDelete`, `threadCreate`, `threadUpdate`, `threadDelete`: `Guilds`
135
+ - `guildMemberAdd`, `guildMemberRemove`, `guildMemberUpdate`: `GuildMembers`
127
136
 
128
137
  ## Built-In Actions
129
138
 
@@ -134,7 +143,14 @@ Supported filters:
134
143
  - `deleteMessage`
135
144
  - `replyToInteraction`
136
145
  - `deferInteraction`
146
+ - `deferUpdateInteraction`
137
147
  - `followUpInteraction`
148
+ - `editInteractionReply`
149
+ - `deleteInteractionReply`
150
+ - `updateInteraction`
151
+ - `showModal`
152
+ - `fetchMessage`
153
+ - `fetchMember`
138
154
  - `banMember`
139
155
  - `kickMember`
140
156
  - `addMemberRole`
@@ -150,6 +166,31 @@ type ActionResult<T> =
150
166
  | { ok: false; requestId: string; ts: number; error: { code: string; message: string; details?: unknown } };
151
167
  ```
152
168
 
169
+ ### Idempotency for safe retries
170
+
171
+ You can provide an `idempotencyKey` in action options to dedupe repeated requests on the same connection:
172
+
173
+ ```ts
174
+ await app.actions.sendMessage(
175
+ { channelId: "123456789012345678", content: "Hello once" },
176
+ { idempotencyKey: "notify:order:123" },
177
+ );
178
+ ```
179
+
180
+ ### App-side action metrics
181
+
182
+ ```ts
183
+ const app = connectBotBridge({
184
+ url: "ws://127.0.0.1:3001/shardwire",
185
+ secret: process.env.SHARDWIRE_SECRET!,
186
+ metrics: {
187
+ onActionComplete(meta) {
188
+ console.log(meta.name, meta.durationMs, meta.ok, meta.errorCode);
189
+ },
190
+ },
191
+ });
192
+ ```
193
+
153
194
  ## Secret Scopes
154
195
 
155
196
  Use a plain string secret for full event/action access:
@@ -225,6 +266,7 @@ Main exports include:
225
266
  - Use `wss://` for non-loopback deployments.
226
267
  - `ws://` is only accepted for loopback hosts (`127.0.0.1`, `localhost`, `::1`).
227
268
  - Event availability depends on enabled intents and secret scope.
269
+ - For vulnerability reporting and security policy, see [`SECURITY.md`](./SECURITY.md).
228
270
 
229
271
  ## Contributing
230
272
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { Snowflake, APIEmbed, APIAllowedMentions } from 'discord-api-types/v10';
1
+ import { Snowflake, APIEmbed, APIAllowedMentions, APIActionRowComponent, APIComponentInMessageActionRow, APITextInputComponent } from 'discord-api-types/v10';
2
2
  import { GatewayIntentBits } from 'discord.js';
3
3
 
4
4
  type Unsubscribe = () => void;
@@ -42,6 +42,23 @@ interface BridgeGuildMember {
42
42
  pending?: boolean;
43
43
  communicationDisabledUntil?: string | null;
44
44
  }
45
+ /** Normalized guild snapshot for `guildCreate` / `guildDelete` events. */
46
+ interface BridgeGuild {
47
+ id: Snowflake;
48
+ name: string;
49
+ icon?: string | null;
50
+ ownerId?: Snowflake;
51
+ }
52
+ /** Normalized thread channel snapshot for thread lifecycle events. */
53
+ interface BridgeThread {
54
+ id: Snowflake;
55
+ guildId: Snowflake;
56
+ parentId: Snowflake | null;
57
+ name: string;
58
+ type: number;
59
+ archived?: boolean;
60
+ locked?: boolean;
61
+ }
45
62
  interface BridgeMessage {
46
63
  id: Snowflake;
47
64
  channelId: Snowflake;
@@ -53,6 +70,8 @@ interface BridgeMessage {
53
70
  editedAt?: string | null;
54
71
  attachments: BridgeAttachment[];
55
72
  embeds: APIEmbed[];
73
+ /** Message component rows (JSON-serializable API shape). */
74
+ components?: APIActionRowComponent<APIComponentInMessageActionRow>[];
56
75
  reference?: BridgeMessageReference;
57
76
  }
58
77
  interface BridgeDeletedMessage {
@@ -91,6 +110,7 @@ interface BridgeInteraction {
91
110
  }
92
111
  interface EventEnvelopeBase {
93
112
  receivedAt: number;
113
+ /** Populated when the bot runs under `ShardingManager` (multi-shard). */
94
114
  shardId?: number;
95
115
  }
96
116
  interface ReadyEventPayload extends EventEnvelopeBase {
@@ -115,6 +135,26 @@ interface GuildMemberAddEventPayload extends EventEnvelopeBase {
115
135
  interface GuildMemberRemoveEventPayload extends EventEnvelopeBase {
116
136
  member: BridgeGuildMember;
117
137
  }
138
+ interface GuildMemberUpdateEventPayload extends EventEnvelopeBase {
139
+ oldMember?: BridgeGuildMember;
140
+ member: BridgeGuildMember;
141
+ }
142
+ interface GuildCreateEventPayload extends EventEnvelopeBase {
143
+ guild: BridgeGuild;
144
+ }
145
+ interface GuildDeleteEventPayload extends EventEnvelopeBase {
146
+ guild: BridgeGuild;
147
+ }
148
+ interface ThreadCreateEventPayload extends EventEnvelopeBase {
149
+ thread: BridgeThread;
150
+ }
151
+ interface ThreadUpdateEventPayload extends EventEnvelopeBase {
152
+ oldThread?: BridgeThread;
153
+ thread: BridgeThread;
154
+ }
155
+ interface ThreadDeleteEventPayload extends EventEnvelopeBase {
156
+ thread: BridgeThread;
157
+ }
118
158
  interface MessageReactionAddEventPayload extends EventEnvelopeBase {
119
159
  reaction: BridgeMessageReaction;
120
160
  }
@@ -129,14 +169,24 @@ interface BotEventPayloadMap {
129
169
  messageDelete: MessageDeleteEventPayload;
130
170
  messageReactionAdd: MessageReactionAddEventPayload;
131
171
  messageReactionRemove: MessageReactionRemoveEventPayload;
172
+ guildCreate: GuildCreateEventPayload;
173
+ guildDelete: GuildDeleteEventPayload;
132
174
  guildMemberAdd: GuildMemberAddEventPayload;
133
175
  guildMemberRemove: GuildMemberRemoveEventPayload;
176
+ guildMemberUpdate: GuildMemberUpdateEventPayload;
177
+ threadCreate: ThreadCreateEventPayload;
178
+ threadUpdate: ThreadUpdateEventPayload;
179
+ threadDelete: ThreadDeleteEventPayload;
134
180
  }
135
181
  type BotEventName = keyof BotEventPayloadMap;
136
182
  interface BridgeMessageInput {
137
183
  content?: string;
138
184
  embeds?: APIEmbed[];
139
185
  allowedMentions?: APIAllowedMentions;
186
+ components?: APIActionRowComponent<APIComponentInMessageActionRow>[];
187
+ /** Bitfield compatible with `MessageFlags` from discord.js / Discord API. */
188
+ flags?: number;
189
+ stickerIds?: Snowflake[];
140
190
  }
141
191
  interface SendMessageActionPayload extends BridgeMessageInput {
142
192
  channelId: Snowflake;
@@ -161,6 +211,32 @@ interface FollowUpInteractionActionPayload extends BridgeMessageInput {
161
211
  interactionId: Snowflake;
162
212
  ephemeral?: boolean;
163
213
  }
214
+ interface DeferUpdateInteractionActionPayload {
215
+ interactionId: Snowflake;
216
+ }
217
+ interface EditInteractionReplyActionPayload extends BridgeMessageInput {
218
+ interactionId: Snowflake;
219
+ }
220
+ interface DeleteInteractionReplyActionPayload {
221
+ interactionId: Snowflake;
222
+ }
223
+ interface UpdateInteractionActionPayload extends BridgeMessageInput {
224
+ interactionId: Snowflake;
225
+ }
226
+ interface ShowModalActionPayload {
227
+ interactionId: Snowflake;
228
+ title: string;
229
+ customId: string;
230
+ components: APIActionRowComponent<APITextInputComponent>[];
231
+ }
232
+ interface FetchMessageActionPayload {
233
+ channelId: Snowflake;
234
+ messageId: Snowflake;
235
+ }
236
+ interface FetchMemberActionPayload {
237
+ guildId: Snowflake;
238
+ userId: Snowflake;
239
+ }
164
240
  interface BanMemberActionPayload {
165
241
  guildId: Snowflake;
166
242
  userId: Snowflake;
@@ -200,7 +276,14 @@ interface BotActionPayloadMap {
200
276
  deleteMessage: DeleteMessageActionPayload;
201
277
  replyToInteraction: ReplyToInteractionActionPayload;
202
278
  deferInteraction: DeferInteractionActionPayload;
279
+ deferUpdateInteraction: DeferUpdateInteractionActionPayload;
203
280
  followUpInteraction: FollowUpInteractionActionPayload;
281
+ editInteractionReply: EditInteractionReplyActionPayload;
282
+ deleteInteractionReply: DeleteInteractionReplyActionPayload;
283
+ updateInteraction: UpdateInteractionActionPayload;
284
+ showModal: ShowModalActionPayload;
285
+ fetchMessage: FetchMessageActionPayload;
286
+ fetchMember: FetchMemberActionPayload;
204
287
  banMember: BanMemberActionPayload;
205
288
  kickMember: KickMemberActionPayload;
206
289
  addMemberRole: AddMemberRoleActionPayload;
@@ -217,6 +300,18 @@ interface DeferInteractionActionResult {
217
300
  deferred: true;
218
301
  interactionId: Snowflake;
219
302
  }
303
+ interface DeferUpdateInteractionActionResult {
304
+ deferred: true;
305
+ interactionId: Snowflake;
306
+ }
307
+ interface DeleteInteractionReplyActionResult {
308
+ deleted: true;
309
+ interactionId: Snowflake;
310
+ }
311
+ interface ShowModalActionResult {
312
+ shown: true;
313
+ interactionId: Snowflake;
314
+ }
220
315
  interface MemberModerationActionResult {
221
316
  guildId: Snowflake;
222
317
  userId: Snowflake;
@@ -232,7 +327,14 @@ interface BotActionResultDataMap {
232
327
  deleteMessage: DeleteMessageActionResult;
233
328
  replyToInteraction: BridgeMessage;
234
329
  deferInteraction: DeferInteractionActionResult;
330
+ deferUpdateInteraction: DeferUpdateInteractionActionResult;
235
331
  followUpInteraction: BridgeMessage;
332
+ editInteractionReply: BridgeMessage;
333
+ deleteInteractionReply: DeleteInteractionReplyActionResult;
334
+ updateInteraction: BridgeMessage;
335
+ showModal: ShowModalActionResult;
336
+ fetchMessage: BridgeMessage;
337
+ fetchMember: BridgeGuildMember;
236
338
  banMember: MemberModerationActionResult;
237
339
  kickMember: MemberModerationActionResult;
238
340
  addMemberRole: BridgeGuildMember;
@@ -250,6 +352,10 @@ interface EventSubscriptionFilter {
250
352
  channelId?: Snowflake | readonly Snowflake[];
251
353
  userId?: Snowflake | readonly Snowflake[];
252
354
  commandName?: string | readonly string[];
355
+ /** Matches `BridgeInteraction.customId` when present (components, modals). */
356
+ customId?: string | readonly string[];
357
+ /** Matches `BridgeInteraction.kind`. */
358
+ interactionKind?: BridgeInteractionKind | readonly BridgeInteractionKind[];
253
359
  }
254
360
  interface EventSubscription<K extends BotEventName = BotEventName> {
255
361
  name: K;
@@ -265,6 +371,14 @@ interface ScopedSecretConfig {
265
371
  allow?: SecretPermissions;
266
372
  }
267
373
  type BotBridgeSecret = string | ScopedSecretConfig;
374
+ /** Structured Discord / transport context for failed actions (machine-readable). */
375
+ interface ActionErrorDetails {
376
+ discordStatus?: number;
377
+ discordCode?: number;
378
+ /** When true, callers may retry with backoff (e.g. rate limits). */
379
+ retryable?: boolean;
380
+ [key: string]: unknown;
381
+ }
268
382
  interface BotBridgeOptions {
269
383
  token: string;
270
384
  intents: readonly BotIntentName[];
@@ -275,9 +389,24 @@ interface BotBridgeOptions {
275
389
  heartbeatMs?: number;
276
390
  maxPayloadBytes?: number;
277
391
  secrets: readonly BotBridgeSecret[];
392
+ /** Reject new TCP connections when authenticated client count reaches this cap (default: unlimited). */
393
+ maxConnections?: number;
394
+ /** Max concurrent action executions per bot process (default: 32). */
395
+ maxConcurrentActions?: number;
396
+ /** When the queue is full, fail fast with `SERVICE_UNAVAILABLE` (default: 5000). */
397
+ actionQueueTimeoutMs?: number;
278
398
  };
279
399
  logger?: ShardwireLogger;
280
400
  }
401
+ interface AppBridgeMetricsHooks {
402
+ onActionComplete?: (meta: {
403
+ name: BotActionName;
404
+ requestId: string;
405
+ durationMs: number;
406
+ ok: boolean;
407
+ errorCode?: string;
408
+ }) => void;
409
+ }
281
410
  interface AppBridgeOptions {
282
411
  url: string;
283
412
  secret: string;
@@ -291,11 +420,12 @@ interface AppBridgeOptions {
291
420
  };
292
421
  requestTimeoutMs?: number;
293
422
  logger?: ShardwireLogger;
423
+ metrics?: AppBridgeMetricsHooks;
294
424
  }
295
425
  interface ActionError {
296
- code: "UNAUTHORIZED" | "TIMEOUT" | "DISCONNECTED" | "FORBIDDEN" | "NOT_FOUND" | "INVALID_REQUEST" | "INTERNAL_ERROR";
426
+ code: "UNAUTHORIZED" | "TIMEOUT" | "DISCONNECTED" | "FORBIDDEN" | "NOT_FOUND" | "INVALID_REQUEST" | "INTERNAL_ERROR" | "SERVICE_UNAVAILABLE";
297
427
  message: string;
298
- details?: unknown;
428
+ details?: ActionErrorDetails | unknown;
299
429
  }
300
430
  interface ActionSuccess<T> {
301
431
  ok: true;
@@ -316,11 +446,14 @@ declare class BridgeCapabilityError extends Error {
316
446
  constructor(kind: "event" | "action", name: string, message?: string);
317
447
  }
318
448
  type EventHandler<K extends BotEventName> = (payload: BotEventPayloadMap[K]) => void;
449
+ type AppBridgeActionInvokeOptions = {
450
+ timeoutMs?: number;
451
+ requestId?: string;
452
+ /** When set, duplicate keys within TTL return the first result (best-effort idempotency). */
453
+ idempotencyKey?: string;
454
+ };
319
455
  type AppBridgeActions = {
320
- [K in BotActionName]: (payload: BotActionPayloadMap[K], options?: {
321
- timeoutMs?: number;
322
- requestId?: string;
323
- }) => Promise<ActionResult<BotActionResultDataMap[K]>>;
456
+ [K in BotActionName]: (payload: BotActionPayloadMap[K], options?: AppBridgeActionInvokeOptions) => Promise<ActionResult<BotActionResultDataMap[K]>>;
324
457
  };
325
458
  interface BotBridge {
326
459
  ready(): Promise<void>;
@@ -345,4 +478,4 @@ declare function createBotBridge(options: BotBridgeOptions): BotBridge;
345
478
 
346
479
  declare function connectBotBridge(options: AppBridgeOptions): AppBridge;
347
480
 
348
- export { type ActionError, type ActionFailure, type ActionResult, type ActionSuccess, type AddMemberRoleActionPayload, type AddMessageReactionActionPayload, type AppBridge, type AppBridgeActions, type AppBridgeOptions, type BanMemberActionPayload, type BotActionName, type BotActionPayloadMap, type BotActionResultDataMap, type BotBridge, type BotBridgeOptions, type BotBridgeSecret, type BotEventName, type BotEventPayloadMap, type BotIntentName, type BridgeAttachment, type BridgeCapabilities, BridgeCapabilityError, type BridgeDeletedMessage, type BridgeGuildMember, type BridgeInteraction, type BridgeInteractionKind, type BridgeMessage, type BridgeMessageInput, type BridgeMessageReaction, type BridgeMessageReference, type BridgeReactionEmoji, type BridgeUser, type DeferInteractionActionPayload, type DeleteMessageActionPayload, type EditMessageActionPayload, type EventEnvelopeBase, type EventHandler, type EventSubscription, type EventSubscriptionFilter, type FollowUpInteractionActionPayload, type GuildMemberAddEventPayload, type GuildMemberRemoveEventPayload, type InteractionCreateEventPayload, type KickMemberActionPayload, type MessageCreateEventPayload, type MessageDeleteEventPayload, type MessageReactionActionResult, type MessageReactionAddEventPayload, type MessageReactionRemoveEventPayload, type MessageUpdateEventPayload, type ReadyEventPayload, type RemoveMemberRoleActionPayload, type RemoveOwnMessageReactionActionPayload, type ReplyToInteractionActionPayload, type ScopedSecretConfig, type SecretPermissions, type SendMessageActionPayload, type ShardwireLogger, type Unsubscribe, connectBotBridge, createBotBridge };
481
+ export { type ActionError, type ActionErrorDetails, type ActionFailure, type ActionResult, type ActionSuccess, type AddMemberRoleActionPayload, type AddMessageReactionActionPayload, type AppBridge, type AppBridgeActionInvokeOptions, type AppBridgeActions, type AppBridgeMetricsHooks, type AppBridgeOptions, type BanMemberActionPayload, type BotActionName, type BotActionPayloadMap, type BotActionResultDataMap, type BotBridge, type BotBridgeOptions, type BotBridgeSecret, type BotEventName, type BotEventPayloadMap, type BotIntentName, type BridgeAttachment, type BridgeCapabilities, BridgeCapabilityError, type BridgeDeletedMessage, type BridgeGuild, type BridgeGuildMember, type BridgeInteraction, type BridgeInteractionKind, type BridgeMessage, type BridgeMessageInput, type BridgeMessageReaction, type BridgeMessageReference, type BridgeReactionEmoji, type BridgeThread, type BridgeUser, type DeferInteractionActionPayload, type DeferInteractionActionResult, type DeferUpdateInteractionActionPayload, type DeferUpdateInteractionActionResult, type DeleteInteractionReplyActionPayload, type DeleteInteractionReplyActionResult, type DeleteMessageActionPayload, type DeleteMessageActionResult, type EditInteractionReplyActionPayload, type EditMessageActionPayload, type EventEnvelopeBase, type EventHandler, type EventSubscription, type EventSubscriptionFilter, type FetchMemberActionPayload, type FetchMessageActionPayload, type FollowUpInteractionActionPayload, type GuildCreateEventPayload, type GuildDeleteEventPayload, type GuildMemberAddEventPayload, type GuildMemberRemoveEventPayload, type GuildMemberUpdateEventPayload, type InteractionCreateEventPayload, type KickMemberActionPayload, type MemberModerationActionResult, type MessageCreateEventPayload, type MessageDeleteEventPayload, type MessageReactionActionResult, type MessageReactionAddEventPayload, type MessageReactionRemoveEventPayload, type MessageUpdateEventPayload, type ReadyEventPayload, type RemoveMemberRoleActionPayload, type RemoveOwnMessageReactionActionPayload, type ReplyToInteractionActionPayload, type ScopedSecretConfig, type SecretPermissions, type SendMessageActionPayload, type ShardwireLogger, type ShowModalActionPayload, type ShowModalActionResult, type ThreadCreateEventPayload, type ThreadDeleteEventPayload, type ThreadUpdateEventPayload, type Unsubscribe, type UpdateInteractionActionPayload, connectBotBridge, createBotBridge };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Snowflake, APIEmbed, APIAllowedMentions } from 'discord-api-types/v10';
1
+ import { Snowflake, APIEmbed, APIAllowedMentions, APIActionRowComponent, APIComponentInMessageActionRow, APITextInputComponent } from 'discord-api-types/v10';
2
2
  import { GatewayIntentBits } from 'discord.js';
3
3
 
4
4
  type Unsubscribe = () => void;
@@ -42,6 +42,23 @@ interface BridgeGuildMember {
42
42
  pending?: boolean;
43
43
  communicationDisabledUntil?: string | null;
44
44
  }
45
+ /** Normalized guild snapshot for `guildCreate` / `guildDelete` events. */
46
+ interface BridgeGuild {
47
+ id: Snowflake;
48
+ name: string;
49
+ icon?: string | null;
50
+ ownerId?: Snowflake;
51
+ }
52
+ /** Normalized thread channel snapshot for thread lifecycle events. */
53
+ interface BridgeThread {
54
+ id: Snowflake;
55
+ guildId: Snowflake;
56
+ parentId: Snowflake | null;
57
+ name: string;
58
+ type: number;
59
+ archived?: boolean;
60
+ locked?: boolean;
61
+ }
45
62
  interface BridgeMessage {
46
63
  id: Snowflake;
47
64
  channelId: Snowflake;
@@ -53,6 +70,8 @@ interface BridgeMessage {
53
70
  editedAt?: string | null;
54
71
  attachments: BridgeAttachment[];
55
72
  embeds: APIEmbed[];
73
+ /** Message component rows (JSON-serializable API shape). */
74
+ components?: APIActionRowComponent<APIComponentInMessageActionRow>[];
56
75
  reference?: BridgeMessageReference;
57
76
  }
58
77
  interface BridgeDeletedMessage {
@@ -91,6 +110,7 @@ interface BridgeInteraction {
91
110
  }
92
111
  interface EventEnvelopeBase {
93
112
  receivedAt: number;
113
+ /** Populated when the bot runs under `ShardingManager` (multi-shard). */
94
114
  shardId?: number;
95
115
  }
96
116
  interface ReadyEventPayload extends EventEnvelopeBase {
@@ -115,6 +135,26 @@ interface GuildMemberAddEventPayload extends EventEnvelopeBase {
115
135
  interface GuildMemberRemoveEventPayload extends EventEnvelopeBase {
116
136
  member: BridgeGuildMember;
117
137
  }
138
+ interface GuildMemberUpdateEventPayload extends EventEnvelopeBase {
139
+ oldMember?: BridgeGuildMember;
140
+ member: BridgeGuildMember;
141
+ }
142
+ interface GuildCreateEventPayload extends EventEnvelopeBase {
143
+ guild: BridgeGuild;
144
+ }
145
+ interface GuildDeleteEventPayload extends EventEnvelopeBase {
146
+ guild: BridgeGuild;
147
+ }
148
+ interface ThreadCreateEventPayload extends EventEnvelopeBase {
149
+ thread: BridgeThread;
150
+ }
151
+ interface ThreadUpdateEventPayload extends EventEnvelopeBase {
152
+ oldThread?: BridgeThread;
153
+ thread: BridgeThread;
154
+ }
155
+ interface ThreadDeleteEventPayload extends EventEnvelopeBase {
156
+ thread: BridgeThread;
157
+ }
118
158
  interface MessageReactionAddEventPayload extends EventEnvelopeBase {
119
159
  reaction: BridgeMessageReaction;
120
160
  }
@@ -129,14 +169,24 @@ interface BotEventPayloadMap {
129
169
  messageDelete: MessageDeleteEventPayload;
130
170
  messageReactionAdd: MessageReactionAddEventPayload;
131
171
  messageReactionRemove: MessageReactionRemoveEventPayload;
172
+ guildCreate: GuildCreateEventPayload;
173
+ guildDelete: GuildDeleteEventPayload;
132
174
  guildMemberAdd: GuildMemberAddEventPayload;
133
175
  guildMemberRemove: GuildMemberRemoveEventPayload;
176
+ guildMemberUpdate: GuildMemberUpdateEventPayload;
177
+ threadCreate: ThreadCreateEventPayload;
178
+ threadUpdate: ThreadUpdateEventPayload;
179
+ threadDelete: ThreadDeleteEventPayload;
134
180
  }
135
181
  type BotEventName = keyof BotEventPayloadMap;
136
182
  interface BridgeMessageInput {
137
183
  content?: string;
138
184
  embeds?: APIEmbed[];
139
185
  allowedMentions?: APIAllowedMentions;
186
+ components?: APIActionRowComponent<APIComponentInMessageActionRow>[];
187
+ /** Bitfield compatible with `MessageFlags` from discord.js / Discord API. */
188
+ flags?: number;
189
+ stickerIds?: Snowflake[];
140
190
  }
141
191
  interface SendMessageActionPayload extends BridgeMessageInput {
142
192
  channelId: Snowflake;
@@ -161,6 +211,32 @@ interface FollowUpInteractionActionPayload extends BridgeMessageInput {
161
211
  interactionId: Snowflake;
162
212
  ephemeral?: boolean;
163
213
  }
214
+ interface DeferUpdateInteractionActionPayload {
215
+ interactionId: Snowflake;
216
+ }
217
+ interface EditInteractionReplyActionPayload extends BridgeMessageInput {
218
+ interactionId: Snowflake;
219
+ }
220
+ interface DeleteInteractionReplyActionPayload {
221
+ interactionId: Snowflake;
222
+ }
223
+ interface UpdateInteractionActionPayload extends BridgeMessageInput {
224
+ interactionId: Snowflake;
225
+ }
226
+ interface ShowModalActionPayload {
227
+ interactionId: Snowflake;
228
+ title: string;
229
+ customId: string;
230
+ components: APIActionRowComponent<APITextInputComponent>[];
231
+ }
232
+ interface FetchMessageActionPayload {
233
+ channelId: Snowflake;
234
+ messageId: Snowflake;
235
+ }
236
+ interface FetchMemberActionPayload {
237
+ guildId: Snowflake;
238
+ userId: Snowflake;
239
+ }
164
240
  interface BanMemberActionPayload {
165
241
  guildId: Snowflake;
166
242
  userId: Snowflake;
@@ -200,7 +276,14 @@ interface BotActionPayloadMap {
200
276
  deleteMessage: DeleteMessageActionPayload;
201
277
  replyToInteraction: ReplyToInteractionActionPayload;
202
278
  deferInteraction: DeferInteractionActionPayload;
279
+ deferUpdateInteraction: DeferUpdateInteractionActionPayload;
203
280
  followUpInteraction: FollowUpInteractionActionPayload;
281
+ editInteractionReply: EditInteractionReplyActionPayload;
282
+ deleteInteractionReply: DeleteInteractionReplyActionPayload;
283
+ updateInteraction: UpdateInteractionActionPayload;
284
+ showModal: ShowModalActionPayload;
285
+ fetchMessage: FetchMessageActionPayload;
286
+ fetchMember: FetchMemberActionPayload;
204
287
  banMember: BanMemberActionPayload;
205
288
  kickMember: KickMemberActionPayload;
206
289
  addMemberRole: AddMemberRoleActionPayload;
@@ -217,6 +300,18 @@ interface DeferInteractionActionResult {
217
300
  deferred: true;
218
301
  interactionId: Snowflake;
219
302
  }
303
+ interface DeferUpdateInteractionActionResult {
304
+ deferred: true;
305
+ interactionId: Snowflake;
306
+ }
307
+ interface DeleteInteractionReplyActionResult {
308
+ deleted: true;
309
+ interactionId: Snowflake;
310
+ }
311
+ interface ShowModalActionResult {
312
+ shown: true;
313
+ interactionId: Snowflake;
314
+ }
220
315
  interface MemberModerationActionResult {
221
316
  guildId: Snowflake;
222
317
  userId: Snowflake;
@@ -232,7 +327,14 @@ interface BotActionResultDataMap {
232
327
  deleteMessage: DeleteMessageActionResult;
233
328
  replyToInteraction: BridgeMessage;
234
329
  deferInteraction: DeferInteractionActionResult;
330
+ deferUpdateInteraction: DeferUpdateInteractionActionResult;
235
331
  followUpInteraction: BridgeMessage;
332
+ editInteractionReply: BridgeMessage;
333
+ deleteInteractionReply: DeleteInteractionReplyActionResult;
334
+ updateInteraction: BridgeMessage;
335
+ showModal: ShowModalActionResult;
336
+ fetchMessage: BridgeMessage;
337
+ fetchMember: BridgeGuildMember;
236
338
  banMember: MemberModerationActionResult;
237
339
  kickMember: MemberModerationActionResult;
238
340
  addMemberRole: BridgeGuildMember;
@@ -250,6 +352,10 @@ interface EventSubscriptionFilter {
250
352
  channelId?: Snowflake | readonly Snowflake[];
251
353
  userId?: Snowflake | readonly Snowflake[];
252
354
  commandName?: string | readonly string[];
355
+ /** Matches `BridgeInteraction.customId` when present (components, modals). */
356
+ customId?: string | readonly string[];
357
+ /** Matches `BridgeInteraction.kind`. */
358
+ interactionKind?: BridgeInteractionKind | readonly BridgeInteractionKind[];
253
359
  }
254
360
  interface EventSubscription<K extends BotEventName = BotEventName> {
255
361
  name: K;
@@ -265,6 +371,14 @@ interface ScopedSecretConfig {
265
371
  allow?: SecretPermissions;
266
372
  }
267
373
  type BotBridgeSecret = string | ScopedSecretConfig;
374
+ /** Structured Discord / transport context for failed actions (machine-readable). */
375
+ interface ActionErrorDetails {
376
+ discordStatus?: number;
377
+ discordCode?: number;
378
+ /** When true, callers may retry with backoff (e.g. rate limits). */
379
+ retryable?: boolean;
380
+ [key: string]: unknown;
381
+ }
268
382
  interface BotBridgeOptions {
269
383
  token: string;
270
384
  intents: readonly BotIntentName[];
@@ -275,9 +389,24 @@ interface BotBridgeOptions {
275
389
  heartbeatMs?: number;
276
390
  maxPayloadBytes?: number;
277
391
  secrets: readonly BotBridgeSecret[];
392
+ /** Reject new TCP connections when authenticated client count reaches this cap (default: unlimited). */
393
+ maxConnections?: number;
394
+ /** Max concurrent action executions per bot process (default: 32). */
395
+ maxConcurrentActions?: number;
396
+ /** When the queue is full, fail fast with `SERVICE_UNAVAILABLE` (default: 5000). */
397
+ actionQueueTimeoutMs?: number;
278
398
  };
279
399
  logger?: ShardwireLogger;
280
400
  }
401
+ interface AppBridgeMetricsHooks {
402
+ onActionComplete?: (meta: {
403
+ name: BotActionName;
404
+ requestId: string;
405
+ durationMs: number;
406
+ ok: boolean;
407
+ errorCode?: string;
408
+ }) => void;
409
+ }
281
410
  interface AppBridgeOptions {
282
411
  url: string;
283
412
  secret: string;
@@ -291,11 +420,12 @@ interface AppBridgeOptions {
291
420
  };
292
421
  requestTimeoutMs?: number;
293
422
  logger?: ShardwireLogger;
423
+ metrics?: AppBridgeMetricsHooks;
294
424
  }
295
425
  interface ActionError {
296
- code: "UNAUTHORIZED" | "TIMEOUT" | "DISCONNECTED" | "FORBIDDEN" | "NOT_FOUND" | "INVALID_REQUEST" | "INTERNAL_ERROR";
426
+ code: "UNAUTHORIZED" | "TIMEOUT" | "DISCONNECTED" | "FORBIDDEN" | "NOT_FOUND" | "INVALID_REQUEST" | "INTERNAL_ERROR" | "SERVICE_UNAVAILABLE";
297
427
  message: string;
298
- details?: unknown;
428
+ details?: ActionErrorDetails | unknown;
299
429
  }
300
430
  interface ActionSuccess<T> {
301
431
  ok: true;
@@ -316,11 +446,14 @@ declare class BridgeCapabilityError extends Error {
316
446
  constructor(kind: "event" | "action", name: string, message?: string);
317
447
  }
318
448
  type EventHandler<K extends BotEventName> = (payload: BotEventPayloadMap[K]) => void;
449
+ type AppBridgeActionInvokeOptions = {
450
+ timeoutMs?: number;
451
+ requestId?: string;
452
+ /** When set, duplicate keys within TTL return the first result (best-effort idempotency). */
453
+ idempotencyKey?: string;
454
+ };
319
455
  type AppBridgeActions = {
320
- [K in BotActionName]: (payload: BotActionPayloadMap[K], options?: {
321
- timeoutMs?: number;
322
- requestId?: string;
323
- }) => Promise<ActionResult<BotActionResultDataMap[K]>>;
456
+ [K in BotActionName]: (payload: BotActionPayloadMap[K], options?: AppBridgeActionInvokeOptions) => Promise<ActionResult<BotActionResultDataMap[K]>>;
324
457
  };
325
458
  interface BotBridge {
326
459
  ready(): Promise<void>;
@@ -345,4 +478,4 @@ declare function createBotBridge(options: BotBridgeOptions): BotBridge;
345
478
 
346
479
  declare function connectBotBridge(options: AppBridgeOptions): AppBridge;
347
480
 
348
- export { type ActionError, type ActionFailure, type ActionResult, type ActionSuccess, type AddMemberRoleActionPayload, type AddMessageReactionActionPayload, type AppBridge, type AppBridgeActions, type AppBridgeOptions, type BanMemberActionPayload, type BotActionName, type BotActionPayloadMap, type BotActionResultDataMap, type BotBridge, type BotBridgeOptions, type BotBridgeSecret, type BotEventName, type BotEventPayloadMap, type BotIntentName, type BridgeAttachment, type BridgeCapabilities, BridgeCapabilityError, type BridgeDeletedMessage, type BridgeGuildMember, type BridgeInteraction, type BridgeInteractionKind, type BridgeMessage, type BridgeMessageInput, type BridgeMessageReaction, type BridgeMessageReference, type BridgeReactionEmoji, type BridgeUser, type DeferInteractionActionPayload, type DeleteMessageActionPayload, type EditMessageActionPayload, type EventEnvelopeBase, type EventHandler, type EventSubscription, type EventSubscriptionFilter, type FollowUpInteractionActionPayload, type GuildMemberAddEventPayload, type GuildMemberRemoveEventPayload, type InteractionCreateEventPayload, type KickMemberActionPayload, type MessageCreateEventPayload, type MessageDeleteEventPayload, type MessageReactionActionResult, type MessageReactionAddEventPayload, type MessageReactionRemoveEventPayload, type MessageUpdateEventPayload, type ReadyEventPayload, type RemoveMemberRoleActionPayload, type RemoveOwnMessageReactionActionPayload, type ReplyToInteractionActionPayload, type ScopedSecretConfig, type SecretPermissions, type SendMessageActionPayload, type ShardwireLogger, type Unsubscribe, connectBotBridge, createBotBridge };
481
+ export { type ActionError, type ActionErrorDetails, type ActionFailure, type ActionResult, type ActionSuccess, type AddMemberRoleActionPayload, type AddMessageReactionActionPayload, type AppBridge, type AppBridgeActionInvokeOptions, type AppBridgeActions, type AppBridgeMetricsHooks, type AppBridgeOptions, type BanMemberActionPayload, type BotActionName, type BotActionPayloadMap, type BotActionResultDataMap, type BotBridge, type BotBridgeOptions, type BotBridgeSecret, type BotEventName, type BotEventPayloadMap, type BotIntentName, type BridgeAttachment, type BridgeCapabilities, BridgeCapabilityError, type BridgeDeletedMessage, type BridgeGuild, type BridgeGuildMember, type BridgeInteraction, type BridgeInteractionKind, type BridgeMessage, type BridgeMessageInput, type BridgeMessageReaction, type BridgeMessageReference, type BridgeReactionEmoji, type BridgeThread, type BridgeUser, type DeferInteractionActionPayload, type DeferInteractionActionResult, type DeferUpdateInteractionActionPayload, type DeferUpdateInteractionActionResult, type DeleteInteractionReplyActionPayload, type DeleteInteractionReplyActionResult, type DeleteMessageActionPayload, type DeleteMessageActionResult, type EditInteractionReplyActionPayload, type EditMessageActionPayload, type EventEnvelopeBase, type EventHandler, type EventSubscription, type EventSubscriptionFilter, type FetchMemberActionPayload, type FetchMessageActionPayload, type FollowUpInteractionActionPayload, type GuildCreateEventPayload, type GuildDeleteEventPayload, type GuildMemberAddEventPayload, type GuildMemberRemoveEventPayload, type GuildMemberUpdateEventPayload, type InteractionCreateEventPayload, type KickMemberActionPayload, type MemberModerationActionResult, type MessageCreateEventPayload, type MessageDeleteEventPayload, type MessageReactionActionResult, type MessageReactionAddEventPayload, type MessageReactionRemoveEventPayload, type MessageUpdateEventPayload, type ReadyEventPayload, type RemoveMemberRoleActionPayload, type RemoveOwnMessageReactionActionPayload, type ReplyToInteractionActionPayload, type ScopedSecretConfig, type SecretPermissions, type SendMessageActionPayload, type ShardwireLogger, type ShowModalActionPayload, type ShowModalActionResult, type ThreadCreateEventPayload, type ThreadDeleteEventPayload, type ThreadUpdateEventPayload, type Unsubscribe, type UpdateInteractionActionPayload, connectBotBridge, createBotBridge };