@knime/hub-features 1.5.1 → 1.6.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @knime/hub-features
2
2
 
3
+ ## 1.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ef010ff: added DownloadProgressPanel, added name input argument to useDownloadArtifact.start
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [ef010ff]
12
+ - @knime/components@1.22.0
13
+
14
+ ## 1.5.2
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies [bb186d1]
19
+ - @knime/utils@1.3.2
20
+ - @knime/components@1.21.1
21
+
3
22
  ## 1.5.1
4
23
 
5
24
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knime/hub-features",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "Vue components & composables for shared hub features",
5
5
  "homepage": "https://knime.github.io/webapps-common/",
6
6
  "license": "GPL 3 and Additional Permissions according to Sec. 7 (SEE the file LICENSE)",
@@ -31,9 +31,9 @@
31
31
  "lodash-es": "4.17.21",
32
32
  "ofetch": "^1.4.1",
33
33
  "typescript": "^5.4.5",
34
- "@knime/components": "1.21.0",
34
+ "@knime/components": "1.22.0",
35
35
  "@knime/styles": "1.3.1",
36
- "@knime/utils": "1.3.1"
36
+ "@knime/utils": "1.3.2"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "consola": "3.x",
@@ -42,7 +42,6 @@ const labelsEventBus = useEventBus("versionLabels");
42
42
 
43
43
  const closeLabelPopovers = throttle(() => {
44
44
  labelsEventBus.emit("versionLabelShowPopover");
45
- // eslint-disable-next-line no-magic-numbers
46
45
  }, 10000); // Arbitrary delay to reduce overhead, is automatically reset @scrollend
47
46
  </script>
48
47
 
@@ -1,6 +1,7 @@
1
1
  import { computed, ref } from "vue";
2
2
  import { FetchError, type FetchOptions } from "ofetch";
3
3
 
4
+ import type { DownloadItem as DownloadItemExternal } from "@knime/components";
4
5
  import { sleep } from "@knime/utils";
5
6
 
6
7
  import { DEFAULT_API_BASE_URL } from "../common/constants";
@@ -30,13 +31,9 @@ type ItemVersion = number | "current-state" | "most-recent";
30
31
  /**
31
32
  * Tracks the state of each concurrent download.
32
33
  */
33
- type DownloadItem = {
34
- downloadId: string;
34
+ type DownloadItem = DownloadItemExternal & {
35
35
  itemId: string;
36
- status: "READY" | "IN_PROGRESS" | "FAILED" | "CANCELLED";
37
36
  version?: ItemVersion;
38
- downloadUrl?: string;
39
- failureDetails?: string;
40
37
  };
41
38
 
42
39
  export type UseDownloadArtifactOptions = {
@@ -55,6 +52,9 @@ export type UseDownloadArtifactOptions = {
55
52
  customFetchClientOptions?: FetchOptions;
56
53
  };
57
54
 
55
+ const isAbortError = (error: unknown) =>
56
+ error instanceof DOMException && error.name === "AbortError";
57
+
58
58
  /**
59
59
  * This composable supports multiple concurrent downloads. Each time you call ```start```, the following behavior applies:
60
60
  *
@@ -120,6 +120,17 @@ export const useDownloadArtifact = (
120
120
  );
121
121
  };
122
122
 
123
+ const openDownload = (downloadUrl: string) => {
124
+ const a = document.createElement("a");
125
+ a.target = "_blank";
126
+ a.href = downloadUrl;
127
+ a.download = "";
128
+ a.style.display = "none";
129
+ document.body.appendChild(a);
130
+ a.click();
131
+ document.body.removeChild(a);
132
+ };
133
+
123
134
  /**
124
135
  * Makes a request to the download status endpoint for the given download item.
125
136
  * Returns a boolean that is true if a terminal state has been reached (download is ready,
@@ -145,7 +156,8 @@ export const useDownloadArtifact = (
145
156
  downloadItems.value[downloadId].status = "READY";
146
157
  downloadItems.value[downloadId].downloadUrl =
147
158
  statusResponse.downloadUrl;
148
- window.open(statusResponse.downloadUrl, "_parent");
159
+ // open download in a new tab
160
+ openDownload(statusResponse.downloadUrl);
149
161
  return true;
150
162
  } else if (statusResponse?.status === "FAILED") {
151
163
  let details = "Download failed";
@@ -157,10 +169,11 @@ export const useDownloadArtifact = (
157
169
  } catch (error: unknown) {
158
170
  consola.error("Error fetching status:", error);
159
171
  // https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort
160
- const isAbortError =
161
- error instanceof DOMException && error.name === "AbortError";
172
+ const isAborted =
173
+ isAbortError(error) ||
174
+ (error instanceof FetchError && isAbortError(error.cause));
162
175
 
163
- if (!isAbortError) {
176
+ if (!isAborted) {
164
177
  downloadItems.value[downloadId].status = "FAILED";
165
178
  downloadItems.value[downloadId].failureDetails =
166
179
  error instanceof Error ? error.message : (error as string);
@@ -174,20 +187,28 @@ export const useDownloadArtifact = (
174
187
  itemId,
175
188
  downloadId,
176
189
  version,
190
+ name,
177
191
  }: {
178
192
  itemId: string;
179
193
  downloadId: string;
180
194
  version?: ItemVersion;
195
+ name: string;
181
196
  }) => {
182
197
  const controller = new AbortController();
183
198
  abortControllers[downloadId] = controller;
184
199
 
185
200
  const signal = controller.signal;
186
201
 
202
+ let itemName = `${name}`;
203
+ if (version) {
204
+ itemName += ` (${version})`;
205
+ }
206
+
187
207
  downloadItems.value[downloadId] = {
188
208
  itemId,
189
209
  ...(version && { version }),
190
210
  downloadId,
211
+ name: itemName,
191
212
  status: "IN_PROGRESS",
192
213
  };
193
214
 
@@ -226,8 +247,10 @@ export const useDownloadArtifact = (
226
247
  const start = async ({
227
248
  itemId,
228
249
  version,
250
+ name,
229
251
  }: {
230
252
  itemId: string;
253
+ name: string;
231
254
  version?: ItemVersion;
232
255
  }) => {
233
256
  try {
@@ -247,12 +270,13 @@ export const useDownloadArtifact = (
247
270
  downloadStatusResponse?.status === "READY" &&
248
271
  downloadStatusResponse?.downloadUrl
249
272
  ) {
250
- window.open(downloadStatusResponse.downloadUrl, "_parent");
273
+ openDownload(downloadStatusResponse.downloadUrl);
251
274
  } else {
252
275
  await pollDownloadItem({
253
276
  itemId,
254
277
  version,
255
278
  downloadId,
279
+ name,
256
280
  });
257
281
  }
258
282
  } catch (error: unknown) {
@@ -313,5 +337,10 @@ export const useDownloadArtifact = (
313
337
  * Resets the state of the composable, clearing all downloads and controllers.
314
338
  */
315
339
  resetState,
340
+
341
+ /**
342
+ * Opens a download from the given url
343
+ */
344
+ openDownload,
316
345
  };
317
346
  };
@@ -277,7 +277,6 @@ export const useFileUpload = (options: UseFileUploadOptions = {}) => {
277
277
  const uploadPayload = await promise.retryPromise({
278
278
  fn: () => prepareUpload(parentId, enqueableFiles),
279
279
  excludeError: (error: FetchError) =>
280
- // eslint-disable-next-line no-magic-numbers
281
280
  Boolean(error.statusCode && error.statusCode < 500),
282
281
  retryDelayMS: DEFAULT_RETRY_DELAY_MS,
283
282
  });