@newhomestar/sdk 0.7.7 → 0.7.9
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/events.js +41 -3
- package/package.json +2 -1
package/dist/events.js
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
// NOVA_SERVICE_SLUG — Stamped as source_service on every event
|
|
32
32
|
// =====================================================================
|
|
33
33
|
import dotenv from 'dotenv';
|
|
34
|
+
import { Agent as UndiciAgent } from 'undici';
|
|
34
35
|
// Load .env.local in dev if NOVA_EVENTS_SERVICE_URL not already set
|
|
35
36
|
if (!process.env.NOVA_EVENTS_SERVICE_URL) {
|
|
36
37
|
dotenv.config({ path: '.env.local', override: false });
|
|
@@ -774,6 +775,17 @@ export function startInboundConsumer(db, options) {
|
|
|
774
775
|
reader.releaseLock();
|
|
775
776
|
}
|
|
776
777
|
}
|
|
778
|
+
// Dedicated undici Agent for SSE connections.
|
|
779
|
+
// Node.js's built-in fetch (undici) has a default bodyTimeout of 300s (5 minutes)
|
|
780
|
+
// which kills long-lived SSE streams as if the response body "stalled".
|
|
781
|
+
// Setting bodyTimeout: 0 disables this limit for the stream connection.
|
|
782
|
+
// headersTimeout: 30_000 still guards against slow initial connection.
|
|
783
|
+
const sseAgent = new UndiciAgent({
|
|
784
|
+
bodyTimeout: 0,
|
|
785
|
+
headersTimeout: 30_000,
|
|
786
|
+
keepAliveTimeout: 120_000,
|
|
787
|
+
keepAliveMaxTimeout: 600_000,
|
|
788
|
+
});
|
|
777
789
|
/** Main loop: connect, consume, reconnect */
|
|
778
790
|
async function _connect() {
|
|
779
791
|
while (!abort.signal.aborted) {
|
|
@@ -784,6 +796,10 @@ export function startInboundConsumer(db, options) {
|
|
|
784
796
|
const response = await fetch(streamUrl, {
|
|
785
797
|
headers: { 'Authorization': `Bearer ${serviceToken}`, 'Accept': 'text/event-stream' },
|
|
786
798
|
signal: abort.signal,
|
|
799
|
+
// @ts-ignore — undici-specific dispatcher: disables the 5-minute bodyTimeout
|
|
800
|
+
// that kills SSE streams. Standard fetch API does not expose this option,
|
|
801
|
+
// but Node.js's undici-powered fetch accepts it.
|
|
802
|
+
dispatcher: sseAgent,
|
|
787
803
|
});
|
|
788
804
|
if (!response.ok) {
|
|
789
805
|
const text = await response.text().catch(() => '');
|
|
@@ -792,12 +808,33 @@ export function startInboundConsumer(db, options) {
|
|
|
792
808
|
// Successfully connected — reset reconnect counter
|
|
793
809
|
reconnectAttempt = 0;
|
|
794
810
|
console.log(`[nova/events] SSE consumer connected to queue "${queueName}"`);
|
|
795
|
-
|
|
811
|
+
// Consume the stream; catch stream-level errors separately from
|
|
812
|
+
// connection-level errors so they don't trigger exponential backoff.
|
|
813
|
+
// A stream dying mid-flight (e.g. proxy timeout) is a normal lifecycle
|
|
814
|
+
// event — reconnect immediately with a short fixed delay.
|
|
815
|
+
try {
|
|
816
|
+
await _consumeStream(response);
|
|
817
|
+
}
|
|
818
|
+
catch (streamErr) {
|
|
819
|
+
if (abort.signal.aborted)
|
|
820
|
+
return;
|
|
821
|
+
const isAbortError = streamErr?.name === 'AbortError' || streamErr?.code === 20;
|
|
822
|
+
if (isAbortError)
|
|
823
|
+
return;
|
|
824
|
+
console.error(`[nova/events] SSE stream error for queue "${queueName}":`, String(streamErr).slice(0, 300));
|
|
825
|
+
}
|
|
796
826
|
if (abort.signal.aborted) {
|
|
797
827
|
console.log(`[nova/events] SSE consumer for queue "${queueName}" shut down gracefully`);
|
|
798
828
|
return;
|
|
799
829
|
}
|
|
800
|
-
|
|
830
|
+
// Stream ended (normal close OR proxy timeout). The connection WAS
|
|
831
|
+
// established, so no exponential backoff — reconnect in a fixed 1s.
|
|
832
|
+
console.log(`[nova/events] SSE stream for queue "${queueName}" closed — reconnecting in 1000ms…`);
|
|
833
|
+
await new Promise((resolve) => {
|
|
834
|
+
const t = setTimeout(resolve, 1_000);
|
|
835
|
+
abort.signal.addEventListener('abort', () => { clearTimeout(t); resolve(); }, { once: true });
|
|
836
|
+
});
|
|
837
|
+
continue; // skip exponential backoff below
|
|
801
838
|
}
|
|
802
839
|
catch (err) {
|
|
803
840
|
if (abort.signal.aborted) {
|
|
@@ -809,7 +846,8 @@ export function startInboundConsumer(db, options) {
|
|
|
809
846
|
return;
|
|
810
847
|
console.error(`[nova/events] SSE connection error for queue "${queueName}":`, String(err).slice(0, 300));
|
|
811
848
|
}
|
|
812
|
-
// Exponential backoff
|
|
849
|
+
// Exponential backoff only for connection-level failures (fetch threw or
|
|
850
|
+
// server returned non-200). Stream-level drops use the fixed 1s above.
|
|
813
851
|
const delayMs = Math.min(1_000 * Math.pow(2, reconnectAttempt), maxReconnectDelay);
|
|
814
852
|
reconnectAttempt++;
|
|
815
853
|
console.log(`[nova/events] Reconnecting queue "${queueName}" in ${delayMs}ms…`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newhomestar/sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.9",
|
|
4
4
|
"description": "Type-safe SDK for building Nova pipelines (workers & functions)",
|
|
5
5
|
"homepage": "https://github.com/newhomestar/nova-node-sdk#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"dotenv": "^16.4.3",
|
|
45
45
|
"express": "^4.18.2",
|
|
46
46
|
"express-oauth2-jwt-bearer": "^1.7.4",
|
|
47
|
+
"undici": "^7.24.4",
|
|
47
48
|
"yaml": "^2.7.1"
|
|
48
49
|
},
|
|
49
50
|
"peerDependencies": {
|