dicom-curate 0.26.1 → 0.26.2

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.
@@ -79661,6 +79661,30 @@ function curateDict(inputFilePath, dicomData, mappingOptions) {
79661
79661
  return { dicomData: mappedDicomData, mapResults: (0, import_lodash4.cloneDeep)(mapResults) };
79662
79662
  }
79663
79663
 
79664
+ // src/fetchWithRetry.ts
79665
+ var MAX_ATTEMPTS = 5;
79666
+ var BASE_DELAY_MS = 1e3;
79667
+ var BACKOFF_MULTIPLIER = 3;
79668
+ async function fetchWithRetry(...args) {
79669
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
79670
+ try {
79671
+ return await fetch(...args);
79672
+ } catch (error2) {
79673
+ const isNetworkError = error2 instanceof TypeError;
79674
+ const isLastAttempt = attempt === MAX_ATTEMPTS;
79675
+ if (!isNetworkError || isLastAttempt) {
79676
+ throw error2;
79677
+ }
79678
+ const delayMs = BASE_DELAY_MS * BACKOFF_MULTIPLIER ** (attempt - 1);
79679
+ console.warn(
79680
+ `fetch attempt ${attempt}/${MAX_ATTEMPTS} failed: ${error2.message}. Retrying in ${delayMs}ms...`
79681
+ );
79682
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
79683
+ }
79684
+ }
79685
+ throw new Error("fetchWithRetry: unreachable");
79686
+ }
79687
+
79664
79688
  // src/hash.ts
79665
79689
  var import_md5 = __toESM(require_md5(), 1);
79666
79690
  var import_js_crc = __toESM(require_crc(), 1);
