shardwire 1.2.0 → 1.3.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 +101 -42
- package/dist/index.d.mts +20 -6
- package/dist/index.d.ts +20 -6
- package/dist/index.js +70 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +70 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +82 -75
package/dist/index.mjs
CHANGED
|
@@ -63,7 +63,9 @@ var EVENT_REQUIRED_INTENTS = {
|
|
|
63
63
|
};
|
|
64
64
|
function getAvailableEvents(intents) {
|
|
65
65
|
const enabled = new Set(intents);
|
|
66
|
-
return BOT_EVENT_NAMES.filter(
|
|
66
|
+
return BOT_EVENT_NAMES.filter(
|
|
67
|
+
(eventName) => EVENT_REQUIRED_INTENTS[eventName].every((intent) => enabled.has(intent))
|
|
68
|
+
);
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
// src/discord/runtime/adapter.ts
|
|
@@ -405,10 +407,19 @@ function mapDiscordErrorToActionExecutionError(error) {
|
|
|
405
407
|
return new ActionExecutionError("INVALID_REQUEST", message, detailPayload);
|
|
406
408
|
}
|
|
407
409
|
if (details.status === 429) {
|
|
410
|
+
let retryAfterMs;
|
|
411
|
+
if (error instanceof DiscordAPIError) {
|
|
412
|
+
const raw = error.rawError;
|
|
413
|
+
const retryAfter = raw?.retry_after;
|
|
414
|
+
if (typeof retryAfter === "number" && Number.isFinite(retryAfter)) {
|
|
415
|
+
retryAfterMs = Math.max(0, Math.ceil(retryAfter * 1e3));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
408
418
|
return new ActionExecutionError("SERVICE_UNAVAILABLE", message, {
|
|
409
419
|
discordStatus: 429,
|
|
410
420
|
retryable: true,
|
|
411
|
-
...details.code !== void 0 ? { discordCode: details.code } : {}
|
|
421
|
+
...details.code !== void 0 ? { discordCode: details.code } : {},
|
|
422
|
+
...retryAfterMs !== void 0 ? { retryAfterMs } : {}
|
|
412
423
|
});
|
|
413
424
|
}
|
|
414
425
|
return null;
|
|
@@ -726,7 +737,10 @@ var DiscordJsRuntimeAdapter = class {
|
|
|
726
737
|
throw mappedError;
|
|
727
738
|
}
|
|
728
739
|
this.logger.error("Discord action execution failed.", { action: name, error: String(error) });
|
|
729
|
-
throw new ActionExecutionError(
|
|
740
|
+
throw new ActionExecutionError(
|
|
741
|
+
"INTERNAL_ERROR",
|
|
742
|
+
error instanceof Error ? error.message : "Discord action failed."
|
|
743
|
+
);
|
|
730
744
|
}
|
|
731
745
|
}
|
|
732
746
|
async fetchSendableChannel(channelId) {
|
|
@@ -794,7 +808,10 @@ var DiscordJsRuntimeAdapter = class {
|
|
|
794
808
|
async replyToInteraction(payload) {
|
|
795
809
|
const interaction = this.getReplyCapableInteraction(payload.interactionId);
|
|
796
810
|
if (interaction.replied || interaction.deferred) {
|
|
797
|
-
throw new ActionExecutionError(
|
|
811
|
+
throw new ActionExecutionError(
|
|
812
|
+
"INVALID_REQUEST",
|
|
813
|
+
`Interaction "${payload.interactionId}" has already been acknowledged.`
|
|
814
|
+
);
|
|
798
815
|
}
|
|
799
816
|
const reply = await interaction.reply({
|
|
800
817
|
...toSendOptions(payload),
|
|
@@ -806,7 +823,10 @@ var DiscordJsRuntimeAdapter = class {
|
|
|
806
823
|
async deferInteraction(payload) {
|
|
807
824
|
const interaction = this.getReplyCapableInteraction(payload.interactionId);
|
|
808
825
|
if (interaction.replied) {
|
|
809
|
-
throw new ActionExecutionError(
|
|
826
|
+
throw new ActionExecutionError(
|
|
827
|
+
"INVALID_REQUEST",
|
|
828
|
+
`Interaction "${payload.interactionId}" has already been replied to.`
|
|
829
|
+
);
|
|
810
830
|
}
|
|
811
831
|
if (!interaction.deferred) {
|
|
812
832
|
await interaction.deferReply({
|
|
@@ -829,7 +849,10 @@ var DiscordJsRuntimeAdapter = class {
|
|
|
829
849
|
async followUpInteraction(payload) {
|
|
830
850
|
const interaction = this.getReplyCapableInteraction(payload.interactionId);
|
|
831
851
|
if (!interaction.replied && !interaction.deferred) {
|
|
832
|
-
throw new ActionExecutionError(
|
|
852
|
+
throw new ActionExecutionError(
|
|
853
|
+
"INVALID_REQUEST",
|
|
854
|
+
`Interaction "${payload.interactionId}" has not been acknowledged yet.`
|
|
855
|
+
);
|
|
833
856
|
}
|
|
834
857
|
const followUp = await interaction.followUp({
|
|
835
858
|
...toSendOptions(payload),
|
|
@@ -938,7 +961,10 @@ var DiscordJsRuntimeAdapter = class {
|
|
|
938
961
|
return candidate.emoji.identifier === payload.emoji || candidate.emoji.name === payload.emoji || candidate.emoji.toString() === payload.emoji;
|
|
939
962
|
});
|
|
940
963
|
if (!reaction) {
|
|
941
|
-
throw new ActionExecutionError(
|
|
964
|
+
throw new ActionExecutionError(
|
|
965
|
+
"NOT_FOUND",
|
|
966
|
+
`Reaction "${payload.emoji}" was not found on message "${payload.messageId}".`
|
|
967
|
+
);
|
|
942
968
|
}
|
|
943
969
|
await reaction.users.remove(ownUserId);
|
|
944
970
|
return {
|
|
@@ -981,7 +1007,9 @@ function normalizeKindList(value) {
|
|
|
981
1007
|
return void 0;
|
|
982
1008
|
}
|
|
983
1009
|
const rawValues = Array.isArray(value) ? value : [value];
|
|
984
|
-
const normalized = [
|
|
1010
|
+
const normalized = [
|
|
1011
|
+
...new Set(rawValues.filter((entry) => typeof entry === "string"))
|
|
1012
|
+
].sort();
|
|
985
1013
|
return normalized.length > 0 ? normalized : void 0;
|
|
986
1014
|
}
|
|
987
1015
|
function normalizeEventSubscriptionFilter(filter) {
|
|
@@ -1247,6 +1275,8 @@ var BridgeTransportServer = class {
|
|
|
1247
1275
|
const maxConcurrent = config.options.server.maxConcurrentActions ?? 32;
|
|
1248
1276
|
const queueTimeout = config.options.server.actionQueueTimeoutMs ?? 5e3;
|
|
1249
1277
|
this.actionSemaphore = new AsyncSemaphore(maxConcurrent, queueTimeout);
|
|
1278
|
+
this.idempotencyTtlMs = config.options.server.idempotencyTtlMs ?? 12e4;
|
|
1279
|
+
this.idempotencyScope = config.options.server.idempotencyScope ?? "connection";
|
|
1250
1280
|
this.wss = new WebSocketServer({
|
|
1251
1281
|
host: config.options.server.host,
|
|
1252
1282
|
port: config.options.server.port,
|
|
@@ -1269,7 +1299,8 @@ var BridgeTransportServer = class {
|
|
|
1269
1299
|
stickyEvents = /* @__PURE__ */ new Map();
|
|
1270
1300
|
actionSemaphore;
|
|
1271
1301
|
idempotencyCache = /* @__PURE__ */ new Map();
|
|
1272
|
-
idempotencyTtlMs
|
|
1302
|
+
idempotencyTtlMs;
|
|
1303
|
+
idempotencyScope;
|
|
1273
1304
|
authBuckets = /* @__PURE__ */ new Map();
|
|
1274
1305
|
connectionCount() {
|
|
1275
1306
|
let count = 0;
|
|
@@ -1499,7 +1530,7 @@ var BridgeTransportServer = class {
|
|
|
1499
1530
|
}
|
|
1500
1531
|
const idempotencyRaw = payload.idempotencyKey;
|
|
1501
1532
|
const idempotencyKey = typeof idempotencyRaw === "string" && idempotencyRaw.length > 0 && idempotencyRaw.length <= 256 ? idempotencyRaw : void 0;
|
|
1502
|
-
const idempotencyCacheKey = idempotencyKey ?
|
|
1533
|
+
const idempotencyCacheKey = idempotencyKey ? this.idempotencyScope === "secret" && activeSecret ? `secret:${activeSecret.id}:${idempotencyKey}` : `conn:${state.id}:${idempotencyKey}` : void 0;
|
|
1503
1534
|
if (idempotencyCacheKey) {
|
|
1504
1535
|
this.pruneIdempotencyCache(Date.now());
|
|
1505
1536
|
const cached = this.idempotencyCache.get(idempotencyCacheKey);
|
|
@@ -1512,9 +1543,7 @@ var BridgeTransportServer = class {
|
|
|
1512
1543
|
});
|
|
1513
1544
|
this.safeSend(
|
|
1514
1545
|
state.socket,
|
|
1515
|
-
stringifyEnvelope(
|
|
1516
|
-
makeEnvelope(replay.ok ? "action.result" : "action.error", replay, { requestId })
|
|
1517
|
-
)
|
|
1546
|
+
stringifyEnvelope(makeEnvelope(replay.ok ? "action.result" : "action.error", replay, { requestId }))
|
|
1518
1547
|
);
|
|
1519
1548
|
return;
|
|
1520
1549
|
}
|
|
@@ -1705,11 +1734,7 @@ function normalizeSecretEntry(secret, index) {
|
|
|
1705
1734
|
value: scoped.value,
|
|
1706
1735
|
scope: {
|
|
1707
1736
|
events: normalizeScopeList(scoped.allow?.events, BOT_EVENT_NAMES, `server.secrets[${index}].allow.events`),
|
|
1708
|
-
actions: normalizeScopeList(
|
|
1709
|
-
scoped.allow?.actions,
|
|
1710
|
-
BOT_ACTION_NAMES,
|
|
1711
|
-
`server.secrets[${index}].allow.actions`
|
|
1712
|
-
)
|
|
1737
|
+
actions: normalizeScopeList(scoped.allow?.actions, BOT_ACTION_NAMES, `server.secrets[${index}].allow.actions`)
|
|
1713
1738
|
}
|
|
1714
1739
|
};
|
|
1715
1740
|
}
|
|
@@ -1736,6 +1761,14 @@ function assertBotBridgeOptions(options) {
|
|
|
1736
1761
|
if (options.server.actionQueueTimeoutMs !== void 0) {
|
|
1737
1762
|
assertPositiveNumber("server.actionQueueTimeoutMs", options.server.actionQueueTimeoutMs);
|
|
1738
1763
|
}
|
|
1764
|
+
if (options.server.idempotencyScope !== void 0) {
|
|
1765
|
+
if (options.server.idempotencyScope !== "connection" && options.server.idempotencyScope !== "secret") {
|
|
1766
|
+
throw new Error('server.idempotencyScope must be "connection" or "secret".');
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
if (options.server.idempotencyTtlMs !== void 0) {
|
|
1770
|
+
assertPositiveNumber("server.idempotencyTtlMs", options.server.idempotencyTtlMs);
|
|
1771
|
+
}
|
|
1739
1772
|
if (!Array.isArray(options.server.secrets) || options.server.secrets.length === 0) {
|
|
1740
1773
|
throw new Error("server.secrets must contain at least one secret.");
|
|
1741
1774
|
}
|
|
@@ -1935,6 +1968,24 @@ var AppRequestError = class extends Error {
|
|
|
1935
1968
|
code;
|
|
1936
1969
|
};
|
|
1937
1970
|
var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
|
|
1971
|
+
function metricsExtrasFromActionError(error) {
|
|
1972
|
+
const details = error.details;
|
|
1973
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) {
|
|
1974
|
+
return {};
|
|
1975
|
+
}
|
|
1976
|
+
const obj = details;
|
|
1977
|
+
const extras = {};
|
|
1978
|
+
if (typeof obj.retryAfterMs === "number" && Number.isFinite(obj.retryAfterMs)) {
|
|
1979
|
+
extras.retryAfterMs = obj.retryAfterMs;
|
|
1980
|
+
}
|
|
1981
|
+
if (typeof obj.discordStatus === "number" && Number.isFinite(obj.discordStatus)) {
|
|
1982
|
+
extras.discordStatus = obj.discordStatus;
|
|
1983
|
+
}
|
|
1984
|
+
if (typeof obj.discordCode === "number" && Number.isFinite(obj.discordCode)) {
|
|
1985
|
+
extras.discordCode = obj.discordCode;
|
|
1986
|
+
}
|
|
1987
|
+
return extras;
|
|
1988
|
+
}
|
|
1938
1989
|
function connectBotBridge(options) {
|
|
1939
1990
|
assertAppBridgeOptions(options);
|
|
1940
1991
|
const logger = withLogger(options.logger);
|
|
@@ -2243,7 +2294,7 @@ function connectBotBridge(options) {
|
|
|
2243
2294
|
requestId,
|
|
2244
2295
|
durationMs: Date.now() - started,
|
|
2245
2296
|
ok: result.ok,
|
|
2246
|
-
...!result.ok ? { errorCode: result.error.code } : {}
|
|
2297
|
+
...!result.ok ? { errorCode: result.error.code, ...metricsExtrasFromActionError(result.error) } : {}
|
|
2247
2298
|
});
|
|
2248
2299
|
return result;
|
|
2249
2300
|
} catch (error) {
|