@knime/utils 1.11.1 → 1.11.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.
Files changed (86) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +67 -14
  3. package/dist/globals.d.ts +8 -0
  4. package/dist/index.js +714 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/capitalize.d.ts +20 -0
  7. package/dist/src/capitalize.d.ts.map +1 -0
  8. package/dist/src/dateMinMaxCheck.d.ts +19 -0
  9. package/dist/src/dateMinMaxCheck.d.ts.map +1 -0
  10. package/dist/src/debounce.d.ts +4 -0
  11. package/dist/src/debounce.d.ts.map +1 -0
  12. package/dist/src/fileExtensions.d.ts +33 -0
  13. package/dist/src/fileExtensions.d.ts.map +1 -0
  14. package/dist/src/filters.d.ts +13 -0
  15. package/dist/src/filters.d.ts.map +1 -0
  16. package/dist/src/format.d.ts +39 -0
  17. package/dist/src/format.d.ts.map +1 -0
  18. package/dist/src/formatHotkeys.d.ts +17 -0
  19. package/dist/src/formatHotkeys.d.ts.map +1 -0
  20. package/dist/src/getDeepActiveElement.d.ts +3 -0
  21. package/dist/src/getDeepActiveElement.d.ts.map +1 -0
  22. package/{src/getWrappedAroundIndex.ts → dist/src/getWrappedAroundIndex.d.ts} +3 -2
  23. package/dist/src/getWrappedAroundIndex.d.ts.map +1 -0
  24. package/{src/hotkeys.ts → dist/src/hotkeys.d.ts} +4 -17
  25. package/dist/src/hotkeys.d.ts.map +1 -0
  26. package/dist/src/index.d.ts +29 -0
  27. package/dist/src/index.d.ts.map +1 -0
  28. package/dist/src/intervalUtils.d.ts +114 -0
  29. package/dist/src/intervalUtils.d.ts.map +1 -0
  30. package/{src/localTimezone.ts → dist/src/localTimezone.d.ts} +3 -2
  31. package/dist/src/localTimezone.d.ts.map +1 -0
  32. package/dist/src/navigator.d.ts +11 -0
  33. package/dist/src/navigator.d.ts.map +1 -0
  34. package/{src/numIntegerDigits.ts → dist/src/numIntegerDigits.d.ts} +3 -2
  35. package/dist/src/numIntegerDigits.d.ts.map +1 -0
  36. package/dist/src/promise/createAbortablePromise.d.ts +32 -0
  37. package/dist/src/promise/createAbortablePromise.d.ts.map +1 -0
  38. package/dist/src/promise/createUnwrappedPromise.d.ts +7 -0
  39. package/dist/src/promise/createUnwrappedPromise.d.ts.map +1 -0
  40. package/dist/src/promise/index.d.ts +4 -0
  41. package/dist/src/promise/index.d.ts.map +1 -0
  42. package/dist/src/promise/retryPromise.d.ts +16 -0
  43. package/dist/src/promise/retryPromise.d.ts.map +1 -0
  44. package/dist/src/sanitization.d.ts +8 -0
  45. package/dist/src/sanitization.d.ts.map +1 -0
  46. package/dist/src/sleep.d.ts +5 -0
  47. package/dist/src/sleep.d.ts.map +1 -0
  48. package/dist/src/truncateString.d.ts +3 -0
  49. package/dist/src/truncateString.d.ts.map +1 -0
  50. package/dist/src/updateDate.d.ts +11 -0
  51. package/dist/src/updateDate.d.ts.map +1 -0
  52. package/{src/updateTime.ts → dist/src/updateTime.d.ts} +3 -11
  53. package/dist/src/updateTime.d.ts.map +1 -0
  54. package/dist/test-utils/index.d.ts +3 -0
  55. package/dist/test-utils/index.d.ts.map +1 -0
  56. package/dist/test-utils/index.js +40 -0
  57. package/dist/test-utils/index.js.map +1 -0
  58. package/dist/test-utils/muteConsole.d.ts +8 -0
  59. package/dist/test-utils/muteConsole.d.ts.map +1 -0
  60. package/package.json +51 -33
  61. package/src/capitalize.ts +0 -45
  62. package/src/dateMinMaxCheck.ts +0 -68
  63. package/src/debounce.ts +0 -28
  64. package/src/fileExtensions.ts +0 -87
  65. package/src/filters.ts +0 -114
  66. package/src/format.ts +0 -113
  67. package/src/formatHotkeys.ts +0 -98
  68. package/src/getDeepActiveElement.ts +0 -9
  69. package/src/index.ts +0 -76
  70. package/src/intervalUtils.ts +0 -434
  71. package/src/navigator.ts +0 -13
  72. package/src/promise/createAbortablePromise.ts +0 -56
  73. package/src/promise/createUnwrappedPromise.ts +0 -17
  74. package/src/promise/index.ts +0 -3
  75. package/src/promise/retryPromise.ts +0 -45
  76. package/src/sanitization.ts +0 -72
  77. package/src/sleep.ts +0 -6
  78. package/src/truncateString.ts +0 -7
  79. package/src/updateDate.ts +0 -23
  80. package/src/uploadManager/index.ts +0 -370
  81. package/test-utils/index.ts +0 -3
  82. package/test-utils/muteConsole.ts +0 -54
  83. /package/{src/audit → audit}/README.md +0 -0
  84. /package/{src/audit → audit}/bulkAudit.js +0 -0
  85. /package/{src/audit → audit}/cli.js +0 -0
  86. /package/{src/config/globals.d.ts → globals.d.ts} +0 -0
