@prodcycle/prodcycle 0.6.2 → 0.6.3

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/api-client.js +16 -4
  2. package/package.json +1 -1
@@ -322,6 +322,7 @@ class ComplianceApiClient {
322
322
  let lastError = null;
323
323
  for (let attempt = 0; attempt < MAX_RETRY_ATTEMPTS; attempt++) {
324
324
  let response;
325
+ let responseText;
325
326
  try {
326
327
  response = await fetch(url, {
327
328
  method,
@@ -332,11 +333,23 @@ class ComplianceApiClient {
332
333
  ...(data !== null ? { body: JSON.stringify(data) } : {}),
333
334
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
334
335
  });
336
+ // Body read inside the same try/catch as fetch() because undici can
337
+ // throw mid-stream (ALB drops the connection, abort signal fires
338
+ // during body read, server sends a partial response). Pre-fix this
339
+ // leaked out of `request()` as an unhandled error instead of being
340
+ // retried — long chunked-session scans were especially exposed,
341
+ // since every `appendChunk` call is its own request and any one
342
+ // mid-stream drop torpedoed the whole scan. The chunk write is
343
+ // idempotent on the server (unique `(scan_id, fingerprint)` index),
344
+ // so retry is safe. Mirror of the Python client's catch over
345
+ // `OSError, http.client.HTTPException` — keep both in lockstep.
346
+ responseText = await response.text();
335
347
  }
336
348
  catch (networkErr) {
337
- // Connection-level failures (DNS, TCP, TLS). Treat as retryable up
338
- // to the same cap as 503 the server may be momentarily down or
339
- // the network blip may resolve.
349
+ // Connection-level failures (DNS, TCP, TLS) OR body-read-level
350
+ // failures (abort, RST mid-stream). Treat as retryable up to the
351
+ // same cap as 503 — the server may be momentarily down or the
352
+ // network blip may resolve.
340
353
  lastError =
341
354
  networkErr instanceof Error ? networkErr : new Error(String(networkErr));
342
355
  if (attempt < MAX_RETRY_ATTEMPTS - 1) {
@@ -345,7 +358,6 @@ class ComplianceApiClient {
345
358
  }
346
359
  throw new Error(`Failed to connect to ProdCycle API: ${lastError.message}`);
347
360
  }
348
- const responseText = await response.text();
349
361
  let parsed = null;
350
362
  try {
351
363
  parsed = responseText ? JSON.parse(responseText) : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prodcycle/prodcycle",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Multi-framework policy-as-code compliance scanner for infrastructure and application code.",
5
5
  "homepage": "https://docs.prodcycle.com",
6
6
  "repository": {