cod-dicomweb-server 1.3.10 → 1.3.12

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.
@@ -7,7 +7,7 @@ declare class CodDicomWebServer {
7
7
  private metadataManager;
8
8
  private seriesUidFileUrls;
9
9
  constructor(args?: {
10
- maxWorkerFetchSize?: number;
10
+ maxCacheSize?: number;
11
11
  domain?: string;
12
12
  disableWorker?: boolean;
13
13
  enableLocalCache?: boolean;
@@ -11,7 +11,7 @@ import { download, getDirectoryHandle } from '../fileAccessSystemUtils';
11
11
  class CodDicomWebServer {
12
12
  filePromises = {};
13
13
  options = {
14
- maxWorkerFetchSize: Infinity,
14
+ maxCacheSize: 4 * 1024 * 1024 * 1024,
15
15
  domain: constants.url.DOMAIN,
16
16
  enableLocalCache: false
17
17
  };
@@ -19,19 +19,19 @@ class CodDicomWebServer {
19
19
  metadataManager;
20
20
  seriesUidFileUrls = {};
21
21
  constructor(args = {}) {
22
- const { maxWorkerFetchSize, domain, disableWorker, enableLocalCache } = args;
23
- this.options.maxWorkerFetchSize = maxWorkerFetchSize || this.options.maxWorkerFetchSize;
22
+ const { maxCacheSize, domain, disableWorker, enableLocalCache } = args;
23
+ this.options.maxCacheSize = maxCacheSize || this.options.maxCacheSize;
24
24
  this.options.domain = domain || this.options.domain;
25
25
  this.options.enableLocalCache = !!enableLocalCache;
26
26
  const fileStreamingScriptName = constants.dataRetrieval.FILE_STREAMING_WORKER_NAME;
27
27
  const filePartialScriptName = constants.dataRetrieval.FILE_PARTIAL_WORKER_NAME;
28
- this.fileManager = new FileManager({ fileStreamingScriptName });
28
+ this.fileManager = new FileManager();
29
29
  this.metadataManager = new MetadataManager();
30
30
  if (disableWorker) {
31
31
  const dataRetrievalManager = getDataRetrievalManager();
32
32
  dataRetrievalManager.setDataRetrieverMode(Enums.DataRetrieveMode.REQUEST);
33
33
  }
34
- register({ fileStreamingScriptName, filePartialScriptName }, this.options.maxWorkerFetchSize);
34
+ register({ fileStreamingScriptName, filePartialScriptName });
35
35
  }
36
36
  setOptions = (newOptions) => {
37
37
  Object.keys(newOptions).forEach((key) => {
@@ -110,10 +110,6 @@ class CodDicomWebServer {
110
110
  if (pixelDataElement.hadUndefinedLength && pixelDataElement.fragments) {
111
111
  ({ position: dataOffset, length } = pixelDataElement.fragments[0]);
112
112
  }
113
- else {
114
- // Adding 8 bytes for 4 bytes tag + 4 bytes length for uncomppressed pixelData
115
- dataOffset += 8;
116
- }
117
113
  return arraybuffer.slice(dataOffset, dataOffset + length);
118
114
  }
119
115
  });
@@ -167,15 +163,11 @@ class CodDicomWebServer {
167
163
  });
168
164
  }
169
165
  const directoryHandle = this.options.enableLocalCache && (await getDirectoryHandle());
170
- const { maxWorkerFetchSize } = this.getOptions();
171
166
  const dataRetrievalManager = getDataRetrievalManager();
172
- const { FILE_STREAMING_WORKER_NAME, FILE_PARTIAL_WORKER_NAME, THRESHOLD } = constants.dataRetrieval;
167
+ const { FILE_STREAMING_WORKER_NAME, FILE_PARTIAL_WORKER_NAME } = constants.dataRetrieval;
173
168
  let tarPromise;
174
169
  if (!this.filePromises[fileUrl]) {
175
170
  tarPromise = new Promise((resolveFile, rejectFile) => {
176
- if (this.fileManager.getTotalSize() + THRESHOLD > maxWorkerFetchSize) {
177
- throw new CustomError(`CodDicomWebServer.ts: Maximum size(${maxWorkerFetchSize}) for fetching files reached`);
178
- }
179
171
  const FetchTypeEnum = constants.Enums.FetchType;
180
172
  if (fetchType === FetchTypeEnum.API_OPTIMIZED) {
181
173
  const handleFirstChunk = (evt) => {
@@ -255,7 +247,7 @@ class CodDicomWebServer {
255
247
  rejectRequest(evt.message);
256
248
  throw evt.error;
257
249
  }
258
- const { url, position, chunk, isAppending } = evt.data;
250
+ const { url, position, chunk, totalLength, isAppending } = evt.data;
259
251
  if (isAppending) {
260
252
  if (chunk) {
261
253
  this.fileManager.append(url, chunk, position);
@@ -264,6 +256,14 @@ class CodDicomWebServer {
264
256
  this.fileManager.setPosition(url, position);
265
257
  }
266
258
  }
259
+ else {
260
+ // The full empty file including with first chunk have been stored to fileManager
261
+ // by the worker listener in the file promise.
262
+ // So, we check whether the cache exceeded the limit here.
263
+ if (this.fileManager.getTotalSize() > this.options.maxCacheSize) {
264
+ this.fileManager.decacheNecessaryBytes(url, totalLength);
265
+ }
266
+ }
267
267
  if (!requestResolved && url === fileUrl && offsets && position > offsets.endByte) {
268
268
  try {
269
269
  const file = this.fileManager.get(url, offsets);
@@ -11,6 +11,7 @@ export declare class CustomMessageEvent extends MessageEvent<{
11
11
  chunk?: Uint8Array;
12
12
  isAppending?: boolean;
13
13
  fileArraybuffer?: Uint8Array;
14
+ totalLength: number;
14
15
  offsets?: {
15
16
  startByte: number;
16
17
  endByte: number;
@@ -1,4 +1,4 @@
1
1
  export declare function register(workerNames: {
2
2
  fileStreamingScriptName: string;
3
3
  filePartialScriptName: string;
4
- }, maxFetchSize: number): void;
4
+ }): void;
@@ -2,7 +2,7 @@ import { Enums } from '../constants';
2
2
  import { getDataRetrievalManager } from './dataRetrievalManager';
3
3
  import filePartial from './scripts/filePartial';
4
4
  import fileStreaming from './scripts/fileStreaming';
5
- export function register(workerNames, maxFetchSize) {
5
+ export function register(workerNames) {
6
6
  const { fileStreamingScriptName, filePartialScriptName } = workerNames;
7
7
  const dataRetrievalManager = getDataRetrievalManager();
8
8
  if (dataRetrievalManager.getDataRetrieverMode() === Enums.DataRetrieveMode.REQUEST) {
@@ -21,5 +21,4 @@ export function register(workerNames, maxFetchSize) {
21
21
  });
22
22
  dataRetrievalManager.register(filePartialScriptName, partialWorkerFn);
23
23
  }
24
- dataRetrievalManager.executeTask(fileStreamingScriptName, 'setMaxFetchSize', maxFetchSize);
25
24
  }
@@ -1,8 +1,4 @@
1
1
  declare const fileStreaming: {
2
- maxFetchSize: number;
3
- fetchedSize: number;
4
- setMaxFetchSize(size: number): void;
5
- decreaseFetchedSize(size: number): void;
6
2
  stream(args: {
7
3
  url: string;
8
4
  headers?: Record<string, string>;
@@ -14,6 +10,7 @@ declare const fileStreaming: {
14
10
  isAppending?: boolean;
15
11
  fileArraybuffer?: Uint8Array;
16
12
  chunk?: Uint8Array;
13
+ totalLength: number;
17
14
  }) => void): Promise<Uint8Array | void>;
18
15
  };
19
16
  export default fileStreaming;
@@ -1,18 +1,6 @@
1
1
  import { CustomError } from '../../classes/customClasses';
2
2
  import { createStreamingFileName, readFile, writeFile } from '../../fileAccessSystemUtils';
3
3
  const fileStreaming = {
4
- maxFetchSize: 4 * 1024 * 1024 * 1024,
5
- fetchedSize: 0,
6
- setMaxFetchSize(size) {
7
- if (size > 0) {
8
- this.maxFetchSize = size;
9
- }
10
- },
11
- decreaseFetchedSize(size) {
12
- if (size > 0 && size <= this.fetchedSize) {
13
- this.fetchedSize -= size;
14
- }
15
- },
16
4
  async stream(args, callBack) {
17
5
  const { url, headers, useSharedArrayBuffer, directoryHandle } = args;
18
6
  const controller = new AbortController();
@@ -23,7 +11,8 @@ const fileStreaming = {
23
11
  if (directoryHandle) {
24
12
  const file = (await readFile(directoryHandle, fileName, { isJson: false }));
25
13
  if (file) {
26
- callBack({ url, position: file.byteLength, fileArraybuffer: new Uint8Array(file) });
14
+ const totalLength = file.byteLength;
15
+ callBack({ url, position: totalLength, fileArraybuffer: new Uint8Array(file), totalLength });
27
16
  return;
28
17
  }
29
18
  }
@@ -48,11 +37,6 @@ const fileStreaming = {
48
37
  }
49
38
  if (!completed) {
50
39
  let position = firstChunk.value.length;
51
- if (this.fetchedSize + position > this.maxFetchSize) {
52
- controller.abort();
53
- throw new CustomError(`Maximum size(${this.maxFetchSize}) for fetching files reached`);
54
- }
55
- this.fetchedSize += position;
56
40
  if (useSharedArrayBuffer) {
57
41
  sharedArraybuffer = new SharedArrayBuffer(totalLength);
58
42
  fileArraybuffer = new Uint8Array(sharedArraybuffer);
@@ -61,7 +45,7 @@ const fileStreaming = {
61
45
  fileArraybuffer = new Uint8Array(totalLength);
62
46
  }
63
47
  fileArraybuffer.set(firstChunk.value);
64
- callBack({ url, position, fileArraybuffer });
48
+ callBack({ url, position, fileArraybuffer, totalLength });
65
49
  while (!completed) {
66
50
  result = await reader.read();
67
51
  if (result.done) {
@@ -69,20 +53,14 @@ const fileStreaming = {
69
53
  continue;
70
54
  }
71
55
  const chunk = result.value;
72
- if (this.fetchedSize + chunk.length > this.maxFetchSize) {
73
- sharedArraybuffer = null;
74
- fileArraybuffer = null;
75
- controller.abort();
76
- throw new CustomError(`Maximum size(${this.maxFetchSize}) for fetching files reached`);
77
- }
78
- this.fetchedSize += chunk.length;
79
56
  fileArraybuffer.set(chunk, position);
80
57
  position += chunk.length;
81
58
  callBack({
82
59
  isAppending: true,
83
60
  url,
84
61
  position: position,
85
- chunk: !useSharedArrayBuffer ? chunk : undefined
62
+ chunk: !useSharedArrayBuffer ? chunk : undefined,
63
+ totalLength
86
64
  });
87
65
  }
88
66
  if (directoryHandle) {
@@ -1,12 +1,7 @@
1
- import type { FileManagerOptions } from './types';
1
+ import type { FileManagerFile } from './types';
2
2
  declare class FileManager {
3
3
  private files;
4
- private fileStreamingScriptName;
5
- constructor({ fileStreamingScriptName }: FileManagerOptions);
6
- set(url: string, file: {
7
- data: Uint8Array;
8
- position: number;
9
- }): void;
4
+ set(url: string, file: Omit<FileManagerFile, 'lastModified'>): void;
10
5
  get(url: string, offsets?: {
11
6
  startByte: number;
12
7
  endByte: number;
@@ -17,5 +12,6 @@ declare class FileManager {
17
12
  getTotalSize(): number;
18
13
  remove(url: string): void;
19
14
  purge(): void;
15
+ decacheNecessaryBytes(url: string, bytesNeeded: number): number;
20
16
  }
21
17
  export default FileManager;
@@ -1,12 +1,7 @@
1
- import { getDataRetrievalManager } from './dataRetrieval/dataRetrievalManager';
2
1
  class FileManager {
3
2
  files = {};
4
- fileStreamingScriptName;
5
- constructor({ fileStreamingScriptName }) {
6
- this.fileStreamingScriptName = fileStreamingScriptName;
7
- }
8
3
  set(url, file) {
9
- this.files[url] = file;
4
+ this.files[url] = { ...file, lastModified: Date.now() };
10
5
  }
11
6
  get(url, offsets) {
12
7
  if (!this.files[url] || (offsets && this.files[url].position <= offsets.endByte)) {
@@ -17,6 +12,7 @@ class FileManager {
17
12
  setPosition(url, position) {
18
13
  if (this.files[url]) {
19
14
  this.files[url].position = position;
15
+ this.files[url].lastModified = Date.now();
20
16
  }
21
17
  }
22
18
  getPosition(url) {
@@ -29,24 +25,40 @@ class FileManager {
29
25
  }
30
26
  }
31
27
  getTotalSize() {
32
- return Object.entries(this.files).reduce((total, [url, { position }]) => {
33
- return url.includes('?bytes=') ? total : total + position;
28
+ return Object.values(this.files).reduce((total, { data }) => {
29
+ return total + data.byteLength;
34
30
  }, 0);
35
31
  }
36
32
  remove(url) {
37
- const removedSize = this.getPosition(url);
38
- delete this.files[url];
39
- if (url.includes('?bytes=')) {
40
- return;
33
+ try {
34
+ delete this.files[url];
35
+ console.log(`Removed ${url} from CodDicomwebServer cache`);
36
+ }
37
+ catch (error) {
38
+ console.warn(`Error removing ${url} from CodDicomwebServer cache:`, error);
41
39
  }
42
- const retrievalManager = getDataRetrievalManager();
43
- retrievalManager.executeTask(this.fileStreamingScriptName, 'decreaseFetchedSize', removedSize);
44
40
  }
45
41
  purge() {
42
+ const fileURLs = Object.keys(this.files);
43
+ const totalSize = this.getTotalSize();
44
+ fileURLs.forEach((url) => this.remove(url));
45
+ console.log(`Purged ${totalSize - this.getTotalSize()} bytes from CodDicomwebServer cache`);
46
+ }
47
+ decacheNecessaryBytes(url, bytesNeeded) {
46
48
  const totalSize = this.getTotalSize();
47
- this.files = {};
48
- const retrievalManager = getDataRetrievalManager();
49
- retrievalManager.executeTask(this.fileStreamingScriptName, 'decreaseFetchedSize', totalSize);
49
+ const filesToDelete = [];
50
+ let collectiveSize = 0;
51
+ Object.entries(this.files)
52
+ .sort(([, a], [, b]) => a.lastModified - b.lastModified)
53
+ .forEach(([key, file]) => {
54
+ if (collectiveSize < bytesNeeded && key !== url) {
55
+ filesToDelete.push(key);
56
+ collectiveSize += file.data.byteLength;
57
+ }
58
+ });
59
+ filesToDelete.forEach((key) => this.remove(key));
60
+ console.log(`Decached ${totalSize - this.getTotalSize()} bytes`);
61
+ return collectiveSize;
50
62
  }
51
63
  }
52
64
  export default FileManager;
@@ -1,6 +1,6 @@
1
1
  type CodDicomWebServerOptions = {
2
2
  [key: string]: number | string | boolean;
3
- maxWorkerFetchSize: number;
3
+ maxCacheSize: number;
4
4
  domain: string;
5
5
  enableLocalCache: boolean;
6
6
  };
@@ -0,0 +1,6 @@
1
+ type FileManagerFile = {
2
+ data: Uint8Array;
3
+ position: number;
4
+ lastModified: number;
5
+ };
6
+ export type { FileManagerFile };
@@ -1,5 +1,5 @@
1
1
  export * from './codDicomWebServerOptions';
2
- export * from './fileManagerOptions';
2
+ export * from './fileManagerFile';
3
3
  export * from './metadata';
4
4
  export * from './metadataUrlCreationParams';
5
5
  export * from './parsedWadoRsUrlDetails';
@@ -1,5 +1,5 @@
1
1
  export * from './codDicomWebServerOptions';
2
- export * from './fileManagerOptions';
2
+ export * from './fileManagerFile';
3
3
  export * from './metadata';
4
4
  export * from './metadataUrlCreationParams';
5
5
  export * from './parsedWadoRsUrlDetails';
package/dist/umd/563.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  *
3
- * cod-dicomweb-server v1.3.10
3
+ * cod-dicomweb-server v1.3.12
4
4
  * git+https://github.com/gradienthealth/cod-dicomweb-server.git
5
5
  *
6
6
  * Copyright (c) Adithyan Dinesh and project contributors.