@@ -1,370 +0,0 @@
1
- import { AbortError, createAbortablePromise, retryPromise } from "../promise";
2
-
3
- // eslint-disable-next-line no-magic-numbers
4
- const CHUNK_SIZE = 50 * 1024 * 1024; // 50 MB per chunk
5
-
6
- const DEFAULT_RETRY_COUNT = 5;
7
- const DEFAULT_RETRY_DELAY_MS = 50;
8
-
9
- export type UploadStateName =
10
- | "idle"
11
- | "inprogress"
12
- | "cancelled"
13
- | "failed"
14
- | "complete";
15
-
16
- export type PartUploadResponse = {
17
- partId: string;
18
- };
19
-
20
- const uploadChunkWithProgress = (params: {
21
- method: string;
22
- url: string;
23
- chunk: Blob;
24
- chunkIndex: number;
25
- onProgress: (progress: number) => void;
26
- abortSignal: AbortSignal;
27
- }) => {
28
- const { method, url, chunk, chunkIndex, onProgress, abortSignal } = params;
29
-
30
- const OK = 200;
31
- const CREATED = 201;
32
-
33
- return new Promise<{ partId: string }>((resolve, reject) => {
34
- const xhr = new XMLHttpRequest();
35
-
36
- xhr.open(method, url, true);
37
-
38
- abortSignal.addEventListener("abort", () => xhr.abort());
39
-
40
- // Track the upload progress of the current chunk
41
- xhr.upload.onprogress = (event) => {
42
- if (event.lengthComputable) {
43
- onProgress(event.loaded);
44
- }
45
- };
46
-
47
- xhr.onload = () => {
48
- if (xhr.status === OK || xhr.status === CREATED) {
49
- const etag = xhr.getResponseHeader("ETag");
50
-
51
- // 1. note that some backends don't return the etag header, then we just set an empty string
52
- // 2. remove possible doublequotes (") around the etag and/or weak identifier (if either is present)
53
- // https://datatracker.ietf.org/doc/html/rfc7232#section-2.3
54
- resolve({ partId: etag?.replace(/^(?:W\/)?"|"$/g, "") ?? "" });
55
- } else {
56
- consola.error("Failed or unexpected XHR response", {
57
- xhrStatus: xhr.status,
58
- xhrResponse: xhr.response,
59
- chunkIndex,
60
- });
61
- reject(new Error("Failed to upload file"));
62
- }
63
- };
64
-
65
- xhr.onerror = (event) => {
66
- consola.error("Failed to upload chunk", {
67
- xhrErrorEvent: event,
68
- chunkIndex,
69
- });
70
- reject(new Error("Failed to upload file"));
71
- };
72
-
73
- xhr.send(chunk);
74
- });
75
- };
76
-
77
- export type UploaderManagerConfig = {
78
- /**
79
- * Function to resolve the metadata for a request to upload a file part
80
- * @param uploadId
81
- * @param partNumber
82
- * @returns
83
- */
84
- resolveFilePartUploadURL: (
85
- uploadId: string,
86
- partNumber: number,
87
- ) => Promise<{ method: string; url: string }>;
88
-
89
- /**
90
- * Callback that gets triggered when the whole file is uploaded.
91
- * @param
92
- * @returns
93
- */
94
- onFileUploadComplete?: (payload: {
95
- /**
96
- * Identifier of the upload for this file (must be unique)
97
- */
98
- uploadId: string;
99
- /**
100
- * Ids of all the uploaded file parts
101
- */
102
- filePartIds: Record<number, string>;
103
- }) => void;
104
-
105
- /**
106
- * Callback that gets triggered when an upload has failed.
107
- * @param uploadId
108
- * @param error the error that cause the failure if it exists
109
- * @returns
110
- */
111
- onFileUploadFailed?: (payload: { uploadId: string; error: Error }) => void;
112
-
113
- /**
114
- * Callback that gets triggered when the progress of a file part upload
115
- * for a specific upload id is updated
116
- * @param uploadId
117
- * @param progress
118
- * @returns
119
- */
120
- onProgress?: (uploadId: string, progress: number) => void;
121
- };
122
-
123
- type UploadState = {
124
- uploadId: string;
125
- file: File;
126
- completedChunkCount: number;
127
- uploadedByteCount: number;
128
- state: UploadStateName;
129
- abortController: AbortController;
130
- };
131
-
132
- /**
133
- * Creates and returns an `uploadManager`, a comprehensive multi-file, multi-part upload handler.
134
- *
135
- * The `uploadManager` offers functionality for handling file uploads in parts and provides
136
- * several built-in mechanisms to ensure the process is reliable and adaptable.
137
- *
138
- * Key features include:
139
- *
140
- * - **Chunked File Uploads**: Splits provided files into manageable chunks and initiates the upload
141
- * for each part using the `resolveFilePartUploadURL` function provided in the configuration.
142
- *
143
- * - **Retry Mechanism**: Automatically retries failed upload attempts up to a maximum of 5 times
144
- * per failed request.
145
- *
146
- * - **Upload Cancellation**: Allows cancellation of ongoing uploads, giving users control over the process.
147
- *
148
- * - **Progress Tracking**: Monitors the progress of each file part upload and triggers the `onProgress`
149
- * callback to provide updates.
150
- *
151
- * - **Error Handling**: The returned `uploadFiles` function does not throw errors directly. Instead,
152
- * it manages retries and stops any failed uploads after the retry limit is reached, marking them as failed.
153
- * This ensures that the promise it returns completes gracefully without uncaught exceptions.
154
- *
155
- */
156
- export const createUploadManager = (config: UploaderManagerConfig) => {
157
- const {
158
- resolveFilePartUploadURL: getUploadFilePartUrl,
159
- onFileUploadComplete,
160
- onFileUploadFailed,
161
- } = config;
162
- const uploadStateMap = new Map<string, UploadState>();
163
-
164
- const completedUploads = new Set<string>();
165
- const cancelledUploads = new Set<string>();
166
- const failedUploads = new Set<string>();
167
-
168
- const assertState = (uploadId: string, state: UploadStateName) =>
169
- uploadStateMap.has(uploadId) &&
170
- uploadStateMap.get(uploadId)!.state === state;
171
-
172
- const getUploadState = (uploadId: string) => uploadStateMap.get(uploadId);
173
- const setUploadState = (uploadId: string, state: Partial<UploadState>) => {
174
- uploadStateMap.set(uploadId, {
175
- ...uploadStateMap.get(uploadId)!,
176
- ...state,
177
- });
178
- };
179
-
180
- const isFailed = (uploadId: string) => assertState(uploadId, "failed");
181
- const isCancelled = (uploadId: string) => assertState(uploadId, "cancelled");
182
-
183
- const getTotalFileParts = (file: File) => Math.ceil(file.size / CHUNK_SIZE);
184
-
185
- const abortInFlightUpload = (uploadId: string, reason: string) => {
186
- const currentState = uploadStateMap.get(uploadId);
187
-
188
- if (!currentState) {
189
- return;
190
- }
191
-
192
- currentState.abortController.abort(
193
- new AbortError(
194
- `Upload (${currentState.uploadId}) for file "${currentState.file.name}" aborted. Reason: "${reason}"`,
195
- ),
196
- );
197
- };
198
-
199
- const cancel = (uploadId: string) => {
200
- if (!uploadStateMap.has(uploadId)) {
201
- consola.error("cancel:: Invalid upload id");
202
- return;
203
- }
204
-
205
- setUploadState(uploadId, { state: "cancelled" });
206
-
207
- abortInFlightUpload(uploadId, "upload cancelled");
208
- cancelledUploads.add(uploadId);
209
- };
210
-
211
- const setFailed = (uploadId: string, error: Error) => {
212
- if (!uploadStateMap.has(uploadId)) {
213
- consola.error("failed:: Invalid upload id");
214
- return;
215
- }
216
-
217
- setUploadState(uploadId, { state: "failed" });
218
- onFileUploadFailed?.({ uploadId, error });
219
- failedUploads.add(uploadId);
220
-
221
- if (completedUploads.has(uploadId)) {
222
- completedUploads.delete(uploadId);
223
- }
224
- };
225
-
226
- const uploadFileFromChunkIndex = async (
227
- uploadId: string,
228
- file: File,
229
- initialChunkIndex = 0,
230
- ) => {
231
- const totalChunks = getTotalFileParts(file);
232
- const totalBytes = file.size;
233
- const completedPartIds: Record<number, string> = {};
234
-
235
- const { abortController, runAbortablePromise } = createAbortablePromise();
236
-
237
- uploadStateMap.set(uploadId, {
238
- uploadId,
239
- file,
240
- state: "inprogress",
241
- uploadedByteCount: uploadStateMap.get(uploadId)?.uploadedByteCount ?? 0,
242
- completedChunkCount:
243
- uploadStateMap.get(uploadId)?.completedChunkCount ?? 0,
244
- abortController,
245
- });
246
-
247
- let chunkIndex = initialChunkIndex;
248
- let uploadedBytes = uploadStateMap.get(uploadId)?.uploadedByteCount;
249
-
250
- while (chunkIndex < totalChunks) {
251
- if (isCancelled(uploadId) || isFailed(uploadId)) {
252
- break;
253
- }
254
-
255
- const start = chunkIndex * CHUNK_SIZE;
256
- const end = Math.min(start + CHUNK_SIZE, file.size);
257
-
258
- const chunk = file.slice(start, end);
259
-
260
- try {
261
- const { method, url } = await retryPromise({
262
- fn: () => getUploadFilePartUrl(uploadId, chunkIndex + 1),
263
- retryCount: DEFAULT_RETRY_COUNT,
264
- retryDelayMS: DEFAULT_RETRY_DELAY_MS,
265
- });
266
-
267
- consola.trace("File part upload url resolved", { method, url });
268
-
269
- const onChunkUploadProgress = (uploadedChunkBytes: number) => {
270
- uploadedBytes = uploadedChunkBytes + start;
271
- setUploadState(uploadId, { uploadedByteCount: uploadedBytes });
272
-
273
- const progress = Math.round((uploadedBytes / totalBytes) * 100);
274
-
275
- config.onProgress?.(uploadId, progress);
276
- };
277
-
278
- const filePartResponse = await runAbortablePromise(() =>
279
- retryPromise({
280
- fn: () =>
281
- uploadChunkWithProgress({
282
- method,
283
- url,
284
- chunk,
285
- chunkIndex,
286
- onProgress: onChunkUploadProgress,
287
- abortSignal: abortController.signal,
288
- }),
289
- retryCount: DEFAULT_RETRY_COUNT,
290
- retryDelayMS: DEFAULT_RETRY_DELAY_MS,
291
- }),
292
- );
293
-
294
- consola.trace(
295
- `${file.name}: Chunk ${chunkIndex + 1} of ${totalChunks} uploaded successfully`,
296
- );
297
-
298
- completedPartIds[chunkIndex + 1] = filePartResponse.partId;
299
-
300
- setUploadState(uploadId, {
301
- completedChunkCount:
302
- uploadStateMap.get(uploadId)!.completedChunkCount + 1,
303
- });
304
- } catch (error) {
305
- if (error instanceof AbortError) {
306
- consola.info("Aborted::", error);
307
- return;
308
- }
309
-
310
- setFailed(
311
- uploadId,
312
- error instanceof Error ? error : new Error(error as string),
313
- );
314
- return;
315
- }
316
-
317
- chunkIndex++;
318
- }
319
-
320
- if (isCancelled(uploadId) || isFailed(uploadId)) {
321
- return;
322
- }
323
-
324
- setUploadState(uploadId, {
325
- completedChunkCount: totalChunks,
326
- state: "complete",
327
- });
328
- completedUploads.add(uploadId);
329
- onFileUploadComplete?.({ uploadId, filePartIds: completedPartIds });
330
- };
331
-
332
- const uploadFiles = (files: Array<{ uploadId: string; file: File }>) => {
333
- const result: Array<Promise<unknown>> = [];
334
-
335
- for (const { uploadId, file } of files) {
336
- result.push(uploadFileFromChunkIndex(uploadId, file));
337
- }
338
-
339
- return Promise.allSettled(result).then(() => {
340
- // state cleanup
341
- const completed = [...completedUploads.values()];
342
- const cancelled = [...cancelledUploads.values()];
343
- const failed = [...failedUploads.values()];
344
-
345
- [...completed, ...cancelled, ...failed].forEach((uploadId) =>
346
- uploadStateMap.delete(uploadId),
347
- );
348
-
349
- completedUploads.clear();
350
- cancelledUploads.clear();
351
- failedUploads.clear();
352
-
353
- return {
354
- completed,
355
- cancelled,
356
- failed,
357
- };
358
- });
359
- };
360
-
361
- return {
362
- uploadFiles,
363
-
364
- getUploadState,
365
- isFailed,
366
- isCancelled,
367
- cancel,
368
- setFailed,
369
- };
370
- };
@@ -1,3 +0,0 @@
1
- import muteConsole from "./muteConsole";
2
-
3
- export { muteConsole };
@@ -1,54 +0,0 @@
1
- /* eslint-disable no-console */
2
-
3
- type ConsoleMethod = "log" | "warn" | "error" | "info";
4
- const methods: ConsoleMethod[] = ["log", "warn", "error", "info"];
5
-
6
- /**
7
- * Helper function to run code suppressing all console output
8
- * @param f The function to run. Can be async.
9
- * @returns The original function's return value
10
- */
11
- export default (f: (...args: never[]) => Promise<unknown>) => {
12
- const originals = methods.map((method) => console[method]);
13
- methods.forEach((method) => {
14
- console[method] = () => {};
15
- });
16
-
17
- let originalConsola: typeof consola;
18
- // @ts-expect-error Property 'consola' does not exist on type 'typeof globalThis'.
19
- if (global.consola) {
20
- // @ts-expect-error Property 'consola' does not exist on type 'typeof globalThis'.
21
- originalConsola = global.consola;
22
- // @ts-expect-error Property 'consola' does not exist on type 'typeof globalThis'.
23
- global.consola = consola.create({
24
- level: -1,
25
- });
26
- }
27
-
28
- const restoreOriginals = () => {
29
- methods.forEach((method, i) => {
30
- console[method] = originals[i];
31
- });
32
- if (originalConsola) {
33
- // @ts-expect-error Property 'consola' does not exist on type 'typeof globalThis'.
34
- global.consola = originalConsola;
35
- }
36
- };
37
-
38
- if (f.constructor.name === "AsyncFunction") {
39
- // async
40
- const result = f();
41
- if (result instanceof Promise) {
42
- return result.finally(restoreOriginals);
43
- }
44
- } else {
45
- // sync
46
- try {
47
- return f();
48
- } catch (e) {
49
- throw e;
50
- } finally {
51
- restoreOriginals();
52
- }
53
- }
54
- };
File without changes
File without changes
File without changes
File without changes