cod-dicomweb-server 1.2.4 → 1.3.0

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 (65) hide show
  1. package/README.md +15 -0
  2. package/dist/cjs/1104a37b16dee0d2ada1.ts +14 -0
  3. package/dist/cjs/7d4e5892d21def245792.ts +14 -0
  4. package/dist/cjs/main.js +1957 -0
  5. package/dist/esm/classes/CodDicomWebServer.d.ts +1 -0
  6. package/dist/esm/classes/CodDicomWebServer.js +65 -48
  7. package/dist/esm/classes/customClasses.d.ts +19 -0
  8. package/dist/esm/classes/customClasses.js +13 -0
  9. package/dist/esm/classes/index.d.ts +2 -0
  10. package/dist/esm/classes/index.js +2 -0
  11. package/dist/esm/classes/utils.js +4 -3
  12. package/dist/esm/constants/enums.d.ts +4 -0
  13. package/dist/esm/constants/enums.js +5 -0
  14. package/dist/esm/constants/index.d.ts +3 -3
  15. package/dist/esm/constants/index.js +3 -3
  16. package/dist/esm/dataRetrieval/dataRetrievalManager.d.ts +17 -0
  17. package/dist/esm/dataRetrieval/dataRetrievalManager.js +54 -0
  18. package/dist/esm/dataRetrieval/register.d.ts +4 -0
  19. package/dist/esm/dataRetrieval/register.js +25 -0
  20. package/dist/esm/dataRetrieval/requestManager.d.ts +12 -0
  21. package/dist/esm/dataRetrieval/requestManager.js +65 -0
  22. package/dist/esm/dataRetrieval/scripts/filePartial.d.ts +18 -0
  23. package/dist/esm/dataRetrieval/scripts/filePartial.js +16 -0
  24. package/dist/esm/{webWorker → dataRetrieval}/scripts/fileStreaming.d.ts +7 -1
  25. package/dist/esm/{webWorker → dataRetrieval}/scripts/fileStreaming.js +11 -10
  26. package/dist/esm/dataRetrieval/utils/environment.d.ts +1 -0
  27. package/dist/esm/dataRetrieval/utils/environment.js +3 -0
  28. package/dist/esm/dataRetrieval/workerManager.d.ts +10 -0
  29. package/dist/esm/{webWorker → dataRetrieval}/workerManager.js +9 -8
  30. package/dist/esm/dataRetrieval/workers/filePartialWorker.js +7 -0
  31. package/dist/esm/dataRetrieval/workers/fileStreamingWorker.js +7 -0
  32. package/dist/esm/fileManager.d.ts +2 -2
  33. package/dist/esm/fileManager.js +8 -8
  34. package/dist/esm/index.d.ts +1 -1
  35. package/dist/esm/index.js +1 -1
  36. package/dist/esm/metadataManager.js +3 -2
  37. package/dist/esm/types/fileManagerOptions.d.ts +1 -1
  38. package/dist/esm/types/index.d.ts +1 -1
  39. package/dist/esm/types/index.js +1 -1
  40. package/dist/esm/types/metadata.d.ts +1 -1
  41. package/dist/esm/types/scriptObject.d.ts +4 -0
  42. package/dist/umd/614.js +19 -0
  43. package/dist/umd/614.js.map +1 -0
  44. package/dist/umd/66.js +19 -0
  45. package/dist/umd/66.js.map +1 -0
  46. package/dist/umd/main.js +2 -2
  47. package/dist/umd/main.js.map +1 -1
  48. package/package.json +13 -4
  49. package/dist/esm/types/workerCustomMessageEvents.d.ts +0 -10
  50. package/dist/esm/webWorker/registerWorkers.d.ts +0 -4
  51. package/dist/esm/webWorker/registerWorkers.js +0 -16
  52. package/dist/esm/webWorker/scripts/filePartial.d.ts +0 -7
  53. package/dist/esm/webWorker/scripts/filePartial.js +0 -11
  54. package/dist/esm/webWorker/workerManager.d.ts +0 -10
  55. package/dist/esm/webWorker/workers/filePartialWorker.js +0 -3
  56. package/dist/esm/webWorker/workers/fileStreamingWorker.js +0 -3
  57. package/dist/umd/16.js +0 -19
  58. package/dist/umd/16.js.map +0 -1
  59. package/dist/umd/170.js +0 -19
  60. package/dist/umd/170.js.map +0 -1
  61. /package/dist/esm/constants/{worker.d.ts → dataRetrieval.d.ts} +0 -0
  62. /package/dist/esm/constants/{worker.js → dataRetrieval.js} +0 -0
  63. /package/dist/esm/{webWorker → dataRetrieval}/workers/filePartialWorker.d.ts +0 -0
  64. /package/dist/esm/{webWorker → dataRetrieval}/workers/fileStreamingWorker.d.ts +0 -0
  65. /package/dist/esm/types/{workerCustomMessageEvents.js → scriptObject.js} +0 -0
