@minesa-org/mini-interaction 0.2.21 → 0.2.23
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/clients/MiniInteraction.js +40 -33
- package/package.json +1 -1
|
@@ -1370,8 +1370,13 @@ export class MiniInteraction {
|
|
|
1370
1370
|
* Includes retry logic to handle race conditions where the ACK hasn't reached Discord yet.
|
|
1371
1371
|
*/
|
|
1372
1372
|
async sendFollowUp(token, response, messageId = "@original", retryCount = 0) {
|
|
1373
|
-
const MAX_RETRIES =
|
|
1374
|
-
const BASE_DELAY_MS =
|
|
1373
|
+
const MAX_RETRIES = 5;
|
|
1374
|
+
const BASE_DELAY_MS = 200; // Start with 200ms delay
|
|
1375
|
+
const INITIAL_DELAY_MS = 100; // Wait for ACK to propagate to Discord
|
|
1376
|
+
// On first attempt, add a small delay to allow ACK to propagate
|
|
1377
|
+
if (retryCount === 0) {
|
|
1378
|
+
await new Promise(resolve => setTimeout(resolve, INITIAL_DELAY_MS));
|
|
1379
|
+
}
|
|
1375
1380
|
const isEdit = messageId !== "";
|
|
1376
1381
|
const url = isEdit
|
|
1377
1382
|
? `${DISCORD_BASE_URL}/webhooks/${this.applicationId}/${token}/messages/${messageId}`
|
|
@@ -1401,7 +1406,7 @@ export class MiniInteraction {
|
|
|
1401
1406
|
const errorBody = await fetchResponse.text();
|
|
1402
1407
|
// Check for "Unknown Webhook" error (10015) - this means ACK hasn't reached Discord yet
|
|
1403
1408
|
if (fetchResponse.status === 404 && errorBody.includes("10015") && retryCount < MAX_RETRIES) {
|
|
1404
|
-
const delayMs = BASE_DELAY_MS * Math.pow(2, retryCount); // Exponential backoff:
|
|
1409
|
+
const delayMs = BASE_DELAY_MS * Math.pow(2, retryCount); // Exponential backoff: 200, 400, 800, 1600, 3200ms
|
|
1405
1410
|
console.warn(`[MiniInteraction] Webhook not ready yet, retrying in ${delayMs}ms (attempt ${retryCount + 1}/${MAX_RETRIES})`);
|
|
1406
1411
|
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
1407
1412
|
return this.sendFollowUp(token, response, messageId, retryCount + 1);
|
|
@@ -1583,13 +1588,16 @@ function resolveOAuthConfig(provided) {
|
|
|
1583
1588
|
}
|
|
1584
1589
|
/**
|
|
1585
1590
|
* Wraps a handler function with timeout detection and error handling.
|
|
1591
|
+
*
|
|
1592
|
+
* CRITICAL FOR HTTP INTERACTIONS:
|
|
1593
|
+
* When deferReply() is called, we MUST return the ACK to Discord immediately.
|
|
1594
|
+
* The handler continues executing and sends follow-up via webhook.
|
|
1595
|
+
* The ACK must reach Discord before any webhook PATCH requests can succeed.
|
|
1586
1596
|
*/
|
|
1587
1597
|
function createTimeoutWrapper(handler, timeoutMs, handlerName, enableWarnings = true, ackPromise) {
|
|
1588
1598
|
return async (...args) => {
|
|
1589
1599
|
const startTime = Date.now();
|
|
1590
1600
|
let timeoutId;
|
|
1591
|
-
let ackResult = null;
|
|
1592
|
-
let ackReceived = false;
|
|
1593
1601
|
const timeoutPromise = new Promise((_, reject) => {
|
|
1594
1602
|
timeoutId = setTimeout(() => {
|
|
1595
1603
|
const elapsed = Date.now() - startTime;
|
|
@@ -1597,55 +1605,54 @@ function createTimeoutWrapper(handler, timeoutMs, handlerName, enableWarnings =
|
|
|
1597
1605
|
reject(new Error(`Handler timeout: ${handlerName} exceeded ${timeoutMs}ms limit`));
|
|
1598
1606
|
}, timeoutMs);
|
|
1599
1607
|
});
|
|
1600
|
-
//
|
|
1601
|
-
|
|
1608
|
+
// Start handler execution immediately (don't await yet)
|
|
1609
|
+
const handlerPromise = Promise.resolve(handler(...args));
|
|
1610
|
+
// If we have an ackPromise, race between ACK and timeout
|
|
1611
|
+
// When ACK is received, return IMMEDIATELY so Discord gets the response
|
|
1612
|
+
// Handler continues in background
|
|
1602
1613
|
if (ackPromise) {
|
|
1603
|
-
|
|
1604
|
-
ackResult =
|
|
1605
|
-
|
|
1606
|
-
|
|
1614
|
+
try {
|
|
1615
|
+
const ackResult = await Promise.race([
|
|
1616
|
+
ackPromise,
|
|
1617
|
+
timeoutPromise,
|
|
1618
|
+
]);
|
|
1619
|
+
// ACK received! Clear timeout and return immediately
|
|
1607
1620
|
if (timeoutId) {
|
|
1608
1621
|
clearTimeout(timeoutId);
|
|
1609
|
-
timeoutId = undefined;
|
|
1610
1622
|
}
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1623
|
+
// Handler continues in background - attach error handler
|
|
1624
|
+
handlerPromise.catch((error) => {
|
|
1625
|
+
console.error(`[MiniInteraction] ${handlerName} background execution failed:`, error instanceof Error ? error.message : String(error));
|
|
1626
|
+
});
|
|
1627
|
+
return ackResult;
|
|
1628
|
+
}
|
|
1629
|
+
catch (error) {
|
|
1630
|
+
// Timeout occurred before ACK - fall through to check handler
|
|
1631
|
+
if (timeoutId) {
|
|
1632
|
+
clearTimeout(timeoutId);
|
|
1633
|
+
}
|
|
1634
|
+
throw error;
|
|
1635
|
+
}
|
|
1614
1636
|
}
|
|
1637
|
+
// No ACK promise - wait for handler with timeout
|
|
1615
1638
|
try {
|
|
1616
|
-
// ALWAYS wait for handler to complete
|
|
1617
1639
|
const handlerResult = await Promise.race([
|
|
1618
|
-
|
|
1640
|
+
handlerPromise,
|
|
1619
1641
|
timeoutPromise,
|
|
1620
1642
|
]);
|
|
1621
1643
|
if (timeoutId) {
|
|
1622
1644
|
clearTimeout(timeoutId);
|
|
1623
1645
|
}
|
|
1624
1646
|
const elapsed = Date.now() - startTime;
|
|
1625
|
-
if (enableWarnings && elapsed > timeoutMs * 0.8
|
|
1647
|
+
if (enableWarnings && elapsed > timeoutMs * 0.8) {
|
|
1626
1648
|
console.warn(`[MiniInteraction] ${handlerName} completed in ${elapsed}ms (${Math.round((elapsed / timeoutMs) * 100)}% of timeout limit)`);
|
|
1627
1649
|
}
|
|
1628
|
-
// If we got an ACK, return that for the HTTP response
|
|
1629
|
-
// The handler has completed at this point so all background work is done
|
|
1630
|
-
if (ackReceived && ackResult !== null) {
|
|
1631
|
-
return ackResult;
|
|
1632
|
-
}
|
|
1633
1650
|
return handlerResult;
|
|
1634
1651
|
}
|
|
1635
1652
|
catch (error) {
|
|
1636
1653
|
if (timeoutId) {
|
|
1637
1654
|
clearTimeout(timeoutId);
|
|
1638
1655
|
}
|
|
1639
|
-
// If handler timed out but we have an ACK, return the ACK
|
|
1640
|
-
// This allows deferReply to work even if later operations are slow
|
|
1641
|
-
if (error instanceof Error && error.message.includes("Handler timeout")) {
|
|
1642
|
-
if (ackReceived && ackResult !== null) {
|
|
1643
|
-
console.warn(`[MiniInteraction] ${handlerName} timed out but ACK was already captured. ` +
|
|
1644
|
-
`Background work may not complete in serverless environments.`);
|
|
1645
|
-
return ackResult;
|
|
1646
|
-
}
|
|
1647
|
-
throw error;
|
|
1648
|
-
}
|
|
1649
1656
|
console.error(`[MiniInteraction] ${handlerName} failed:`, error);
|
|
1650
1657
|
throw error;
|
|
1651
1658
|
}
|
package/package.json
CHANGED