@newhomestar/sdk 0.7.9 → 0.7.11

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.
Files changed (2) hide show
  1. package/dist/events.js +35 -15
  2. package/package.json +1 -1
package/dist/events.js CHANGED
@@ -54,7 +54,7 @@ async function _relayRow(db, row, eventsUrl, serviceToken) {
54
54
  ...row.payload,
55
55
  idempotency_key: row.idempotencyKey,
56
56
  };
57
- const res = await fetch(`${eventsUrl}/events/queue`, {
57
+ const res = await fetch(`${eventsUrl}/queue`, {
58
58
  method: 'POST',
59
59
  headers: {
60
60
  'Authorization': `Bearer ${serviceToken}`,
@@ -317,7 +317,7 @@ export async function logEvent(payload) {
317
317
  const eventsUrl = _getEnvOrThrow('NOVA_EVENTS_SERVICE_URL', 'logEvent');
318
318
  const serviceToken = _getEnvOrThrow('NOVA_SERVICE_TOKEN', 'logEvent');
319
319
  const serviceSlug = process.env.NOVA_SERVICE_SLUG;
320
- const res = await fetch(`${eventsUrl}/events`, {
320
+ const res = await fetch(`${eventsUrl}`, {
321
321
  method: 'POST',
322
322
  headers: {
323
323
  'Authorization': `Bearer ${serviceToken}`,
@@ -349,7 +349,7 @@ export async function queueEvent(payload) {
349
349
  const eventsUrl = _getEnvOrThrow('NOVA_EVENTS_SERVICE_URL', 'queueEvent');
350
350
  const serviceToken = _getEnvOrThrow('NOVA_SERVICE_TOKEN', 'queueEvent');
351
351
  const serviceSlug = process.env.NOVA_SERVICE_SLUG;
352
- const res = await fetch(`${eventsUrl}/events/queue`, {
352
+ const res = await fetch(`${eventsUrl}/queue`, {
353
353
  method: 'POST',
354
354
  headers: {
355
355
  'Authorization': `Bearer ${serviceToken}`,
@@ -565,7 +565,7 @@ export async function withInboundEvent(db, msg, handler) {
565
565
  // Safe because: if ACK fails, PGMQ re-delivers, UNIQUE(msg_id) blocks
566
566
  // re-processing, and we just ACK again on the duplicate delivery.
567
567
  try {
568
- const ackRes = await fetch(`${eventsUrl}/events/queue/ack`, {
568
+ const ackRes = await fetch(`${eventsUrl}/queue/ack`, {
569
569
  method: 'POST',
570
570
  headers: {
571
571
  'Authorization': `Bearer ${serviceToken}`,
@@ -656,6 +656,14 @@ export function startInboundConsumer(db, options) {
656
656
  }
657
657
  const abort = new AbortController();
658
658
  let reconnectAttempt = 0;
659
+ /** Log nested error causes for rich diagnostics (undici stores ECONNREFUSED etc. in err.cause) */
660
+ function _logErrorCause(err, depth = 0) {
661
+ if (!err?.cause || depth > 3)
662
+ return;
663
+ const indent = ' ' + ' └─'.repeat(depth + 1) + ' ';
664
+ console.error(`[nova/events] ${indent}cause: ${String(err.cause).slice(0, 500)}`);
665
+ _logErrorCause(err.cause, depth + 1);
666
+ }
659
667
  /** Derive the event topic from a raw PGMQ message payload */
660
668
  function _deriveEventType(payload) {
661
669
  if (typeof payload.topic === 'string')
@@ -668,7 +676,7 @@ export function startInboundConsumer(db, options) {
668
676
  /** Send a NACK to extend the visibility timeout with exponential backoff */
669
677
  async function _nack(msgId, readCt) {
670
678
  try {
671
- await fetch(`${eventsUrl}/events/queue/nack`, {
679
+ await fetch(`${eventsUrl}/queue/nack`, {
672
680
  method: 'POST',
673
681
  headers: {
674
682
  'Authorization': `Bearer ${serviceToken}`,
@@ -702,7 +710,7 @@ export function startInboundConsumer(db, options) {
702
710
  // No handler registered and no default — skip + ACK to unblock queue
703
711
  console.log(`[nova/events] No handler for event type "${eventType}" (msg_id=${msgId}) — ACK and skip`);
704
712
  try {
705
- await fetch(`${eventsUrl}/events/queue/ack`, {
713
+ await fetch(`${eventsUrl}/queue/ack`, {
706
714
  method: 'POST',
707
715
  headers: { 'Authorization': `Bearer ${serviceToken}`, 'Content-Type': 'application/json' },
708
716
  body: JSON.stringify({ queue_name: queueName, msg_id: msgId }),
@@ -779,20 +787,25 @@ export function startInboundConsumer(db, options) {
779
787
  // Node.js's built-in fetch (undici) has a default bodyTimeout of 300s (5 minutes)
780
788
  // which kills long-lived SSE streams as if the response body "stalled".
781
789
  // Setting bodyTimeout: 0 disables this limit for the stream connection.
782
- // headersTimeout: 30_000 still guards against slow initial connection.
790
+ // headersTimeout: 120_000 guards against slow initial connections (was 30s — too short
791
+ // for containers that may take time to establish TCP to the Events Service).
783
792
  const sseAgent = new UndiciAgent({
784
793
  bodyTimeout: 0,
785
- headersTimeout: 30_000,
794
+ headersTimeout: 120_000,
786
795
  keepAliveTimeout: 120_000,
787
796
  keepAliveMaxTimeout: 600_000,
788
797
  });
789
798
  /** Main loop: connect, consume, reconnect */
790
799
  async function _connect() {
800
+ // Log config once on startup so container logs show what we're connecting to
801
+ const tokenSuffix = serviceToken ? `***${serviceToken.slice(-4)}` : 'MISSING';
802
+ console.log(`[nova/events] SSE consumer starting — url=${eventsUrl} ` +
803
+ `token=${tokenSuffix} queue=${queueName} vt=${vt}s headersTimeout=120s`);
791
804
  while (!abort.signal.aborted) {
792
- const streamUrl = `${eventsUrl}/events/queue/stream?queue=${encodeURIComponent(queueName)}&vt=${vt}`;
805
+ const streamUrl = `${eventsUrl}/queue/stream?queue=${encodeURIComponent(queueName)}&vt=${vt}`;
806
+ const connectStart = Date.now();
793
807
  try {
794
- console.log(`[nova/events] Connecting to SSE queue "${queueName}" ` +
795
- `(attempt ${reconnectAttempt + 1})…`);
808
+ console.log(`[nova/events] Connecting to SSE stream (attempt ${reconnectAttempt + 1}): ${streamUrl}`);
796
809
  const response = await fetch(streamUrl, {
797
810
  headers: { 'Authorization': `Bearer ${serviceToken}`, 'Accept': 'text/event-stream' },
798
811
  signal: abort.signal,
@@ -806,8 +819,9 @@ export function startInboundConsumer(db, options) {
806
819
  throw new Error(`SSE connect failed: HTTP ${response.status} — ${text.slice(0, 200)}`);
807
820
  }
808
821
  // Successfully connected — reset reconnect counter
822
+ const ttfb = Date.now() - connectStart;
809
823
  reconnectAttempt = 0;
810
- console.log(`[nova/events] SSE consumer connected to queue "${queueName}"`);
824
+ console.log(`[nova/events] SSE consumer connected to queue "${queueName}" (TTFB: ${ttfb}ms)`);
811
825
  // Consume the stream; catch stream-level errors separately from
812
826
  // connection-level errors so they don't trigger exponential backoff.
813
827
  // A stream dying mid-flight (e.g. proxy timeout) is a normal lifecycle
@@ -821,7 +835,9 @@ export function startInboundConsumer(db, options) {
821
835
  const isAbortError = streamErr?.name === 'AbortError' || streamErr?.code === 20;
822
836
  if (isAbortError)
823
837
  return;
824
- console.error(`[nova/events] SSE stream error for queue "${queueName}":`, String(streamErr).slice(0, 300));
838
+ const elapsed = Date.now() - connectStart;
839
+ console.error(`[nova/events] SSE stream error for queue "${queueName}" (after ${elapsed}ms):`, String(streamErr).slice(0, 500));
840
+ _logErrorCause(streamErr);
825
841
  }
826
842
  if (abort.signal.aborted) {
827
843
  console.log(`[nova/events] SSE consumer for queue "${queueName}" shut down gracefully`);
@@ -829,7 +845,9 @@ export function startInboundConsumer(db, options) {
829
845
  }
830
846
  // Stream ended (normal close OR proxy timeout). The connection WAS
831
847
  // established, so no exponential backoff — reconnect in a fixed 1s.
832
- console.log(`[nova/events] SSE stream for queue "${queueName}" closed — reconnecting in 1000ms…`);
848
+ const totalDuration = Date.now() - connectStart;
849
+ console.log(`[nova/events] SSE stream for queue "${queueName}" closed after ${totalDuration}ms — ` +
850
+ `reconnecting in 1000ms…`);
833
851
  await new Promise((resolve) => {
834
852
  const t = setTimeout(resolve, 1_000);
835
853
  abort.signal.addEventListener('abort', () => { clearTimeout(t); resolve(); }, { once: true });
@@ -844,7 +862,9 @@ export function startInboundConsumer(db, options) {
844
862
  const isAbortError = err?.name === 'AbortError' || err?.code === 20;
845
863
  if (isAbortError)
846
864
  return;
847
- console.error(`[nova/events] SSE connection error for queue "${queueName}":`, String(err).slice(0, 300));
865
+ const elapsed = Date.now() - connectStart;
866
+ console.error(`[nova/events] SSE connection error for queue "${queueName}" (failed after ${elapsed}ms):`, String(err).slice(0, 500));
867
+ _logErrorCause(err);
848
868
  }
849
869
  // Exponential backoff only for connection-level failures (fetch threw or
850
870
  // server returned non-200). Stream-level drops use the fixed 1s above.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newhomestar/sdk",
3
- "version": "0.7.9",
3
+ "version": "0.7.11",
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": {