@@ -9,6 +9,7 @@ declare class CodDicomWebServer {
9
9
  constructor(args?: {
10
10
  maxWorkerFetchSize?: number;
11
11
  domain?: string;
12
+ disableWorker?: boolean;
12
13
  });
13
14
  setOptions: (newOptions: Partial<CodDicomWebServerOptions>) => void;
14
15
  getOptions: () => CodDicomWebServerOptions;
@@ -2,9 +2,11 @@ import { parseDicom } from 'dicom-parser';
2
2
  import FileManager from '../fileManager';
3
3
  import MetadataManager from '../metadataManager';
4
4
  import { getFrameDetailsFromMetadata, parseWadorsURL } from './utils';
5
- import { getWebWorkerManager } from '../webWorker/workerManager';
6
- import { registerWorkers } from '../webWorker/registerWorkers';
5
+ import { register } from '../dataRetrieval/register';
7
6
  import constants, { Enums } from '../constants';
7
+ import { getDataRetrievalManager } from '../dataRetrieval/dataRetrievalManager';
8
+ import { CustomError } from './customClasses';
9
+ import { CustomErrorEvent } from './customClasses';
8
10
  class CodDicomWebServer {
9
11
  filePromises = {};
10
12
  options = {
@@ -15,14 +17,18 @@ class CodDicomWebServer {
15
17
  metadataManager;
16
18
  seriesUidFileUrls = {};
17
19
  constructor(args = {}) {
18
- const { maxWorkerFetchSize, domain } = args;
20
+ const { maxWorkerFetchSize, domain, disableWorker } = args;
19
21
  this.options.maxWorkerFetchSize = maxWorkerFetchSize || this.options.maxWorkerFetchSize;
20
22
  this.options.domain = domain || this.options.domain;
21
- const fileStreamingWorkerName = constants.worker.FILE_STREAMING_WORKER_NAME;
22
- const filePartialWorkerName = constants.worker.FILE_PARTIAL_WORKER_NAME;
23
- this.fileManager = new FileManager({ fileStreamingWorkerName });
23
+ const fileStreamingScriptName = constants.dataRetrieval.FILE_STREAMING_WORKER_NAME;
24
+ const filePartialScriptName = constants.dataRetrieval.FILE_PARTIAL_WORKER_NAME;
25
+ this.fileManager = new FileManager({ fileStreamingScriptName });
24
26
  this.metadataManager = new MetadataManager();
25
- registerWorkers({ fileStreamingWorkerName, filePartialWorkerName }, this.options.maxWorkerFetchSize);
27
+ if (disableWorker) {
28
+ const dataRetrievalManager = getDataRetrievalManager();
29
+ dataRetrievalManager.setDataRetrieverMode(Enums.DataRetrieveMode.REQUEST);
30
+ }
31
+ register({ fileStreamingScriptName, filePartialScriptName }, this.options.maxWorkerFetchSize);
26
32
  }
27
33
  setOptions = (newOptions) => {
28
34
  Object.keys(newOptions).forEach((key) => {
@@ -45,7 +51,7 @@ class CodDicomWebServer {
45
51
  async fetchCod(wadorsUrl, headers = {}, { useSharedArrayBuffer = false, fetchType = constants.Enums.FetchType.API_OPTIMIZED } = {}) {
46
52
  try {
47
53
  if (!wadorsUrl) {
48
- throw new Error('Url not provided');
54
+ throw new CustomError('Url not provided');
49
55
  }
50
56
  const parsedDetails = parseWadorsURL(wadorsUrl, this.options.domain);
51
57
  if (parsedDetails) {
@@ -58,7 +64,7 @@ class CodDicomWebServer {
58
64
  seriesInstanceUID
59
65
  }, headers);
60
66
  if (!metadataJson) {
61
- throw new Error(`Metadata not found for ${wadorsUrl}`);
67
+ throw new CustomError(`Metadata not found for ${wadorsUrl}`);
62
68
  }
63
69
  const { url: fileUrl, startByte, endByte, thumbnailUrl, isMultiframe } = getFrameDetailsFromMetadata(metadataJson, sopInstanceUID, frameNumber - 1, {
64
70
  domain: this.options.domain,
@@ -68,7 +74,7 @@ class CodDicomWebServer {
68
74
  switch (type) {
69
75
  case Enums.RequestType.THUMBNAIL:
70
76
  if (!thumbnailUrl) {
71
- throw new Error(`Thumbnail not found for ${wadorsUrl}`);
77
+ throw new CustomError(`Thumbnail not found for ${wadorsUrl}`);
72
78
  }
73
79
  this.addFileUrl(seriesInstanceUID, thumbnailUrl);
74
80
  return this.fetchFile(thumbnailUrl, headers, {
@@ -76,7 +82,7 @@ class CodDicomWebServer {
76
82
  });
77
83
  case Enums.RequestType.FRAME: {
78
84
  if (!fileUrl) {
79
- throw new Error('Url not found for frame');
85
+ throw new CustomError('Url not found for frame');
80
86
  }
81
87
  let urlWithBytes = fileUrl;
82
88
  if (fetchType === Enums.FetchType.BYTES_OPTIMIZED) {
@@ -89,7 +95,7 @@ class CodDicomWebServer {
89
95
  fetchType
90
96
  }).then((arraybuffer) => {
91
97
  if (!arraybuffer?.byteLength) {
92
- throw new Error('File Arraybuffer is not found');
98
+ throw new CustomError('File Arraybuffer is not found');
93
99
  }
94
100
  if (isMultiframe) {
95
101
  return arraybuffer;
@@ -113,7 +119,7 @@ class CodDicomWebServer {
113
119
  case Enums.RequestType.INSTANCE_METADATA:
114
120
  return this.parseMetadata(metadataJson, type, sopInstanceUID);
115
121
  default:
116
- throw new Error(`Unsupported request type: ${type}`);
122
+ throw new CustomError(`Unsupported request type: ${type}`);
117
123
  }
118
124
  }
119
125
  else {
@@ -139,7 +145,7 @@ class CodDicomWebServer {
139
145
  }
140
146
  }
141
147
  catch (error) {
142
- const newError = new Error(`CodDicomWebServer.ts: ${error.message || 'An error occured when fetching the COD'}`);
148
+ const newError = new CustomError(`CodDicomWebServer.ts: ${error.message || 'An error occured when fetching the COD'}`);
143
149
  console.error(newError);
144
150
  throw newError;
145
151
  }
@@ -158,29 +164,29 @@ class CodDicomWebServer {
158
164
  });
159
165
  }
160
166
  const { maxWorkerFetchSize } = this.getOptions();
161
- const webWorkerManager = getWebWorkerManager();
162
- const { FILE_STREAMING_WORKER_NAME, FILE_PARTIAL_WORKER_NAME, THRESHOLD } = constants.worker;
167
+ const dataRetrievalManager = getDataRetrievalManager();
168
+ const { FILE_STREAMING_WORKER_NAME, FILE_PARTIAL_WORKER_NAME, THRESHOLD } = constants.dataRetrieval;
163
169
  let tarPromise;
164
170
  if (!this.filePromises[fileUrl]) {
165
171
  tarPromise = new Promise((resolveFile, rejectFile) => {
166
172
  if (this.fileManager.getTotalSize() + THRESHOLD > maxWorkerFetchSize) {
167
- throw new Error(`CodDicomWebServer.ts: Maximum size(${maxWorkerFetchSize}) for fetching files reached`);
173
+ throw new CustomError(`CodDicomWebServer.ts: Maximum size(${maxWorkerFetchSize}) for fetching files reached`);
168
174
  }
169
175
  const FetchTypeEnum = constants.Enums.FetchType;
170
176
  if (fetchType === FetchTypeEnum.API_OPTIMIZED) {
171
177
  const handleFirstChunk = (evt) => {
172
- if (evt instanceof ErrorEvent) {
178
+ if (evt instanceof CustomErrorEvent) {
173
179
  rejectFile(evt.error);
174
180
  throw evt.error;
175
181
  }
176
182
  const { url, position, fileArraybuffer } = evt.data;
177
183
  if (url === fileUrl && fileArraybuffer) {
178
184
  this.fileManager.set(url, { data: fileArraybuffer, position });
179
- webWorkerManager.removeEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleFirstChunk);
185
+ dataRetrievalManager.removeEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleFirstChunk);
180
186
  }
181
187
  };
182
- webWorkerManager.addEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleFirstChunk);
183
- webWorkerManager
188
+ dataRetrievalManager.addEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleFirstChunk);
189
+ dataRetrievalManager
184
190
  .executeTask(FILE_STREAMING_WORKER_NAME, 'stream', {
185
191
  url: fileUrl,
186
192
  headers: headers,
@@ -190,40 +196,46 @@ class CodDicomWebServer {
190
196
  resolveFile();
191
197
  })
192
198
  .catch((error) => {
193
- webWorkerManager.removeEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleFirstChunk);
194
199
  rejectFile(error);
195
200
  })
196
- .then(() => delete this.filePromises[fileUrl]);
201
+ .then(() => {
202
+ dataRetrievalManager.removeEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleFirstChunk);
203
+ delete this.filePromises[fileUrl];
204
+ });
197
205
  }
198
206
  else if (fetchType === FetchTypeEnum.BYTES_OPTIMIZED && offsets) {
199
207
  const { startByte, endByte } = offsets;
200
- headers['Range'] = `bytes=${startByte}-${endByte - 1}`;
201
- const url = fileUrl.split('?bytes=')[0];
202
- webWorkerManager
203
- .executeTask(FILE_PARTIAL_WORKER_NAME, 'partial', {
204
- url: url,
205
- headers: headers,
206
- useSharedArrayBuffer
207
- })
208
- .then((data) => {
209
- if (data) {
210
- this.fileManager.set(fileUrl, {
211
- data: new Uint8Array(data),
212
- position: data.byteLength
213
- });
214
- resolveFile();
208
+ const bytesRemovedUrl = fileUrl.split('?bytes=')[0];
209
+ const handleSlice = (evt) => {
210
+ if (evt instanceof CustomErrorEvent) {
211
+ rejectFile(evt.error);
212
+ throw evt.error;
215
213
  }
216
- else {
217
- rejectFile(new Error(`File - ${url} not found`));
214
+ const { url, fileArraybuffer, offsets } = evt.data;
215
+ if (url === bytesRemovedUrl && offsets.startByte === startByte && offsets.endByte === endByte) {
216
+ this.fileManager.set(fileUrl, { data: fileArraybuffer, position: fileArraybuffer.length });
217
+ dataRetrievalManager.removeEventListener(FILE_PARTIAL_WORKER_NAME, 'message', handleSlice);
218
+ resolveFile();
218
219
  }
220
+ };
221
+ dataRetrievalManager.addEventListener(FILE_PARTIAL_WORKER_NAME, 'message', handleSlice);
222
+ dataRetrievalManager
223
+ .executeTask(FILE_PARTIAL_WORKER_NAME, 'partial', {
224
+ url: bytesRemovedUrl,
225
+ offsets: { startByte, endByte },
226
+ headers,
227
+ useSharedArrayBuffer
219
228
  })
220
229
  .catch((error) => {
221
230
  rejectFile(error);
222
231
  })
223
- .then(() => delete this.filePromises[fileUrl]);
232
+ .then(() => {
233
+ dataRetrievalManager.removeEventListener(FILE_PARTIAL_WORKER_NAME, 'message', handleSlice);
234
+ delete this.filePromises[fileUrl];
235
+ });
224
236
  }
225
237
  else {
226
- rejectFile(new Error('CodDicomWebServer.ts: Offsets is needed in bytes optimized fetching'));
238
+ rejectFile(new CustomError('CodDicomWebServer.ts: Offsets is needed in bytes optimized fetching'));
227
239
  }
228
240
  });
229
241
  this.filePromises[fileUrl] = tarPromise;
@@ -234,7 +246,7 @@ class CodDicomWebServer {
234
246
  return new Promise((resolveRequest, rejectRequest) => {
235
247
  let requestResolved = false;
236
248
  const handleChunkAppend = (evt) => {
237
- if (evt instanceof ErrorEvent) {
249
+ if (evt instanceof CustomErrorEvent) {
238
250
  rejectRequest(evt.message);
239
251
  throw evt.error;
240
252
  }
@@ -259,21 +271,26 @@ class CodDicomWebServer {
259
271
  }
260
272
  };
261
273
  if (offsets && !isBytesOptimized) {
262
- webWorkerManager.addEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleChunkAppend);
274
+ dataRetrievalManager.addEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleChunkAppend);
263
275
  }
264
276
  tarPromise
265
277
  .then(() => {
266
278
  if (!requestResolved) {
267
- const file = this.fileManager.get(fileUrl, isBytesOptimized ? undefined : offsets);
268
- requestResolved = true;
269
- resolveRequest(file?.buffer);
279
+ if (this.fileManager.getPosition(fileUrl)) {
280
+ const file = this.fileManager.get(fileUrl, isBytesOptimized ? undefined : offsets);
281
+ requestResolved = true;
282
+ resolveRequest(file?.buffer);
283
+ }
284
+ else {
285
+ rejectRequest(new CustomError(`File - ${fileUrl} not found`));
286
+ }
270
287
  }
271
288
  })
272
289
  .catch((error) => {
273
290
  rejectRequest(error);
274
291
  })
275
292
  .then(() => {
276
- webWorkerManager.removeEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleChunkAppend);
293
+ dataRetrievalManager.removeEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleChunkAppend);
277
294
  });
278
295
  });
279
296
  }
@@ -0,0 +1,19 @@
1
+ export declare class CustomError extends Error {
2
+ }
3
+ export declare class CustomErrorEvent extends Event {
4
+ error: CustomError;
5
+ message: string;
6
+ constructor(message: string, error: CustomError);
7
+ }
8
+ export declare class CustomMessageEvent extends MessageEvent<{
9
+ url: string;
10
+ position: number;
11
+ chunk?: Uint8Array;
12
+ isAppending?: boolean;
13
+ fileArraybuffer?: Uint8Array;
14
+ offsets?: {
15
+ startByte: number;
16
+ endByte: number;
17
+ };
18
+ }> {
19
+ }
@@ -0,0 +1,13 @@
1
+ export class CustomError extends Error {
2
+ }
3
+ export class CustomErrorEvent extends Event {
4
+ error;
5
+ message;
6
+ constructor(message, error) {
7
+ super(message);
8
+ this.message = message;
9
+ this.error = error;
10
+ }
11
+ }
12
+ export class CustomMessageEvent extends MessageEvent {
13
+ }
@@ -0,0 +1,2 @@
1
+ import CodDicomWebServer from './CodDicomWebServer';
2
+ export { CodDicomWebServer };
@@ -0,0 +1,2 @@
1
+ import CodDicomWebServer from './CodDicomWebServer';
2
+ export { CodDicomWebServer };
@@ -1,4 +1,5 @@
1
1
  import constants, { Enums } from '../constants';
2
+ import { CustomError } from './customClasses';
2
3
  export function parseWadorsURL(url, domain) {
3
4
  if (!url.includes(constants.url.URL_VALIDATION_STRING)) {
4
5
  return;
@@ -32,7 +33,7 @@ export function parseWadorsURL(url, domain) {
32
33
  type = Enums.RequestType.FRAME;
33
34
  break;
34
35
  default:
35
- throw new Error('Invalid type of request');
36
+ throw new CustomError('Invalid type of request');
36
37
  }
37
38
  return {
38
39
  type,
@@ -46,10 +47,10 @@ export function parseWadorsURL(url, domain) {
46
47
  }
47
48
  export function getFrameDetailsFromMetadata(seriesMetadata, sopInstanceUID, frameIndex, bucketDetails) {
48
49
  if (!seriesMetadata || !seriesMetadata.cod?.instances) {
49
- throw new Error('Invalid seriesMetadata provided.');
50
+ throw new CustomError('Invalid seriesMetadata provided.');
50
51
  }
51
52
  if (frameIndex === null || frameIndex === undefined) {
52
- throw new Error('Frame index is required.');
53
+ throw new CustomError('Frame index is required.');
53
54
  }
54
55
  const { domain, bucketName, bucketPrefix } = bucketDetails;
55
56
  let thumbnailUrl;
@@ -15,3 +15,7 @@ export declare enum RequestType {
15
15
  SERIES_METADATA = 2,
16
16
  INSTANCE_METADATA = 3
17
17
  }
18
+ export declare enum DataRetrieveMode {
19
+ WORKER = 0,
20
+ REQUEST = 1
21
+ }
@@ -17,3 +17,8 @@ export var RequestType;
17
17
  RequestType[RequestType["SERIES_METADATA"] = 2] = "SERIES_METADATA";
18
18
  RequestType[RequestType["INSTANCE_METADATA"] = 3] = "INSTANCE_METADATA";
19
19
  })(RequestType || (RequestType = {}));
20
+ export var DataRetrieveMode;
21
+ (function (DataRetrieveMode) {
22
+ DataRetrieveMode[DataRetrieveMode["WORKER"] = 0] = "WORKER";
23
+ DataRetrieveMode[DataRetrieveMode["REQUEST"] = 1] = "REQUEST";
24
+ })(DataRetrieveMode || (DataRetrieveMode = {}));
@@ -1,10 +1,10 @@
1
1
  import * as Enums from './enums';
2
2
  import * as url from './url';
3
- import * as worker from './worker';
3
+ import * as dataRetrieval from './dataRetrieval';
4
4
  declare const constants: {
5
5
  Enums: typeof Enums;
6
6
  url: typeof url;
7
- worker: typeof worker;
7
+ dataRetrieval: typeof dataRetrieval;
8
8
  };
9
- export { Enums, url, worker };
9
+ export { Enums, url, dataRetrieval };
10
10
  export default constants;
@@ -1,6 +1,6 @@
1
1
  import * as Enums from './enums';
2
2
  import * as url from './url';
3
- import * as worker from './worker';
4
- const constants = { Enums, url, worker };
5
- export { Enums, url, worker };
3
+ import * as dataRetrieval from './dataRetrieval';
4
+ const constants = { Enums, url, dataRetrieval };
5
+ export { Enums, url, dataRetrieval };
6
6
  export default constants;
@@ -0,0 +1,17 @@
1
+ import { CustomErrorEvent, CustomMessageEvent } from '../classes/customClasses';
2
+ import { Enums } from '../constants';
3
+ import { ScriptObject } from '../types';
4
+ declare class DataRetrievalManager {
5
+ private dataRetriever;
6
+ private dataRetrieverMode;
7
+ constructor();
8
+ getDataRetrieverMode(): Enums.DataRetrieveMode;
9
+ setDataRetrieverMode(mode: Enums.DataRetrieveMode): void;
10
+ register(name: string, arg: (() => Worker) | ScriptObject): void;
11
+ executeTask(loaderName: string, taskName: string, options: Record<string, unknown> | unknown): Promise<void>;
12
+ addEventListener(workerName: string, eventType: keyof WorkerEventMap, listener: (evt: CustomMessageEvent | CustomErrorEvent) => unknown): void;
13
+ removeEventListener(workerName: string, eventType: keyof WorkerEventMap, listener: (evt: CustomMessageEvent | CustomErrorEvent) => unknown): void;
14
+ reset(): void;
15
+ }
16
+ export declare function getDataRetrievalManager(): DataRetrievalManager;
17
+ export {};
@@ -0,0 +1,54 @@
1
+ import { CustomError } from '../classes/customClasses';
2
+ import { Enums } from '../constants';
3
+ import RequestManager from './requestManager';
4
+ import { isNodeEnvironment } from './utils/environment';
5
+ import WebWorkerManager from './workerManager';
6
+ class DataRetrievalManager {
7
+ dataRetriever;
8
+ dataRetrieverMode;
9
+ constructor() {
10
+ if (isNodeEnvironment()) {
11
+ this.dataRetriever = new RequestManager();
12
+ this.dataRetrieverMode = Enums.DataRetrieveMode.REQUEST;
13
+ }
14
+ else {
15
+ this.dataRetriever = new WebWorkerManager();
16
+ this.dataRetrieverMode = Enums.DataRetrieveMode.WORKER;
17
+ }
18
+ }
19
+ getDataRetrieverMode() {
20
+ return this.dataRetrieverMode;
21
+ }
22
+ setDataRetrieverMode(mode) {
23
+ const managers = {
24
+ [Enums.DataRetrieveMode.WORKER]: WebWorkerManager,
25
+ [Enums.DataRetrieveMode.REQUEST]: RequestManager
26
+ };
27
+ if (!(mode in managers)) {
28
+ throw new CustomError('Invalid mode');
29
+ }
30
+ this.dataRetriever.reset();
31
+ this.dataRetriever = new managers[mode]();
32
+ this.dataRetrieverMode = mode;
33
+ }
34
+ register(name, arg) {
35
+ // @ts-ignore
36
+ this.dataRetriever.register(name, arg);
37
+ }
38
+ async executeTask(loaderName, taskName, options) {
39
+ return await this.dataRetriever.executeTask(loaderName, taskName, options);
40
+ }
41
+ addEventListener(workerName, eventType, listener) {
42
+ this.dataRetriever.addEventListener(workerName, eventType, listener);
43
+ }
44
+ removeEventListener(workerName, eventType, listener) {
45
+ this.dataRetriever.removeEventListener(workerName, eventType, listener);
46
+ }
47
+ reset() {
48
+ this.dataRetriever.reset();
49
+ }
50
+ }
51
+ const dataRetrievalManager = new DataRetrievalManager();
52
+ export function getDataRetrievalManager() {
53
+ return dataRetrievalManager;
54
+ }
@@ -0,0 +1,4 @@
1
+ export declare function register(workerNames: {
2
+ fileStreamingScriptName: string;
3
+ filePartialScriptName: string;
4
+ }, maxFetchSize: number): void;
@@ -0,0 +1,25 @@
1
+ import { Enums } from '../constants';
2
+ import { getDataRetrievalManager } from './dataRetrievalManager';
3
+ import filePartial from './scripts/filePartial';
4
+ import fileStreaming from './scripts/fileStreaming';
5
+ export function register(workerNames, maxFetchSize) {
6
+ const { fileStreamingScriptName, filePartialScriptName } = workerNames;
7
+ const dataRetrievalManager = getDataRetrievalManager();
8
+ if (dataRetrievalManager.getDataRetrieverMode() === Enums.DataRetrieveMode.REQUEST) {
9
+ dataRetrievalManager.register(fileStreamingScriptName, fileStreaming);
10
+ dataRetrievalManager.register(filePartialScriptName, filePartial);
11
+ }
12
+ else {
13
+ // fileStreaming worker
14
+ const streamingWorkerFn = () => new Worker(new URL('./workers/fileStreamingWorker', import.meta.url), {
15
+ name: fileStreamingScriptName
16
+ });
17
+ dataRetrievalManager.register(fileStreamingScriptName, streamingWorkerFn);
18
+ // filePartial worker
19
+ const partialWorkerFn = () => new Worker(new URL('./workers/filePartialWorker', import.meta.url), {
20
+ name: filePartialScriptName
21
+ });
22
+ dataRetrievalManager.register(filePartialScriptName, partialWorkerFn);
23
+ }
24
+ dataRetrievalManager.executeTask(fileStreamingScriptName, 'setMaxFetchSize', maxFetchSize);
25
+ }
@@ -0,0 +1,12 @@
1
+ import { CustomMessageEvent, CustomErrorEvent } from '../classes/customClasses';
2
+ import { ScriptObject } from '../types';
3
+ declare class RequestManager {
4
+ private loaderRegistry;
5
+ register(loaderName: string, loaderObject: ScriptObject): void;
6
+ private listenerCallback;
7
+ executeTask(loaderName: string, taskName: string, options: Record<string, unknown> | unknown): Promise<void>;
8
+ addEventListener(workerName: string, eventType: keyof WorkerEventMap, listener: (evt: CustomMessageEvent | CustomErrorEvent) => unknown): void;
9
+ removeEventListener(workerName: string, eventType: keyof WorkerEventMap, listener: (evt: CustomMessageEvent | CustomErrorEvent) => unknown): void;
10
+ reset(): void;
11
+ }
12
+ export default RequestManager;
@@ -0,0 +1,65 @@
1
+ import { CustomError } from '../classes/customClasses';
2
+ class RequestManager {
3
+ loaderRegistry = {};
4
+ register(loaderName, loaderObject) {
5
+ try {
6
+ if (!loaderObject) {
7
+ throw new CustomError(`Loader object for ${loaderName} is not provided`);
8
+ }
9
+ this.loaderRegistry[loaderName] = {
10
+ loaderObject,
11
+ listeners: {}
12
+ };
13
+ }
14
+ catch (error) {
15
+ console.warn(error);
16
+ throw new CustomError('throws');
17
+ }
18
+ }
19
+ listenerCallback = (loaderName, taskName, args) => {
20
+ const listeners = this.loaderRegistry[loaderName]?.listeners[taskName];
21
+ if (listeners) {
22
+ listeners.forEach((listener) => listener({ data: args }));
23
+ }
24
+ };
25
+ async executeTask(loaderName, taskName, options) {
26
+ const loaderObject = this.loaderRegistry[loaderName]?.loaderObject;
27
+ if (!loaderObject) {
28
+ throw new CustomError(`Loader ${loaderName} not registered`);
29
+ }
30
+ try {
31
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
32
+ // @ts-ignore
33
+ return await loaderObject[taskName](options, (args) => this.listenerCallback(loaderName, 'message', args));
34
+ }
35
+ catch (error) {
36
+ console.error(`Error executing task "${taskName}" on "${loaderName}":`, error);
37
+ throw new CustomError(`Task "${taskName}" failed: ${error.message}`);
38
+ }
39
+ }
40
+ addEventListener(workerName, eventType, listener) {
41
+ const loaderObject = this.loaderRegistry[workerName];
42
+ if (!loaderObject) {
43
+ console.error(`Loader '${workerName}' is not registered.`);
44
+ return;
45
+ }
46
+ if (!loaderObject.listeners[eventType]) {
47
+ loaderObject.listeners[eventType] = [listener];
48
+ }
49
+ else {
50
+ loaderObject.listeners[eventType].push(listener);
51
+ }
52
+ }
53
+ removeEventListener(workerName, eventType, listener) {
54
+ const loaderObject = this.loaderRegistry[workerName];
55
+ if (!loaderObject) {
56
+ console.error(`Loader '${workerName}' is not registered.`);
57
+ return;
58
+ }
59
+ loaderObject.listeners[eventType] = (loaderObject.listeners[eventType] || []).filter((existingListener) => existingListener !== listener);
60
+ }
61
+ reset() {
62
+ this.loaderRegistry = {};
63
+ }
64
+ }
65
+ export default RequestManager;
@@ -0,0 +1,18 @@
1
+ declare const filePartial: {
2
+ partial(args: {
3
+ url: string;
4
+ offsets?: {
5
+ startByte: number;
6
+ endByte: number;
7
+ };
8
+ headers?: Record<string, string>;
9
+ }, callBack: (data: {
10
+ url: string;
11
+ fileArraybuffer: Uint8Array;
12
+ offsets: {
13
+ startByte: number;
14
+ endByte: number;
15
+ };
16
+ }) => void): Promise<void | Error>;
17
+ };
18
+ export default filePartial;
@@ -0,0 +1,16 @@
1
+ import { CustomError } from '../../classes/customClasses';
2
+ const filePartial = {
3
+ async partial(args, callBack) {
4
+ const { url, offsets, headers } = args;
5
+ if (offsets?.startByte && offsets?.endByte) {
6
+ headers['Range'] = `bytes=${offsets.startByte}-${offsets.endByte - 1}`;
7
+ }
8
+ await fetch(url, { headers })
9
+ .then((response) => response.arrayBuffer())
10
+ .then((data) => callBack({ url, fileArraybuffer: new Uint8Array(data), offsets }))
11
+ .catch((error) => {
12
+ throw new CustomError('filePartial.ts: Error when fetching file: ' + error?.message);
13
+ });
14
+ }
15
+ };
16
+ export default filePartial;
@@ -7,6 +7,12 @@ declare const fileStreaming: {
7
7
  url: string;
8
8
  headers?: Record<string, string>;
9
9
  useSharedArrayBuffer?: boolean;
10
- }): Promise<void>;
10
+ }, callBack: (data: {
11
+ url: string;
12
+ position: number;
13
+ isAppending?: boolean;
14
+ fileArraybuffer?: Uint8Array;
15
+ chunk?: Uint8Array;
16
+ }) => void): Promise<Uint8Array | void>;
11
17
  };
12
18
  export default fileStreaming;