@@ -80034,7 +80058,7 @@ async function curateOne({
80034
80058
  }
80035
80059
  if (postMappedHashHeader && postMappedHash)
80036
80060
  headers[postMappedHashHeader] = postMappedHash;
80037
- const resp = await fetch(uploadUrl, {
80061
+ const resp = await fetchWithRetry(uploadUrl, {
80038
80062
  method: "PUT",
80039
80063
  headers,
80040
80064
  body: clonedMapResults.mappedBlob
@@ -73370,6 +73370,30 @@ function curateDict(inputFilePath, dicomData, mappingOptions) {
73370
73370
  return { dicomData: mappedDicomData, mapResults: (0, import_lodash4.cloneDeep)(mapResults) };
73371
73371
  }
73372
73372
 
73373
+ // src/fetchWithRetry.ts
73374
+ var MAX_ATTEMPTS = 5;
73375
+ var BASE_DELAY_MS = 1e3;
73376
+ var BACKOFF_MULTIPLIER = 3;
73377
+ async function fetchWithRetry(...args) {
73378
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
73379
+ try {
73380
+ return await fetch(...args);
73381
+ } catch (error2) {
73382
+ const isNetworkError = error2 instanceof TypeError;
73383
+ const isLastAttempt = attempt === MAX_ATTEMPTS;
73384
+ if (!isNetworkError || isLastAttempt) {
73385
+ throw error2;
73386
+ }
73387
+ const delayMs = BASE_DELAY_MS * BACKOFF_MULTIPLIER ** (attempt - 1);
73388
+ console.warn(
73389
+ `fetch attempt ${attempt}/${MAX_ATTEMPTS} failed: ${error2.message}. Retrying in ${delayMs}ms...`
73390
+ );
73391
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
73392
+ }
73393
+ }
73394
+ throw new Error("fetchWithRetry: unreachable");
73395
+ }
73396
+
73373
73397
  // src/hash.ts
73374
73398
  var import_md5 = __toESM(require_md5(), 1);
73375
73399
  var import_js_crc = __toESM(require_crc(), 1);
@@ -73743,7 +73767,7 @@ async function curateOne({
73743
73767
  }
73744
73768
  if (postMappedHashHeader && postMappedHash)
73745
73769
  headers[postMappedHashHeader] = postMappedHash;
73746
- const resp = await fetch(uploadUrl, {
73770
+ const resp = await fetchWithRetry(uploadUrl, {
73747
73771
  method: "PUT",
73748
73772
  headers,
73749
73773
  body: clonedMapResults.mappedBlob
@@ -0,0 +1,26 @@
1
+ // src/fetchWithRetry.ts
2
+ var MAX_ATTEMPTS = 5;
3
+ var BASE_DELAY_MS = 1e3;
4
+ var BACKOFF_MULTIPLIER = 3;
5
+ async function fetchWithRetry(...args) {
6
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
7
+ try {
8
+ return await fetch(...args);
9
+ } catch (error) {
10
+ const isNetworkError = error instanceof TypeError;
11
+ const isLastAttempt = attempt === MAX_ATTEMPTS;
12
+ if (!isNetworkError || isLastAttempt) {
13
+ throw error;
14
+ }
15
+ const delayMs = BASE_DELAY_MS * BACKOFF_MULTIPLIER ** (attempt - 1);
16
+ console.warn(
17
+ `fetch attempt ${attempt}/${MAX_ATTEMPTS} failed: ${error.message}. Retrying in ${delayMs}ms...`
18
+ );
19
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
20
+ }
21
+ }
22
+ throw new Error("fetchWithRetry: unreachable");
23
+ }
24
+ export {
25
+ fetchWithRetry
26
+ };
package/dist/esm/index.js CHANGED
@@ -81221,6 +81221,30 @@ function curateDict(inputFilePath, dicomData, mappingOptions) {
81221
81221
  return { dicomData: mappedDicomData, mapResults: (0, import_lodash4.cloneDeep)(mapResults) };
81222
81222
  }
81223
81223
 
81224
+ // src/fetchWithRetry.ts
81225
+ var MAX_ATTEMPTS = 5;
81226
+ var BASE_DELAY_MS = 1e3;
81227
+ var BACKOFF_MULTIPLIER = 3;
81228
+ async function fetchWithRetry(...args) {
81229
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
81230
+ try {
81231
+ return await fetch(...args);
81232
+ } catch (error2) {
81233
+ const isNetworkError = error2 instanceof TypeError;
81234
+ const isLastAttempt = attempt === MAX_ATTEMPTS;
81235
+ if (!isNetworkError || isLastAttempt) {
81236
+ throw error2;
81237
+ }
81238
+ const delayMs = BASE_DELAY_MS * BACKOFF_MULTIPLIER ** (attempt - 1);
81239
+ console.warn(
81240
+ `fetch attempt ${attempt}/${MAX_ATTEMPTS} failed: ${error2.message}. Retrying in ${delayMs}ms...`
81241
+ );
81242
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
81243
+ }
81244
+ }
81245
+ throw new Error("fetchWithRetry: unreachable");
81246
+ }
81247
+
81224
81248
  // src/hash.ts
81225
81249
  var import_md5 = __toESM(require_md5(), 1);
81226
81250
  var import_js_crc = __toESM(require_crc(), 1);
@@ -81594,7 +81618,7 @@ async function curateOne({
81594
81618
  }
81595
81619
  if (postMappedHashHeader && postMappedHash)
81596
81620
  headers[postMappedHashHeader] = postMappedHash;
81597
- const resp = await fetch(uploadUrl, {
81621
+ const resp = await fetchWithRetry(uploadUrl, {
81598
81622
  method: "PUT",
81599
81623
  headers,
81600
81624
  body: clonedMapResults.mappedBlob
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Wraps fetch() with retry logic for transient network errors.
3
+ *
4
+ * Retries only on TypeError (network failure — request never reached the
5
+ * server). Does NOT retry on HTTP error responses (4xx, 5xx) since those
6
+ * indicate the request was received and a retry without changing the request
7
+ * would likely produce the same result.
8
+ *
9
+ * Uses exponential backoff (1s, 3s, 9s, 27s, 81s) which also acts as natural
10
+ * backpressure — workers block during backoff, reducing concurrent uploads.
11
+ */
12
+ export declare function fetchWithRetry(...args: Parameters<typeof fetch>): Promise<Response>;
@@ -47934,6 +47934,40 @@
47934
47934
  return { dicomData: mappedDicomData, mapResults: lodashExports.cloneDeep(mapResults) };
47935
47935
  }
47936
47936
 
47937
+ /**
47938
+ * Wraps fetch() with retry logic for transient network errors.
47939
+ *
47940
+ * Retries only on TypeError (network failure — request never reached the
47941
+ * server). Does NOT retry on HTTP error responses (4xx, 5xx) since those
47942
+ * indicate the request was received and a retry without changing the request
47943
+ * would likely produce the same result.
47944
+ *
47945
+ * Uses exponential backoff (1s, 3s, 9s, 27s, 81s) which also acts as natural
47946
+ * backpressure — workers block during backoff, reducing concurrent uploads.
47947
+ */
47948
+ const MAX_ATTEMPTS = 5;
47949
+ const BASE_DELAY_MS = 1000;
47950
+ const BACKOFF_MULTIPLIER = 3;
47951
+ async function fetchWithRetry(...args) {
47952
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
47953
+ try {
47954
+ return await fetch(...args);
47955
+ }
47956
+ catch (error) {
47957
+ const isNetworkError = error instanceof TypeError;
47958
+ const isLastAttempt = attempt === MAX_ATTEMPTS;
47959
+ if (!isNetworkError || isLastAttempt) {
47960
+ throw error;
47961
+ }
47962
+ const delayMs = BASE_DELAY_MS * BACKOFF_MULTIPLIER ** (attempt - 1);
47963
+ console.warn(`fetch attempt ${attempt}/${MAX_ATTEMPTS} failed: ${error.message}. Retrying in ${delayMs}ms...`);
47964
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
47965
+ }
47966
+ }
47967
+ // Unreachable — the loop either returns or throws on the last attempt.
47968
+ throw new Error('fetchWithRetry: unreachable');
47969
+ }
47970
+
47937
47971
  var md5$1 = {exports: {}};
47938
47972
 
47939
47973
  var crypt = {exports: {}};
@@ -49116,7 +49150,7 @@
49116
49150
  }
49117
49151
  if (postMappedHashHeader && postMappedHash)
49118
49152
  headers[postMappedHashHeader] = postMappedHash;
49119
- const resp = await fetch(uploadUrl, {
49153
+ const resp = await fetchWithRetry(uploadUrl, {
49120
49154
  method: 'PUT',
49121
49155
  headers,
49122
49156
  body: clonedMapResults.mappedBlob,
@@ -109949,6 +109983,40 @@
109949
109983
  return { dicomData: mappedDicomData, mapResults: lodashExports.cloneDeep(mapResults) };
109950
109984
  }
109951
109985
 
109986
+ /**
109987
+ * Wraps fetch() with retry logic for transient network errors.
109988
+ *
109989
+ * Retries only on TypeError (network failure — request never reached the
109990
+ * server). Does NOT retry on HTTP error responses (4xx, 5xx) since those
109991
+ * indicate the request was received and a retry without changing the request
109992
+ * would likely produce the same result.
109993
+ *
109994
+ * Uses exponential backoff (1s, 3s, 9s, 27s, 81s) which also acts as natural
109995
+ * backpressure — workers block during backoff, reducing concurrent uploads.
109996
+ */
109997
+ const MAX_ATTEMPTS = 5;
109998
+ const BASE_DELAY_MS = 1000;
109999
+ const BACKOFF_MULTIPLIER = 3;
110000
+ async function fetchWithRetry(...args) {
110001
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
110002
+ try {
110003
+ return await fetch(...args);
110004
+ }
110005
+ catch (error) {
110006
+ const isNetworkError = error instanceof TypeError;
110007
+ const isLastAttempt = attempt === MAX_ATTEMPTS;
110008
+ if (!isNetworkError || isLastAttempt) {
110009
+ throw error;
110010
+ }
110011
+ const delayMs = BASE_DELAY_MS * BACKOFF_MULTIPLIER ** (attempt - 1);
110012
+ console.warn(`fetch attempt ${attempt}/${MAX_ATTEMPTS} failed: ${error.message}. Retrying in ${delayMs}ms...`);
110013
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
110014
+ }
110015
+ }
110016
+ // Unreachable — the loop either returns or throws on the last attempt.
110017
+ throw new Error('fetchWithRetry: unreachable');
110018
+ }
110019
+
109952
110020
  var md5$1 = {exports: {}};
109953
110021
 
109954
110022
  var crypt = {exports: {}};
@@ -111133,7 +111201,7 @@
111133
111201
  }
111134
111202
  if (postMappedHashHeader && postMappedHash)
111135
111203
  headers[postMappedHashHeader] = postMappedHash;
111136
- const resp = await fetch(uploadUrl, {
111204
+ const resp = await fetchWithRetry(uploadUrl, {
111137
111205
  method: 'PUT',
111138
111206
  headers,
111139
111207
  body: clonedMapResults.mappedBlob,