cod-dicomweb-server 1.2.4 → 1.3.1
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.
- package/README.md +21 -0
- package/dist/cjs/1104a37b16dee0d2ada1.ts +14 -0
- package/dist/cjs/7d4e5892d21def245792.ts +14 -0
- package/dist/cjs/main.js +1957 -0
- package/dist/esm/classes/CodDicomWebServer.d.ts +1 -0
- package/dist/esm/classes/CodDicomWebServer.js +65 -48
- package/dist/esm/classes/customClasses.d.ts +19 -0
- package/dist/esm/classes/customClasses.js +13 -0
- package/dist/esm/classes/index.d.ts +2 -0
- package/dist/esm/classes/index.js +2 -0
- package/dist/esm/classes/utils.js +4 -3
- package/dist/esm/constants/enums.d.ts +4 -0
- package/dist/esm/constants/enums.js +5 -0
- package/dist/esm/constants/index.d.ts +3 -3
- package/dist/esm/constants/index.js +3 -3
- package/dist/esm/dataRetrieval/dataRetrievalManager.d.ts +17 -0
- package/dist/esm/dataRetrieval/dataRetrievalManager.js +54 -0
- package/dist/esm/dataRetrieval/register.d.ts +4 -0
- package/dist/esm/dataRetrieval/register.js +25 -0
- package/dist/esm/dataRetrieval/requestManager.d.ts +12 -0
- package/dist/esm/dataRetrieval/requestManager.js +65 -0
- package/dist/esm/dataRetrieval/scripts/filePartial.d.ts +18 -0
- package/dist/esm/dataRetrieval/scripts/filePartial.js +16 -0
- package/dist/esm/{webWorker → dataRetrieval}/scripts/fileStreaming.d.ts +7 -1
- package/dist/esm/{webWorker → dataRetrieval}/scripts/fileStreaming.js +11 -10
- package/dist/esm/dataRetrieval/utils/environment.d.ts +1 -0
- package/dist/esm/dataRetrieval/utils/environment.js +3 -0
- package/dist/esm/dataRetrieval/workerManager.d.ts +10 -0
- package/dist/esm/{webWorker → dataRetrieval}/workerManager.js +9 -8
- package/dist/esm/dataRetrieval/workers/filePartialWorker.js +7 -0
- package/dist/esm/dataRetrieval/workers/fileStreamingWorker.js +7 -0
- package/dist/esm/fileManager.d.ts +2 -2
- package/dist/esm/fileManager.js +8 -8
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/metadataManager.js +3 -2
- package/dist/esm/types/fileManagerOptions.d.ts +1 -1
- package/dist/esm/types/index.d.ts +1 -1
- package/dist/esm/types/index.js +1 -1
- package/dist/esm/types/metadata.d.ts +1 -1
- package/dist/esm/types/scriptObject.d.ts +4 -0
- package/dist/umd/614.js +19 -0
- package/dist/umd/614.js.map +1 -0
- package/dist/umd/66.js +19 -0
- package/dist/umd/66.js.map +1 -0
- package/dist/umd/main.js +2 -2
- package/dist/umd/main.js.map +1 -1
- package/package.json +13 -4
- package/dist/esm/types/workerCustomMessageEvents.d.ts +0 -10
- package/dist/esm/webWorker/registerWorkers.d.ts +0 -4
- package/dist/esm/webWorker/registerWorkers.js +0 -16
- package/dist/esm/webWorker/scripts/filePartial.d.ts +0 -7
- package/dist/esm/webWorker/scripts/filePartial.js +0 -11
- package/dist/esm/webWorker/workerManager.d.ts +0 -10
- package/dist/esm/webWorker/workers/filePartialWorker.js +0 -3
- package/dist/esm/webWorker/workers/fileStreamingWorker.js +0 -3
- package/dist/umd/16.js +0 -19
- package/dist/umd/16.js.map +0 -1
- package/dist/umd/170.js +0 -19
- package/dist/umd/170.js.map +0 -1
- /package/dist/esm/constants/{worker.d.ts → dataRetrieval.d.ts} +0 -0
- /package/dist/esm/constants/{worker.js → dataRetrieval.js} +0 -0
- /package/dist/esm/{webWorker → dataRetrieval}/workers/filePartialWorker.d.ts +0 -0
- /package/dist/esm/{webWorker → dataRetrieval}/workers/fileStreamingWorker.d.ts +0 -0
- /package/dist/esm/types/{workerCustomMessageEvents.js → scriptObject.js} +0 -0
|
@@ -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 {
|
|
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
|
|
22
|
-
const
|
|
23
|
-
this.fileManager = new FileManager({
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
162
|
-
const { FILE_STREAMING_WORKER_NAME, FILE_PARTIAL_WORKER_NAME, THRESHOLD } = constants.
|
|
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
|
|
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
|
|
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
|
-
|
|
185
|
+
dataRetrievalManager.removeEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleFirstChunk);
|
|
180
186
|
}
|
|
181
187
|
};
|
|
182
|
-
|
|
183
|
-
|
|
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(() =>
|
|
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
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
217
|
-
|
|
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(() =>
|
|
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
|
|
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
|
|
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
|
-
|
|
274
|
+
dataRetrievalManager.addEventListener(FILE_STREAMING_WORKER_NAME, 'message', handleChunkAppend);
|
|
263
275
|
}
|
|
264
276
|
tarPromise
|
|
265
277
|
.then(() => {
|
|
266
278
|
if (!requestResolved) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -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
|
|
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
|
|
50
|
+
throw new CustomError('Invalid seriesMetadata provided.');
|
|
50
51
|
}
|
|
51
52
|
if (frameIndex === null || frameIndex === undefined) {
|
|
52
|
-
throw new
|
|
53
|
+
throw new CustomError('Frame index is required.');
|
|
53
54
|
}
|
|
54
55
|
const { domain, bucketName, bucketPrefix } = bucketDetails;
|
|
55
56
|
let thumbnailUrl;
|
|
@@ -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
|
|
3
|
+
import * as dataRetrieval from './dataRetrieval';
|
|
4
4
|
declare const constants: {
|
|
5
5
|
Enums: typeof Enums;
|
|
6
6
|
url: typeof url;
|
|
7
|
-
|
|
7
|
+
dataRetrieval: typeof dataRetrieval;
|
|
8
8
|
};
|
|
9
|
-
export { Enums, url,
|
|
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
|
|
4
|
-
const constants = { Enums, url,
|
|
5
|
-
export { Enums, url,
|
|
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,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
|
-
}
|
|
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;
|