@knime/product-features 1.2.2 → 1.2.4
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 +18 -0
- package/dist/_plugin-vue_export-helper-BOaGB7Aw.js +8 -0
- package/dist/ai.js +180 -191
- package/dist/ai.js.map +1 -1
- package/dist/dataTransfer.js +1158 -1104
- package/dist/dataTransfer.js.map +1 -1
- package/dist/dist-BrNh9vG-.js +2560 -0
- package/dist/dist-BrNh9vG-.js.map +1 -0
- package/dist/nodes.css +35 -26
- package/dist/nodes.js +656 -613
- package/dist/nodes.js.map +1 -1
- package/dist/rfcErrors-Dw7NLSDp.js +172 -0
- package/dist/rfcErrors-Dw7NLSDp.js.map +1 -0
- package/dist/rfcErrors.js +1 -222
- package/dist/src/ai/KdsPromptInput/InlineStatusBanner.vue.d.ts +3 -2
- package/dist/src/ai/KdsPromptInput/InlineStatusBanner.vue.d.ts.map +1 -1
- package/dist/src/ai/KdsPromptInput/KdsPromptInput.vue.d.ts +6 -6
- package/dist/src/ai/KdsPromptInput/KdsPromptInput.vue.d.ts.map +1 -1
- package/dist/src/dataTransfer/download/ui/DownloadProgressPanel.vue.d.ts +6 -4
- package/dist/src/dataTransfer/download/ui/DownloadProgressPanel.vue.d.ts.map +1 -1
- package/dist/src/dataTransfer/download/ui/DownloadProgressPanelItem.vue.d.ts +3 -2
- package/dist/src/dataTransfer/download/ui/DownloadProgressPanelItem.vue.d.ts.map +1 -1
- package/dist/src/dataTransfer/shared/ui/CollapsiblePanel/CollapsiblePanel.vue.d.ts +11 -14
- package/dist/src/dataTransfer/shared/ui/CollapsiblePanel/CollapsiblePanel.vue.d.ts.map +1 -1
- package/dist/src/dataTransfer/shared/ui/ProgressItem/ProgressItem.vue.d.ts +2 -1
- package/dist/src/dataTransfer/shared/ui/ProgressItem/ProgressItem.vue.d.ts.map +1 -1
- package/dist/src/dataTransfer/shared/ui/ProgressList/ProgressList.vue.d.ts +7 -11
- package/dist/src/dataTransfer/shared/ui/ProgressList/ProgressList.vue.d.ts.map +1 -1
- package/dist/src/dataTransfer/upload/ui/UploadProgressPanel.vue.d.ts +6 -4
- package/dist/src/dataTransfer/upload/ui/UploadProgressPanel.vue.d.ts.map +1 -1
- package/dist/src/dataTransfer/upload/ui/UploadProgressPanelItem.vue.d.ts +3 -2
- package/dist/src/dataTransfer/upload/ui/UploadProgressPanelItem.vue.d.ts.map +1 -1
- package/dist/src/nodes/NodeDescriptionTabs/NodeDescriptionTabs.vue.d.ts +2 -1
- package/dist/src/nodes/NodeDescriptionTabs/NodeDescriptionTabs.vue.d.ts.map +1 -1
- package/dist/src/nodes/NodeDescriptionTabs/OptionsTab.vue.d.ts +2 -1
- package/dist/src/nodes/NodeDescriptionTabs/OptionsTab.vue.d.ts.map +1 -1
- package/dist/src/nodes/NodeDescriptionTabs/PortGroup.vue.d.ts +2 -1
- package/dist/src/nodes/NodeDescriptionTabs/PortGroup.vue.d.ts.map +1 -1
- package/dist/src/nodes/NodeDescriptionTabs/PortTemplateInfo.vue.d.ts +2 -1
- package/dist/src/nodes/NodeDescriptionTabs/PortTemplateInfo.vue.d.ts.map +1 -1
- package/dist/src/nodes/NodeDescriptionTabs/PortsTab.vue.d.ts +2 -1
- package/dist/src/nodes/NodeDescriptionTabs/PortsTab.vue.d.ts.map +1 -1
- package/dist/src/nodes/NodeDescriptionTabs/ViewsTab.vue.d.ts +2 -1
- package/dist/src/nodes/NodeDescriptionTabs/ViewsTab.vue.d.ts.map +1 -1
- package/dist/src/nodes/NodePreview/NodePreview.vue.d.ts +2 -1
- package/dist/src/nodes/NodePreview/NodePreview.vue.d.ts.map +1 -1
- package/dist/src/nodes/NodePreview/SVGNodeTorso.vue.d.ts +2 -1
- package/dist/src/nodes/NodePreview/SVGNodeTorso.vue.d.ts.map +1 -1
- package/dist/src/nodes/NodePreview/enums.d.ts +2 -1
- package/dist/src/nodes/NodePreview/enums.d.ts.map +1 -1
- package/dist/src/nodes/NodePreview/types.d.ts +1 -1
- package/dist/src/nodes/NodePreview/types.d.ts.map +1 -1
- package/dist/src/nodes/PortIcon/PortIcon.vue.d.ts +2 -1
- package/dist/src/nodes/PortIcon/PortIcon.vue.d.ts.map +1 -1
- package/dist/src/nodes/PortIcon/SVGPortIcon.vue.d.ts +2 -1
- package/dist/src/nodes/PortIcon/SVGPortIcon.vue.d.ts.map +1 -1
- package/dist/src/rfcErrors/RFCErrorToastTemplate.vue.d.ts +3 -2
- package/dist/src/rfcErrors/RFCErrorToastTemplate.vue.d.ts.map +1 -1
- package/dist/src/toasts/Toast/Toast.vue.d.ts +3 -4
- package/dist/src/toasts/Toast/Toast.vue.d.ts.map +1 -1
- package/dist/src/toasts/Toast/ToastButton.vue.d.ts +2 -1
- package/dist/src/toasts/Toast/ToastButton.vue.d.ts.map +1 -1
- package/dist/src/toasts/ToastStack/ToastStack.vue.d.ts +2 -1
- package/dist/src/toasts/ToastStack/ToastStack.vue.d.ts.map +1 -1
- package/dist/src/versions/CreateVersionForm/CreateVersionForm.vue.d.ts +3 -2
- package/dist/src/versions/CreateVersionForm/CreateVersionForm.vue.d.ts.map +1 -1
- package/dist/src/versions/VersionHistory/CurrentState.vue.d.ts +3 -2
- package/dist/src/versions/VersionHistory/CurrentState.vue.d.ts.map +1 -1
- package/dist/src/versions/VersionHistory/HistoryEvent.vue.d.ts +7 -11
- package/dist/src/versions/VersionHistory/HistoryEvent.vue.d.ts.map +1 -1
- package/dist/src/versions/VersionHistory/HistoryEventAuthor.vue.d.ts +2 -1
- package/dist/src/versions/VersionHistory/HistoryEventAuthor.vue.d.ts.map +1 -1
- package/dist/src/versions/VersionHistory/HistoryEventFooter.vue.d.ts +2 -1
- package/dist/src/versions/VersionHistory/HistoryEventFooter.vue.d.ts.map +1 -1
- package/dist/src/versions/VersionHistory/VersionHistory.vue.d.ts +3 -2
- package/dist/src/versions/VersionHistory/VersionHistory.vue.d.ts.map +1 -1
- package/dist/src/versions/VersionHistory/VersionItem.vue.d.ts +3 -2
- package/dist/src/versions/VersionHistory/VersionItem.vue.d.ts.map +1 -1
- package/dist/src/versions/VersionHistory/VersionLabel.vue.d.ts +6 -141
- package/dist/src/versions/VersionHistory/VersionLabel.vue.d.ts.map +1 -1
- package/dist/src/versions/VersionHistory/VersionLimitInfo.vue.d.ts +2 -1
- package/dist/src/versions/VersionHistory/VersionLimitInfo.vue.d.ts.map +1 -1
- package/dist/toasts.css +13 -13
- package/dist/toasts.js +4700 -475
- package/dist/toasts.js.map +1 -1
- package/dist/versions.js +583 -597
- package/dist/versions.js.map +1 -1
- package/package.json +5 -5
- package/dist/_plugin-vue_export-helper-pcqpp-6-.js +0 -10
- package/dist/_plugin-vue_export-helper-pcqpp-6-.js.map +0 -1
- package/dist/index-B3ql6hun.js +0 -2695
- package/dist/index-B3ql6hun.js.map +0 -1
- package/dist/rfcErrors.js.map +0 -1
package/dist/dataTransfer.js
CHANGED
|
@@ -1,452 +1,466 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BOaGB7Aw.js";
|
|
2
|
+
import { a as getFileExtension, c as promise_exports, l as sleep, o as getFileMimeType, r as formatFileSize, s as knimeFileFormats } from "./dist-BrNh9vG-.js";
|
|
3
|
+
import { t as rfcErrors } from "./rfcErrors-Dw7NLSDp.js";
|
|
4
|
+
import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeModels, normalizeClass, normalizeStyle, openBlock, reactive, readonly, ref, renderList, renderSlot, toDisplayString, unref, useId, useModel, vShow, watch, withCtx, withDirectives } from "vue";
|
|
5
|
+
import { KdsBadge, KdsButton, KdsIcon, KdsLoadingSkeleton, KdsProgressBar, vKdsTooltip } from "@knime/kds-components";
|
|
6
|
+
import { FetchError, ofetch } from "ofetch";
|
|
7
|
+
import './dataTransfer.css';//#region src/dataTransfer/shared/ui/CollapsiblePanel/useIconFlipper.ts
|
|
8
|
+
var useIconFlipper = (options) => {
|
|
9
|
+
return { iconFlipStyles: computed(() => {
|
|
10
|
+
const declaration = {
|
|
11
|
+
transition: "transform 0.2s linear",
|
|
12
|
+
transform: "scaleY(1)"
|
|
13
|
+
};
|
|
14
|
+
if (options.initialRotation) declaration.rotate = `${options.initialRotation}`;
|
|
15
|
+
const transform = options.active.value ? "scaleY(-1)" : void 0;
|
|
16
|
+
return {
|
|
17
|
+
...declaration,
|
|
18
|
+
transform
|
|
19
|
+
};
|
|
20
|
+
}) };
|
|
21
21
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const _hoisted_4 = { class: "title" };
|
|
95
|
-
const _hoisted_5 = {
|
|
96
|
-
key: 0,
|
|
97
|
-
class: "subtitle"
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/dataTransfer/shared/ui/CollapsiblePanel/CollapsiblePanel.vue?vue&type=script&setup=true&lang.ts
|
|
24
|
+
var _hoisted_1$4 = { class: "title-bar" };
|
|
25
|
+
var _hoisted_2$2 = { class: "actions" };
|
|
26
|
+
var _hoisted_3$1 = ["id"];
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/dataTransfer/shared/ui/CollapsiblePanel/CollapsiblePanel.vue
|
|
29
|
+
var CollapsiblePanel_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
30
|
+
__name: "CollapsiblePanel",
|
|
31
|
+
props: /*@__PURE__*/ mergeModels({
|
|
32
|
+
headline: {},
|
|
33
|
+
canBeDismissed: {
|
|
34
|
+
type: Boolean,
|
|
35
|
+
default: false
|
|
36
|
+
}
|
|
37
|
+
}, {
|
|
38
|
+
"modelValue": {
|
|
39
|
+
type: Boolean,
|
|
40
|
+
default: true
|
|
41
|
+
},
|
|
42
|
+
"modelModifiers": {}
|
|
43
|
+
}),
|
|
44
|
+
emits: /*@__PURE__*/ mergeModels(["close"], ["update:modelValue"]),
|
|
45
|
+
setup(__props, { emit: __emit }) {
|
|
46
|
+
const emit = __emit;
|
|
47
|
+
const modelValue = useModel(__props, "modelValue");
|
|
48
|
+
const { iconFlipStyles } = useIconFlipper({
|
|
49
|
+
active: computed(() => modelValue.value),
|
|
50
|
+
initialRotation: "180deg"
|
|
51
|
+
});
|
|
52
|
+
const panelContentId = useId();
|
|
53
|
+
return (_ctx, _cache) => {
|
|
54
|
+
return openBlock(), createElementBlock("div", { class: normalizeClass(["collapsible-panel", { collapsed: !modelValue.value }]) }, [createElementVNode("div", _hoisted_1$4, [createTextVNode(toDisplayString(__props.headline) + " ", 1), createElementVNode("div", _hoisted_2$2, [createVNode(unref(KdsButton), {
|
|
55
|
+
size: "small",
|
|
56
|
+
variant: "transparent",
|
|
57
|
+
"leading-icon": "chevron-up",
|
|
58
|
+
"aria-expanded": modelValue.value,
|
|
59
|
+
"aria-controls": unref(panelContentId),
|
|
60
|
+
ariaLabel: modelValue.value ? "Collapse" : "Expand",
|
|
61
|
+
style: normalizeStyle(unref(iconFlipStyles)),
|
|
62
|
+
onClick: _cache[0] || (_cache[0] = ($event) => modelValue.value = !modelValue.value)
|
|
63
|
+
}, null, 8, [
|
|
64
|
+
"aria-expanded",
|
|
65
|
+
"aria-controls",
|
|
66
|
+
"ariaLabel",
|
|
67
|
+
"style"
|
|
68
|
+
]), __props.canBeDismissed ? (openBlock(), createBlock(unref(KdsButton), {
|
|
69
|
+
key: 0,
|
|
70
|
+
size: "small",
|
|
71
|
+
variant: "transparent",
|
|
72
|
+
ariaLabel: "Close",
|
|
73
|
+
"leading-icon": "x-close",
|
|
74
|
+
onClick: _cache[1] || (_cache[1] = ($event) => emit("close"))
|
|
75
|
+
})) : createCommentVNode("", true)])]), createVNode(Transition, { name: "expand" }, {
|
|
76
|
+
default: withCtx(() => [withDirectives(createElementVNode("div", {
|
|
77
|
+
id: unref(panelContentId),
|
|
78
|
+
class: "panel-content"
|
|
79
|
+
}, [renderSlot(_ctx.$slots, "default", {}, void 0, true)], 8, _hoisted_3$1), [[vShow, modelValue.value]])]),
|
|
80
|
+
_: 3
|
|
81
|
+
})], 2);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}), [["__scopeId", "data-v-59d71b0b"]]);
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/dataTransfer/shared/ui/ProgressItem/ProgressItem.vue?vue&type=script&setup=true&lang.ts
|
|
87
|
+
var _hoisted_1$3 = { class: "progress-wrapper" };
|
|
88
|
+
var _hoisted_2$1 = { class: "item-info" };
|
|
89
|
+
var _hoisted_3 = { class: "item-name" };
|
|
90
|
+
var _hoisted_4 = { class: "title" };
|
|
91
|
+
var _hoisted_5 = {
|
|
92
|
+
key: 0,
|
|
93
|
+
class: "subtitle"
|
|
98
94
|
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
95
|
+
var _hoisted_6 = { class: "item-action" };
|
|
96
|
+
var _hoisted_7 = {
|
|
97
|
+
key: 0,
|
|
98
|
+
class: "progress"
|
|
103
99
|
};
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
const ProgressItem = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-902ce8bb"]]);
|
|
163
|
-
|
|
164
|
-
const _sfc_main$4 = { };
|
|
165
|
-
|
|
166
|
-
const _hoisted_1$2 = {
|
|
167
|
-
class: "progress-list",
|
|
168
|
-
tabindex: "0"
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/dataTransfer/shared/ui/ProgressItem/ProgressItem.vue
|
|
102
|
+
var ProgressItem_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
103
|
+
__name: "ProgressItem",
|
|
104
|
+
props: {
|
|
105
|
+
id: {},
|
|
106
|
+
headline: {},
|
|
107
|
+
icon: {},
|
|
108
|
+
actions: {},
|
|
109
|
+
subtitle: {},
|
|
110
|
+
progress: {},
|
|
111
|
+
badge: {}
|
|
112
|
+
},
|
|
113
|
+
setup(__props) {
|
|
114
|
+
return (_ctx, _cache) => {
|
|
115
|
+
return openBlock(), createElementBlock("div", _hoisted_1$3, [createElementVNode("div", { class: normalizeClass(["progress-item", { padded: __props.progress === void 0 }]) }, [createElementVNode("div", _hoisted_2$1, [createVNode(unref(KdsIcon), { name: __props.icon }, null, 8, ["name"]), createElementVNode("div", _hoisted_3, [withDirectives((openBlock(), createElementBlock("div", _hoisted_4, [createTextVNode(toDisplayString(__props.headline), 1)])), [[
|
|
116
|
+
unref(vKdsTooltip),
|
|
117
|
+
__props.headline,
|
|
118
|
+
"truncated"
|
|
119
|
+
]]), __props.subtitle ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(__props.subtitle), 1)) : createCommentVNode("", true)])]), createElementVNode("div", _hoisted_6, [__props.badge ? (openBlock(), createBlock(unref(KdsBadge), {
|
|
120
|
+
key: 0,
|
|
121
|
+
variant: __props.badge.variant,
|
|
122
|
+
label: __props.badge.text,
|
|
123
|
+
"leading-icon": __props.badge.icon,
|
|
124
|
+
size: "xxsmall"
|
|
125
|
+
}, null, 8, [
|
|
126
|
+
"variant",
|
|
127
|
+
"label",
|
|
128
|
+
"leading-icon"
|
|
129
|
+
])) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(__props.actions.slice(0, 2), (action) => {
|
|
130
|
+
return openBlock(), createBlock(unref(KdsButton), {
|
|
131
|
+
key: action.id,
|
|
132
|
+
"leading-icon": action.icon,
|
|
133
|
+
ariaLabel: action.label,
|
|
134
|
+
variant: "transparent",
|
|
135
|
+
onClick: action.handler
|
|
136
|
+
}, null, 8, [
|
|
137
|
+
"leading-icon",
|
|
138
|
+
"ariaLabel",
|
|
139
|
+
"onClick"
|
|
140
|
+
]);
|
|
141
|
+
}), 128))])], 2), typeof __props.progress === "number" ? (openBlock(), createElementBlock("div", _hoisted_7, [createVNode(unref(KdsProgressBar), {
|
|
142
|
+
percentage: __props.progress,
|
|
143
|
+
size: "medium"
|
|
144
|
+
}, null, 8, ["percentage"])])) : createCommentVNode("", true)]);
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}), [["__scopeId", "data-v-902ce8bb"]]);
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/dataTransfer/shared/ui/ProgressList/ProgressList.vue
|
|
150
|
+
var _sfc_main = {};
|
|
151
|
+
var _hoisted_1$2 = {
|
|
152
|
+
class: "progress-list",
|
|
153
|
+
tabindex: "0"
|
|
169
154
|
};
|
|
170
|
-
|
|
171
155
|
function _sfc_render(_ctx, _cache) {
|
|
172
|
-
|
|
173
|
-
renderSlot(_ctx.$slots, "default", {}, undefined, true)
|
|
174
|
-
]))
|
|
156
|
+
return openBlock(), createElementBlock("div", _hoisted_1$2, [renderSlot(_ctx.$slots, "default", {}, void 0, true)]);
|
|
175
157
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
() => __props.items.filter(({ status }) => status === "complete").length
|
|
269
|
-
);
|
|
270
|
-
const headline = computed(() => {
|
|
271
|
-
const suffix = `file${totalItems.value > 1 ? "s" : ""}`;
|
|
272
|
-
return `Uploaded ${uploadedItems.value} of ${totalItems.value} ${suffix}`;
|
|
273
|
-
});
|
|
274
|
-
const hasSomeItemInProgress = computed(
|
|
275
|
-
() => __props.items.some(
|
|
276
|
-
({ status }) => status === "inprogress" || status === "processing"
|
|
277
|
-
) || __props.placeholderItems > 0
|
|
278
|
-
);
|
|
279
|
-
return (_ctx, _cache) => {
|
|
280
|
-
return openBlock(), createBlock(unref(CollapsiblePanel), {
|
|
281
|
-
modelValue: expanded.value,
|
|
282
|
-
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => expanded.value = $event),
|
|
283
|
-
headline: headline.value,
|
|
284
|
-
class: "upload-panel",
|
|
285
|
-
"can-be-dismissed": !hasSomeItemInProgress.value,
|
|
286
|
-
onClose: _cache[1] || (_cache[1] = ($event) => emit("close"))
|
|
287
|
-
}, {
|
|
288
|
-
default: withCtx(() => [
|
|
289
|
-
createElementVNode("div", _hoisted_1$1, [
|
|
290
|
-
createVNode(unref(ProgressList), null, {
|
|
291
|
-
default: withCtx(() => [
|
|
292
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
|
|
293
|
-
return openBlock(), createBlock(_sfc_main$3, {
|
|
294
|
-
key: item.id,
|
|
295
|
-
item,
|
|
296
|
-
"allow-cancel": __props.allowCancel,
|
|
297
|
-
onCancel: ($event) => emit("cancel", item)
|
|
298
|
-
}, null, 8, ["item", "allow-cancel", "onCancel"]);
|
|
299
|
-
}), 128))
|
|
300
|
-
]),
|
|
301
|
-
_: 1
|
|
302
|
-
}),
|
|
303
|
-
__props.placeholderItems > 0 ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(__props.placeholderItems, (skeleton) => {
|
|
304
|
-
return openBlock(), createElementBlock("div", {
|
|
305
|
-
key: skeleton,
|
|
306
|
-
class: "skeleton-container"
|
|
307
|
-
}, [
|
|
308
|
-
createVNode(unref(KdsLoadingSkeleton), { variant: "icon-large" }),
|
|
309
|
-
createElementVNode("div", _hoisted_2, [
|
|
310
|
-
createVNode(unref(KdsLoadingSkeleton), { variant: "label" }),
|
|
311
|
-
createVNode(unref(KdsLoadingSkeleton), { variant: "text" })
|
|
312
|
-
])
|
|
313
|
-
]);
|
|
314
|
-
}), 128)) : createCommentVNode("", true)
|
|
315
|
-
])
|
|
316
|
-
]),
|
|
317
|
-
_: 1
|
|
318
|
-
}, 8, ["modelValue", "headline", "can-be-dismissed"]);
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
const UploadProgressPanel = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-15b3eb7d"]]);
|
|
324
|
-
|
|
325
|
-
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
326
|
-
__name: "DownloadProgressPanelItem",
|
|
327
|
-
props: {
|
|
328
|
-
item: {}
|
|
329
|
-
},
|
|
330
|
-
emits: ["cancel", "download"],
|
|
331
|
-
setup(__props, { emit: __emit }) {
|
|
332
|
-
const props = __props;
|
|
333
|
-
const emit = __emit;
|
|
334
|
-
const statusMapper = {
|
|
335
|
-
IN_PROGRESS: ["Zipping", "info", "reload"],
|
|
336
|
-
READY: ["Ready", "success", "circle-info"],
|
|
337
|
-
CANCELLED: ["Cancelled", "error", "circle-error"],
|
|
338
|
-
FAILED: ["Failed", "error", "circle-error"]
|
|
339
|
-
};
|
|
340
|
-
const badge = computed(() => {
|
|
341
|
-
const [text, variant, icon] = statusMapper[props.item.status];
|
|
342
|
-
return {
|
|
343
|
-
text,
|
|
344
|
-
variant,
|
|
345
|
-
icon
|
|
346
|
-
};
|
|
347
|
-
});
|
|
348
|
-
const isCancellable = computed(() => props.item.status === "IN_PROGRESS");
|
|
349
|
-
const isReady = computed(() => props.item.status === "READY");
|
|
350
|
-
const actions = computed(() => {
|
|
351
|
-
const actions2 = [];
|
|
352
|
-
if (isCancellable.value) {
|
|
353
|
-
actions2.push({
|
|
354
|
-
id: "cancel",
|
|
355
|
-
icon: "x-close",
|
|
356
|
-
label: "Cancel",
|
|
357
|
-
handler: () => emit("cancel")
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
if (isReady.value) {
|
|
361
|
-
actions2.push({
|
|
362
|
-
id: "download",
|
|
363
|
-
icon: "cloud-download",
|
|
364
|
-
label: "Download again",
|
|
365
|
-
handler: () => emit("download")
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
return actions2;
|
|
369
|
-
});
|
|
370
|
-
return (_ctx, _cache) => {
|
|
371
|
-
return openBlock(), createBlock(unref(ProgressItem), {
|
|
372
|
-
id: __props.item.downloadId,
|
|
373
|
-
icon: "file",
|
|
374
|
-
headline: __props.item.name,
|
|
375
|
-
badge: badge.value,
|
|
376
|
-
actions: actions.value
|
|
377
|
-
}, null, 8, ["id", "headline", "badge", "actions"]);
|
|
378
|
-
};
|
|
379
|
-
}
|
|
158
|
+
var ProgressList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-8f14a3cb"]]);
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/dataTransfer/upload/ui/UploadProgressPanelItem.vue
|
|
161
|
+
var UploadProgressPanelItem_default = /* @__PURE__ */ defineComponent({
|
|
162
|
+
__name: "UploadProgressPanelItem",
|
|
163
|
+
props: {
|
|
164
|
+
item: {},
|
|
165
|
+
allowCancel: { type: Boolean }
|
|
166
|
+
},
|
|
167
|
+
emits: ["cancel"],
|
|
168
|
+
setup(__props, { emit: __emit }) {
|
|
169
|
+
const props = __props;
|
|
170
|
+
const emit = __emit;
|
|
171
|
+
const hasProgress = (item) => {
|
|
172
|
+
return item.status === "inprogress" || item.status === "complete";
|
|
173
|
+
};
|
|
174
|
+
const totalFormatted = computed(() => {
|
|
175
|
+
return formatFileSize(props.item.size);
|
|
176
|
+
});
|
|
177
|
+
const progressedFormatted = computed(() => {
|
|
178
|
+
if (hasProgress(props.item)) return formatFileSize(props.item.size / 100 * (props.item.progress ?? 0));
|
|
179
|
+
return 0;
|
|
180
|
+
});
|
|
181
|
+
const subtitle = computed(() => props.item.status === "inprogress" ? `${progressedFormatted.value} of ${totalFormatted.value}` : `${totalFormatted.value}`);
|
|
182
|
+
const statusMapper = {
|
|
183
|
+
inprogress: [
|
|
184
|
+
"Uploading",
|
|
185
|
+
"info",
|
|
186
|
+
"reload"
|
|
187
|
+
],
|
|
188
|
+
failed: [
|
|
189
|
+
"Failed",
|
|
190
|
+
"error",
|
|
191
|
+
"circle-error"
|
|
192
|
+
],
|
|
193
|
+
complete: [
|
|
194
|
+
"Uploaded",
|
|
195
|
+
"success",
|
|
196
|
+
"circle-success"
|
|
197
|
+
],
|
|
198
|
+
cancelled: [
|
|
199
|
+
"Cancelled",
|
|
200
|
+
"error",
|
|
201
|
+
"circle-error"
|
|
202
|
+
],
|
|
203
|
+
processing: [
|
|
204
|
+
"Processing",
|
|
205
|
+
"info",
|
|
206
|
+
"reload"
|
|
207
|
+
]
|
|
208
|
+
};
|
|
209
|
+
const itemIcon = computed(() => {
|
|
210
|
+
return getFileExtension(props.item.name) === knimeFileFormats.KNWF.extension ? "workflow" : "file";
|
|
211
|
+
});
|
|
212
|
+
const badge = computed(() => {
|
|
213
|
+
if (!props.item.status) return;
|
|
214
|
+
const [text, variant, icon] = statusMapper[props.item.status];
|
|
215
|
+
return {
|
|
216
|
+
text,
|
|
217
|
+
variant,
|
|
218
|
+
icon
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
const shouldShowCancelAction = computed(() => props.allowCancel && props.item.status === "inprogress");
|
|
222
|
+
const actions = computed(() => {
|
|
223
|
+
return shouldShowCancelAction.value ? [{
|
|
224
|
+
id: "cancel",
|
|
225
|
+
icon: "x-close",
|
|
226
|
+
label: "Cancel",
|
|
227
|
+
handler: () => emit("cancel")
|
|
228
|
+
}] : [];
|
|
229
|
+
});
|
|
230
|
+
return (_ctx, _cache) => {
|
|
231
|
+
return openBlock(), createBlock(unref(ProgressItem_default), {
|
|
232
|
+
id: __props.item.id,
|
|
233
|
+
headline: __props.item.name,
|
|
234
|
+
subtitle: subtitle.value,
|
|
235
|
+
icon: itemIcon.value,
|
|
236
|
+
progress: hasProgress(__props.item) ? __props.item.progress : void 0,
|
|
237
|
+
badge: badge.value,
|
|
238
|
+
actions: actions.value
|
|
239
|
+
}, null, 8, [
|
|
240
|
+
"id",
|
|
241
|
+
"headline",
|
|
242
|
+
"subtitle",
|
|
243
|
+
"icon",
|
|
244
|
+
"progress",
|
|
245
|
+
"badge",
|
|
246
|
+
"actions"
|
|
247
|
+
]);
|
|
248
|
+
};
|
|
249
|
+
}
|
|
380
250
|
});
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
251
|
+
//#endregion
|
|
252
|
+
//#region src/dataTransfer/upload/ui/UploadProgressPanel.vue?vue&type=script&setup=true&lang.ts
|
|
253
|
+
var _hoisted_1$1 = { class: "upload-panel-content" };
|
|
254
|
+
var _hoisted_2 = { class: "skeleton-text-content" };
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region src/dataTransfer/upload/ui/UploadProgressPanel.vue
|
|
257
|
+
var UploadProgressPanel_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
258
|
+
__name: "UploadProgressPanel",
|
|
259
|
+
props: /*@__PURE__*/ mergeModels({
|
|
260
|
+
items: {},
|
|
261
|
+
allowCancel: {
|
|
262
|
+
type: Boolean,
|
|
263
|
+
default: true
|
|
264
|
+
},
|
|
265
|
+
allowRemove: { type: Boolean },
|
|
266
|
+
placeholderItems: { default: 0 }
|
|
267
|
+
}, {
|
|
268
|
+
"expanded": {
|
|
269
|
+
type: Boolean,
|
|
270
|
+
default: true
|
|
271
|
+
},
|
|
272
|
+
"expandedModifiers": {}
|
|
273
|
+
}),
|
|
274
|
+
emits: /*@__PURE__*/ mergeModels(["cancel", "close"], ["update:expanded"]),
|
|
275
|
+
setup(__props, { emit: __emit }) {
|
|
276
|
+
const emit = __emit;
|
|
277
|
+
const expanded = useModel(__props, "expanded");
|
|
278
|
+
const totalItems = computed(() => __props.items.length);
|
|
279
|
+
const uploadedItems = computed(() => __props.items.filter(({ status }) => status === "complete").length);
|
|
280
|
+
const headline = computed(() => {
|
|
281
|
+
const suffix = `file${totalItems.value > 1 ? "s" : ""}`;
|
|
282
|
+
return `Uploaded ${uploadedItems.value} of ${totalItems.value} ${suffix}`;
|
|
283
|
+
});
|
|
284
|
+
const hasSomeItemInProgress = computed(() => __props.items.some(({ status }) => status === "inprogress" || status === "processing") || __props.placeholderItems > 0);
|
|
285
|
+
return (_ctx, _cache) => {
|
|
286
|
+
return openBlock(), createBlock(unref(CollapsiblePanel_default), {
|
|
287
|
+
modelValue: expanded.value,
|
|
288
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => expanded.value = $event),
|
|
289
|
+
headline: headline.value,
|
|
290
|
+
class: "upload-panel",
|
|
291
|
+
"can-be-dismissed": !hasSomeItemInProgress.value,
|
|
292
|
+
onClose: _cache[1] || (_cache[1] = ($event) => emit("close"))
|
|
293
|
+
}, {
|
|
294
|
+
default: withCtx(() => [createElementVNode("div", _hoisted_1$1, [createVNode(unref(ProgressList_default), null, {
|
|
295
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
|
|
296
|
+
return openBlock(), createBlock(UploadProgressPanelItem_default, {
|
|
297
|
+
key: item.id,
|
|
298
|
+
item,
|
|
299
|
+
"allow-cancel": __props.allowCancel,
|
|
300
|
+
onCancel: ($event) => emit("cancel", item)
|
|
301
|
+
}, null, 8, [
|
|
302
|
+
"item",
|
|
303
|
+
"allow-cancel",
|
|
304
|
+
"onCancel"
|
|
305
|
+
]);
|
|
306
|
+
}), 128))]),
|
|
307
|
+
_: 1
|
|
308
|
+
}), __props.placeholderItems > 0 ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(__props.placeholderItems, (skeleton) => {
|
|
309
|
+
return openBlock(), createElementBlock("div", {
|
|
310
|
+
key: skeleton,
|
|
311
|
+
class: "skeleton-container"
|
|
312
|
+
}, [createVNode(unref(KdsLoadingSkeleton), { variant: "icon-large" }), createElementVNode("div", _hoisted_2, [createVNode(unref(KdsLoadingSkeleton), { variant: "label" }), createVNode(unref(KdsLoadingSkeleton), { variant: "text" })])]);
|
|
313
|
+
}), 128)) : createCommentVNode("", true)])]),
|
|
314
|
+
_: 1
|
|
315
|
+
}, 8, [
|
|
316
|
+
"modelValue",
|
|
317
|
+
"headline",
|
|
318
|
+
"can-be-dismissed"
|
|
319
|
+
]);
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}), [["__scopeId", "data-v-15b3eb7d"]]);
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region src/dataTransfer/download/ui/DownloadProgressPanelItem.vue
|
|
325
|
+
var DownloadProgressPanelItem_default = /* @__PURE__ */ defineComponent({
|
|
326
|
+
__name: "DownloadProgressPanelItem",
|
|
327
|
+
props: { item: {} },
|
|
328
|
+
emits: ["cancel", "download"],
|
|
329
|
+
setup(__props, { emit: __emit }) {
|
|
330
|
+
const props = __props;
|
|
331
|
+
const emit = __emit;
|
|
332
|
+
const statusMapper = {
|
|
333
|
+
IN_PROGRESS: [
|
|
334
|
+
"Zipping",
|
|
335
|
+
"info",
|
|
336
|
+
"reload"
|
|
337
|
+
],
|
|
338
|
+
READY: [
|
|
339
|
+
"Ready",
|
|
340
|
+
"success",
|
|
341
|
+
"circle-info"
|
|
342
|
+
],
|
|
343
|
+
CANCELLED: [
|
|
344
|
+
"Cancelled",
|
|
345
|
+
"error",
|
|
346
|
+
"circle-error"
|
|
347
|
+
],
|
|
348
|
+
FAILED: [
|
|
349
|
+
"Failed",
|
|
350
|
+
"error",
|
|
351
|
+
"circle-error"
|
|
352
|
+
]
|
|
353
|
+
};
|
|
354
|
+
const badge = computed(() => {
|
|
355
|
+
const [text, variant, icon] = statusMapper[props.item.status];
|
|
356
|
+
return {
|
|
357
|
+
text,
|
|
358
|
+
variant,
|
|
359
|
+
icon
|
|
360
|
+
};
|
|
361
|
+
});
|
|
362
|
+
const isCancellable = computed(() => props.item.status === "IN_PROGRESS");
|
|
363
|
+
const isReady = computed(() => props.item.status === "READY");
|
|
364
|
+
const actions = computed(() => {
|
|
365
|
+
const actions = [];
|
|
366
|
+
if (isCancellable.value) actions.push({
|
|
367
|
+
id: "cancel",
|
|
368
|
+
icon: "x-close",
|
|
369
|
+
label: "Cancel",
|
|
370
|
+
handler: () => emit("cancel")
|
|
371
|
+
});
|
|
372
|
+
if (isReady.value) actions.push({
|
|
373
|
+
id: "download",
|
|
374
|
+
icon: "cloud-download",
|
|
375
|
+
label: "Download again",
|
|
376
|
+
handler: () => emit("download")
|
|
377
|
+
});
|
|
378
|
+
return actions;
|
|
379
|
+
});
|
|
380
|
+
return (_ctx, _cache) => {
|
|
381
|
+
return openBlock(), createBlock(unref(ProgressItem_default), {
|
|
382
|
+
id: __props.item.downloadId,
|
|
383
|
+
icon: "file",
|
|
384
|
+
headline: __props.item.name,
|
|
385
|
+
badge: badge.value,
|
|
386
|
+
actions: actions.value
|
|
387
|
+
}, null, 8, [
|
|
388
|
+
"id",
|
|
389
|
+
"headline",
|
|
390
|
+
"badge",
|
|
391
|
+
"actions"
|
|
392
|
+
]);
|
|
393
|
+
};
|
|
394
|
+
}
|
|
445
395
|
});
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
//#
|
|
396
|
+
//#endregion
|
|
397
|
+
//#region src/dataTransfer/download/ui/DownloadProgressPanel.vue?vue&type=script&setup=true&lang.ts
|
|
398
|
+
var _hoisted_1 = { class: "download-panel-content" };
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/dataTransfer/download/ui/DownloadProgressPanel.vue
|
|
401
|
+
var DownloadProgressPanel_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
402
|
+
__name: "DownloadProgressPanel",
|
|
403
|
+
props: /*@__PURE__*/ mergeModels({ items: {} }, {
|
|
404
|
+
"expanded": {
|
|
405
|
+
type: Boolean,
|
|
406
|
+
default: true
|
|
407
|
+
},
|
|
408
|
+
"expandedModifiers": {}
|
|
409
|
+
}),
|
|
410
|
+
emits: /*@__PURE__*/ mergeModels([
|
|
411
|
+
"cancel",
|
|
412
|
+
"download",
|
|
413
|
+
"close"
|
|
414
|
+
], ["update:expanded"]),
|
|
415
|
+
setup(__props, { emit: __emit }) {
|
|
416
|
+
const props = __props;
|
|
417
|
+
const emit = __emit;
|
|
418
|
+
const expanded = useModel(__props, "expanded");
|
|
419
|
+
const totalItems = computed(() => props.items.length);
|
|
420
|
+
const totalDownloadedItems = computed(() => props.items.filter(({ status }) => status === "READY").length);
|
|
421
|
+
const headline = computed(() => {
|
|
422
|
+
const suffix = `file${totalItems.value > 1 ? "s" : ""}`;
|
|
423
|
+
return `Downloaded ${totalDownloadedItems.value} of ${totalItems.value} ${suffix}`;
|
|
424
|
+
});
|
|
425
|
+
const hasItemsInProgress = computed(() => props.items.some(({ status }) => status === "IN_PROGRESS"));
|
|
426
|
+
watch(() => props.items?.length, (newValue, oldValue) => {
|
|
427
|
+
if (oldValue > 0 && !newValue) emit("close");
|
|
428
|
+
});
|
|
429
|
+
return (_ctx, _cache) => {
|
|
430
|
+
return openBlock(), createBlock(unref(CollapsiblePanel_default), {
|
|
431
|
+
modelValue: expanded.value,
|
|
432
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => expanded.value = $event),
|
|
433
|
+
headline: headline.value,
|
|
434
|
+
class: "download-panel",
|
|
435
|
+
"can-be-dismissed": !hasItemsInProgress.value,
|
|
436
|
+
onClose: _cache[1] || (_cache[1] = ($event) => emit("close"))
|
|
437
|
+
}, {
|
|
438
|
+
default: withCtx(() => [createElementVNode("div", _hoisted_1, [createVNode(unref(ProgressList_default), null, {
|
|
439
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
|
|
440
|
+
return openBlock(), createBlock(DownloadProgressPanelItem_default, {
|
|
441
|
+
key: item.downloadId,
|
|
442
|
+
item,
|
|
443
|
+
onCancel: ($event) => emit("cancel", item),
|
|
444
|
+
onDownload: ($event) => emit("download", item)
|
|
445
|
+
}, null, 8, [
|
|
446
|
+
"item",
|
|
447
|
+
"onCancel",
|
|
448
|
+
"onDownload"
|
|
449
|
+
]);
|
|
450
|
+
}), 128))]),
|
|
451
|
+
_: 1
|
|
452
|
+
})])]),
|
|
453
|
+
_: 1
|
|
454
|
+
}, 8, [
|
|
455
|
+
"modelValue",
|
|
456
|
+
"headline",
|
|
457
|
+
"can-be-dismissed"
|
|
458
|
+
]);
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
}), [["__scopeId", "data-v-53e10d80"]]);
|
|
462
|
+
//#endregion
|
|
463
|
+
//#region ../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs
|
|
450
464
|
/**
|
|
451
465
|
* Checks if a given value is a plain object.
|
|
452
466
|
*
|
|
@@ -495,8 +509,8 @@ function isPlainObject(value) {
|
|
|
495
509
|
if (!(proto === null || proto === Object.prototype || Object.getPrototypeOf(proto) === null)) return false;
|
|
496
510
|
return Object.prototype.toString.call(value) === "[object Object]";
|
|
497
511
|
}
|
|
498
|
-
|
|
499
|
-
//#region
|
|
512
|
+
//#endregion
|
|
513
|
+
//#region ../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/_internal/isUnsafeProperty.mjs
|
|
500
514
|
/**
|
|
501
515
|
* Checks if a property key is unsafe to modify directly.
|
|
502
516
|
*
|
|
@@ -510,8 +524,8 @@ function isPlainObject(value) {
|
|
|
510
524
|
function isUnsafeProperty(key) {
|
|
511
525
|
return key === "__proto__";
|
|
512
526
|
}
|
|
513
|
-
|
|
514
|
-
//#region
|
|
527
|
+
//#endregion
|
|
528
|
+
//#region ../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/object/merge.mjs
|
|
515
529
|
/**
|
|
516
530
|
* Merges the properties of the source object into the target object.
|
|
517
531
|
*
|
|
@@ -569,671 +583,711 @@ function merge(target, source) {
|
|
|
569
583
|
function isMergeableValue(value) {
|
|
570
584
|
return isPlainObject(value) || Array.isArray(value);
|
|
571
585
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
586
|
+
//#endregion
|
|
587
|
+
//#region src/dataTransfer/shared/httpClient/constants.ts
|
|
588
|
+
var API_BASE_URL = "/_/api";
|
|
589
|
+
//#endregion
|
|
590
|
+
//#region src/dataTransfer/shared/httpClient/createHttpClient.ts
|
|
591
|
+
var defaultConfig = {
|
|
592
|
+
baseURL: API_BASE_URL,
|
|
593
|
+
headers: {
|
|
594
|
+
"Content-Type": "application/json",
|
|
595
|
+
Accept: "application/json; application/problem+json"
|
|
596
|
+
}
|
|
582
597
|
};
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
598
|
+
var createHttpClient = (customOptions) => {
|
|
599
|
+
const { baseURL = API_BASE_URL, ...otherCustomOptions } = customOptions ?? {};
|
|
600
|
+
return ofetch.create(merge({
|
|
601
|
+
baseURL,
|
|
602
|
+
...otherCustomOptions
|
|
603
|
+
}, defaultConfig));
|
|
588
604
|
};
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
};
|
|
626
|
-
xhr.send(chunk);
|
|
627
|
-
});
|
|
605
|
+
//#endregion
|
|
606
|
+
//#region src/dataTransfer/upload/manager/index.ts
|
|
607
|
+
var CHUNK_SIZE = 50 * 1024 * 1024;
|
|
608
|
+
var DEFAULT_RETRY_COUNT = 5;
|
|
609
|
+
var DEFAULT_RETRY_DELAY_MS$1 = 50;
|
|
610
|
+
var uploadChunkWithProgress = (params) => {
|
|
611
|
+
const { method, url, chunk, chunkIndex, onProgress, abortSignal } = params;
|
|
612
|
+
const OK = 200;
|
|
613
|
+
const CREATED = 201;
|
|
614
|
+
return new Promise((resolve, reject) => {
|
|
615
|
+
const xhr = new XMLHttpRequest();
|
|
616
|
+
xhr.open(method, url, true);
|
|
617
|
+
abortSignal.addEventListener("abort", () => xhr.abort());
|
|
618
|
+
xhr.upload.onprogress = (event) => {
|
|
619
|
+
if (event.lengthComputable) onProgress(event.loaded);
|
|
620
|
+
};
|
|
621
|
+
xhr.onload = () => {
|
|
622
|
+
if (xhr.status === OK || xhr.status === CREATED) resolve({ partId: xhr.getResponseHeader("ETag")?.replace(/^(?:W\/)?"|"$/g, "") ?? "" });
|
|
623
|
+
else {
|
|
624
|
+
consola.error("Failed or unexpected XHR response", {
|
|
625
|
+
xhrStatus: xhr.status,
|
|
626
|
+
xhrResponse: xhr.response,
|
|
627
|
+
chunkIndex
|
|
628
|
+
});
|
|
629
|
+
reject(/* @__PURE__ */ new Error("Failed to upload file"));
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
xhr.onerror = (event) => {
|
|
633
|
+
consola.error("Failed to upload chunk", {
|
|
634
|
+
xhrErrorEvent: event,
|
|
635
|
+
chunkIndex
|
|
636
|
+
});
|
|
637
|
+
reject(/* @__PURE__ */ new Error("Failed to upload file"));
|
|
638
|
+
};
|
|
639
|
+
xhr.send(chunk);
|
|
640
|
+
});
|
|
628
641
|
};
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
642
|
+
/**
|
|
643
|
+
* Creates and returns an `uploadManager`, a comprehensive multi-file, multi-part upload handler.
|
|
644
|
+
*
|
|
645
|
+
* The `uploadManager` offers functionality for handling file uploads in parts and provides
|
|
646
|
+
* several built-in mechanisms to ensure the process is reliable and adaptable.
|
|
647
|
+
*
|
|
648
|
+
* Key features include:
|
|
649
|
+
*
|
|
650
|
+
* - **Chunked File Uploads**: Splits provided files into manageable chunks and initiates the upload
|
|
651
|
+
* for each part using the `resolveFilePartUploadURL` function provided in the configuration.
|
|
652
|
+
*
|
|
653
|
+
* - **Retry Mechanism**: Automatically retries failed upload attempts up to a maximum of 5 times
|
|
654
|
+
* per failed request.
|
|
655
|
+
*
|
|
656
|
+
* - **Upload Cancellation**: Allows cancellation of ongoing uploads, giving users control over the process.
|
|
657
|
+
*
|
|
658
|
+
* - **Progress Tracking**: Monitors the progress of each file part upload and triggers the `onProgress`
|
|
659
|
+
* callback to provide updates.
|
|
660
|
+
*
|
|
661
|
+
* - **Error Handling**: The returned `uploadFiles` function does not throw errors directly. Instead,
|
|
662
|
+
* it manages retries and stops any failed uploads after the retry limit is reached, marking them as failed.
|
|
663
|
+
* This ensures that the promise it returns completes gracefully without uncaught exceptions.
|
|
664
|
+
*
|
|
665
|
+
*/
|
|
666
|
+
var createUploadManager = (config) => {
|
|
667
|
+
const { resolveFilePartUploadURL: getUploadFilePartUrl, onFileUploadComplete, onFileUploadFailed } = config;
|
|
668
|
+
const uploadStateMap = /* @__PURE__ */ new Map();
|
|
669
|
+
const completedUploads = /* @__PURE__ */ new Set();
|
|
670
|
+
const cancelledUploads = /* @__PURE__ */ new Set();
|
|
671
|
+
const failedUploads = /* @__PURE__ */ new Set();
|
|
672
|
+
const assertState = (uploadId, state) => uploadStateMap.has(uploadId) && uploadStateMap.get(uploadId).state === state;
|
|
673
|
+
const getUploadState = (uploadId) => uploadStateMap.get(uploadId);
|
|
674
|
+
const setUploadState = (uploadId, state) => {
|
|
675
|
+
uploadStateMap.set(uploadId, {
|
|
676
|
+
...uploadStateMap.get(uploadId),
|
|
677
|
+
...state
|
|
678
|
+
});
|
|
679
|
+
};
|
|
680
|
+
const isFailed = (uploadId) => assertState(uploadId, "failed");
|
|
681
|
+
const isCancelled = (uploadId) => assertState(uploadId, "cancelled");
|
|
682
|
+
const getTotalFileParts = (file) => Math.ceil(file.size / CHUNK_SIZE);
|
|
683
|
+
const abortInFlightUpload = (uploadId, reason) => {
|
|
684
|
+
const currentState = uploadStateMap.get(uploadId);
|
|
685
|
+
if (!currentState) return;
|
|
686
|
+
currentState.abortController.abort(new promise_exports.AbortError(`Upload (${currentState.uploadId}) for file "${currentState.file.name}" aborted. Reason: "${reason}"`));
|
|
687
|
+
};
|
|
688
|
+
const cancel = (uploadId) => {
|
|
689
|
+
if (!uploadStateMap.has(uploadId)) {
|
|
690
|
+
consola.error("cancel:: Invalid upload id");
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
setUploadState(uploadId, { state: "cancelled" });
|
|
694
|
+
abortInFlightUpload(uploadId, "upload cancelled");
|
|
695
|
+
cancelledUploads.add(uploadId);
|
|
696
|
+
};
|
|
697
|
+
const setFailed = (uploadId, error) => {
|
|
698
|
+
if (!uploadStateMap.has(uploadId)) {
|
|
699
|
+
consola.error("failed:: Invalid upload id");
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
setUploadState(uploadId, { state: "failed" });
|
|
703
|
+
onFileUploadFailed?.({
|
|
704
|
+
uploadId,
|
|
705
|
+
error
|
|
706
|
+
});
|
|
707
|
+
failedUploads.add(uploadId);
|
|
708
|
+
if (completedUploads.has(uploadId)) completedUploads.delete(uploadId);
|
|
709
|
+
};
|
|
710
|
+
const uploadFileFromChunkIndex = async (uploadId, file, initialChunkIndex = 0) => {
|
|
711
|
+
const totalChunks = getTotalFileParts(file);
|
|
712
|
+
const totalBytes = file.size;
|
|
713
|
+
const completedPartIds = {};
|
|
714
|
+
const { abortController, runAbortablePromise } = promise_exports.createAbortablePromise();
|
|
715
|
+
uploadStateMap.set(uploadId, {
|
|
716
|
+
uploadId,
|
|
717
|
+
file,
|
|
718
|
+
state: "inprogress",
|
|
719
|
+
uploadedByteCount: uploadStateMap.get(uploadId)?.uploadedByteCount ?? 0,
|
|
720
|
+
completedChunkCount: uploadStateMap.get(uploadId)?.completedChunkCount ?? 0,
|
|
721
|
+
abortController
|
|
722
|
+
});
|
|
723
|
+
let chunkIndex = initialChunkIndex;
|
|
724
|
+
let uploadedBytes = uploadStateMap.get(uploadId)?.uploadedByteCount;
|
|
725
|
+
while (chunkIndex < totalChunks) {
|
|
726
|
+
if (isCancelled(uploadId) || isFailed(uploadId)) break;
|
|
727
|
+
const start = chunkIndex * CHUNK_SIZE;
|
|
728
|
+
const end = Math.min(start + CHUNK_SIZE, file.size);
|
|
729
|
+
const chunk = file.slice(start, end);
|
|
730
|
+
try {
|
|
731
|
+
const { method, url } = await promise_exports.retryPromise({
|
|
732
|
+
fn: () => getUploadFilePartUrl(uploadId, chunkIndex + 1),
|
|
733
|
+
retryCount: DEFAULT_RETRY_COUNT,
|
|
734
|
+
retryDelayMS: DEFAULT_RETRY_DELAY_MS$1
|
|
735
|
+
});
|
|
736
|
+
consola.trace("File part upload url resolved", {
|
|
737
|
+
method,
|
|
738
|
+
url
|
|
739
|
+
});
|
|
740
|
+
const onChunkUploadProgress = (uploadedChunkBytes) => {
|
|
741
|
+
uploadedBytes = uploadedChunkBytes + start;
|
|
742
|
+
setUploadState(uploadId, { uploadedByteCount: uploadedBytes });
|
|
743
|
+
const progress = Math.round(uploadedBytes / totalBytes * 100);
|
|
744
|
+
config.onProgress?.(uploadId, progress);
|
|
745
|
+
};
|
|
746
|
+
const filePartResponse = await runAbortablePromise(() => promise_exports.retryPromise({
|
|
747
|
+
fn: () => uploadChunkWithProgress({
|
|
748
|
+
method,
|
|
749
|
+
url,
|
|
750
|
+
chunk,
|
|
751
|
+
chunkIndex,
|
|
752
|
+
onProgress: onChunkUploadProgress,
|
|
753
|
+
abortSignal: abortController.signal
|
|
754
|
+
}),
|
|
755
|
+
retryCount: DEFAULT_RETRY_COUNT,
|
|
756
|
+
retryDelayMS: DEFAULT_RETRY_DELAY_MS$1
|
|
757
|
+
}));
|
|
758
|
+
consola.trace(`${file.name}: Chunk ${chunkIndex + 1} of ${totalChunks} uploaded successfully`);
|
|
759
|
+
completedPartIds[chunkIndex + 1] = filePartResponse.partId;
|
|
760
|
+
setUploadState(uploadId, { completedChunkCount: uploadStateMap.get(uploadId).completedChunkCount + 1 });
|
|
761
|
+
} catch (error) {
|
|
762
|
+
if (error instanceof promise_exports.AbortError) {
|
|
763
|
+
consola.info("Aborted::", error);
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
setFailed(uploadId, error instanceof Error ? error : new Error(error));
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
chunkIndex++;
|
|
770
|
+
}
|
|
771
|
+
if (isCancelled(uploadId) || isFailed(uploadId)) return;
|
|
772
|
+
setUploadState(uploadId, {
|
|
773
|
+
completedChunkCount: totalChunks,
|
|
774
|
+
state: "complete"
|
|
775
|
+
});
|
|
776
|
+
completedUploads.add(uploadId);
|
|
777
|
+
onFileUploadComplete?.({
|
|
778
|
+
uploadId,
|
|
779
|
+
filePartIds: completedPartIds
|
|
780
|
+
});
|
|
781
|
+
};
|
|
782
|
+
const uploadFiles = (files) => {
|
|
783
|
+
const result = [];
|
|
784
|
+
for (const { uploadId, file } of files) result.push(uploadFileFromChunkIndex(uploadId, file));
|
|
785
|
+
return Promise.allSettled(result).then(() => {
|
|
786
|
+
const completed = [...completedUploads.values()];
|
|
787
|
+
const cancelled = [...cancelledUploads.values()];
|
|
788
|
+
const failed = [...failedUploads.values()];
|
|
789
|
+
[
|
|
790
|
+
...completed,
|
|
791
|
+
...cancelled,
|
|
792
|
+
...failed
|
|
793
|
+
].forEach((uploadId) => uploadStateMap.delete(uploadId));
|
|
794
|
+
completedUploads.clear();
|
|
795
|
+
cancelledUploads.clear();
|
|
796
|
+
failedUploads.clear();
|
|
797
|
+
return {
|
|
798
|
+
completed,
|
|
799
|
+
cancelled,
|
|
800
|
+
failed
|
|
801
|
+
};
|
|
802
|
+
});
|
|
803
|
+
};
|
|
804
|
+
return {
|
|
805
|
+
uploadFiles,
|
|
806
|
+
getUploadState,
|
|
807
|
+
isFailed,
|
|
808
|
+
isCancelled,
|
|
809
|
+
cancel,
|
|
810
|
+
setFailed
|
|
811
|
+
};
|
|
791
812
|
};
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
813
|
+
//#endregion
|
|
814
|
+
//#region src/dataTransfer/upload/ui/useUploadManager.ts
|
|
815
|
+
/**
|
|
816
|
+
* This composable wraps the `uploadManager` utility
|
|
817
|
+
* in order to provide reactive state which will work tightly with the `UploadProgressPanel` component
|
|
818
|
+
*
|
|
819
|
+
* The reactive state returned (`uploadItems`) will update whenever the progress
|
|
820
|
+
* or status of each item being uploaded changes
|
|
821
|
+
*/
|
|
822
|
+
var useUploadManager = (options) => {
|
|
823
|
+
const uploadState = ref({});
|
|
824
|
+
const { uploadFiles, cancel, setFailed } = createUploadManager({
|
|
825
|
+
...options,
|
|
826
|
+
onFileUploadComplete: (params) => {
|
|
827
|
+
const { parentId, name } = uploadState.value[params.uploadId];
|
|
828
|
+
uploadState.value[params.uploadId].status = "complete";
|
|
829
|
+
options.onFileUploadComplete?.({
|
|
830
|
+
...params,
|
|
831
|
+
parentId,
|
|
832
|
+
name
|
|
833
|
+
});
|
|
834
|
+
},
|
|
835
|
+
onFileUploadFailed: ({ uploadId, error }) => {
|
|
836
|
+
const { parentId } = uploadState.value[uploadId];
|
|
837
|
+
uploadState.value[uploadId] = {
|
|
838
|
+
...uploadState.value[uploadId],
|
|
839
|
+
status: "failed",
|
|
840
|
+
failureDetails: error.message
|
|
841
|
+
};
|
|
842
|
+
options.onFileUploadFailed?.({
|
|
843
|
+
uploadId,
|
|
844
|
+
error,
|
|
845
|
+
parentId
|
|
846
|
+
});
|
|
847
|
+
},
|
|
848
|
+
onProgress: (uploadId, progress) => {
|
|
849
|
+
if (uploadState.value[uploadId].status === "inprogress") uploadState.value[uploadId].progress = progress;
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
const start = async (parentId, uploadPayload) => {
|
|
853
|
+
try {
|
|
854
|
+
uploadPayload.forEach(({ uploadId, file }) => {
|
|
855
|
+
uploadState.value[uploadId] = {
|
|
856
|
+
id: uploadId,
|
|
857
|
+
name: file.name,
|
|
858
|
+
size: file.size,
|
|
859
|
+
progress: 0,
|
|
860
|
+
status: "inprogress",
|
|
861
|
+
parentId
|
|
862
|
+
};
|
|
863
|
+
});
|
|
864
|
+
await uploadFiles(uploadPayload);
|
|
865
|
+
} catch (error) {
|
|
866
|
+
consola.error("Error uploading files", error);
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
const canCancel = (uploadId) => uploadState.value[uploadId]?.status === "inprogress" || uploadState.value[uploadId]?.status === "failed";
|
|
870
|
+
const removeItem = (uploadId) => {
|
|
871
|
+
if (canCancel(uploadId)) cancel(uploadId);
|
|
872
|
+
delete uploadState.value[uploadId];
|
|
873
|
+
};
|
|
874
|
+
const resetState = () => {
|
|
875
|
+
uploadState.value = {};
|
|
876
|
+
};
|
|
877
|
+
const uploadItems = computed(() => Object.values(uploadState.value));
|
|
878
|
+
return {
|
|
879
|
+
uploadState,
|
|
880
|
+
hasPendingUploads: computed(() => uploadItems.value.some(({ status }) => status === "inprogress")),
|
|
881
|
+
totalPendingUploads: computed(() => uploadItems.value.filter(({ status }) => status === "inprogress").length),
|
|
882
|
+
start,
|
|
883
|
+
cancel: (uploadId) => {
|
|
884
|
+
cancel(uploadId);
|
|
885
|
+
uploadState.value[uploadId].status = "cancelled";
|
|
886
|
+
},
|
|
887
|
+
canCancel,
|
|
888
|
+
removeItem,
|
|
889
|
+
resetState,
|
|
890
|
+
setFailed: (uploadId, error) => {
|
|
891
|
+
uploadState.value[uploadId] = {
|
|
892
|
+
...uploadState.value[uploadId],
|
|
893
|
+
status: "failed",
|
|
894
|
+
failureDetails: error.message
|
|
895
|
+
};
|
|
896
|
+
setFailed(uploadId, error);
|
|
897
|
+
}
|
|
898
|
+
};
|
|
872
899
|
};
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
itemContentType:
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
900
|
+
//#endregion
|
|
901
|
+
//#region src/dataTransfer/upload/useFileUpload.ts
|
|
902
|
+
var DEFAULT_MAX_UPLOAD_QUEUE_SIZE = 10;
|
|
903
|
+
var DEFAULT_UPLOAD_SIZE_LIMIT_BYTES = 5 * 1024 * 1024 * 1024;
|
|
904
|
+
var DEFAULT_RETRY_DELAY_MS = 50;
|
|
905
|
+
var getUploadSizeLimitBytes = () => DEFAULT_UPLOAD_SIZE_LIMIT_BYTES;
|
|
906
|
+
/**
|
|
907
|
+
* This composable handles uploads of data to a hub repository item (space or workflow group).
|
|
908
|
+
* It is a multi-step process which is described as follows:
|
|
909
|
+
*
|
|
910
|
+
* Step 1:
|
|
911
|
+
* First we need to prepare an upload. The items will be uploaded to a `parent` container
|
|
912
|
+
* identified by its id. Then, for each file we want to upload, we must provide paths relative
|
|
913
|
+
* to the parent that identify each file. Example:
|
|
914
|
+
* ```
|
|
915
|
+
* {
|
|
916
|
+
* "items": {
|
|
917
|
+
* "my-file.txt": { itemContentType: "text/plain", itemContentSize: 5000 },
|
|
918
|
+
* "folder/my-file.zip": { itemContentType: "application/zip", itemContentSize: 5000 },
|
|
919
|
+
* }
|
|
920
|
+
* }
|
|
921
|
+
* ```
|
|
922
|
+
* This describes 2 items, one placed in the parent's root and another inside a folder.
|
|
923
|
+
* The response for this would look like:
|
|
924
|
+
* ```
|
|
925
|
+
* {
|
|
926
|
+
* "items": {
|
|
927
|
+
* "my-file.txt": { uploadId: "id1" },
|
|
928
|
+
* "folder/my-file.zip": { uploadId: "id2" },
|
|
929
|
+
* }
|
|
930
|
+
* }
|
|
931
|
+
* ```
|
|
932
|
+
* Now, each corresponding `uploadId` uniquely identifies the upload process for
|
|
933
|
+
* their respective files.
|
|
934
|
+
*
|
|
935
|
+
* Step 2:
|
|
936
|
+
* Next, we need to start uploading the file parts. Technically, you _can_ just upload
|
|
937
|
+
* each file as one big file "part", but practically that is not recommended because a hiccup
|
|
938
|
+
* in the connection means you would have to start the upload from scratch; instead, we split
|
|
939
|
+
* the files in smaller parts.
|
|
940
|
+
* The upload manager helper will take care of splitting the file. However, the responsibility
|
|
941
|
+
* of this composable is to supply a function to resolve the upload URL. This upload URL is obtained
|
|
942
|
+
* by POSTing to an endpoint with the `uploadId` and the `partNumber` and this will return
|
|
943
|
+
* the data required to make an upload to a presigned URL; this will be called for each file part.
|
|
944
|
+
*
|
|
945
|
+
* Step 3:
|
|
946
|
+
* Completion:
|
|
947
|
+
* Once the upload of a single file is complete, the upload manager will trigger the `onFileUploadComplete`
|
|
948
|
+
* callback, which then will supply the ids of all the different parts that got uploaded for a given `uploadId`
|
|
949
|
+
* Then we need to "complete" the upload by sending a request which provides the `uploadId` and a list of
|
|
950
|
+
* the ids of all the parts. Note that some backends don't return the ids of the file parts after a successful
|
|
951
|
+
* upload of a part, in this case empty strings or null can be used instead of the part id.
|
|
952
|
+
*
|
|
953
|
+
* Cancellation:
|
|
954
|
+
* Alternative to completion, you can also cancel an upload. For this, the composable makes a DELETE request
|
|
955
|
+
* providing the corresponding `uploadId` which will effectively cancel it.
|
|
956
|
+
*
|
|
957
|
+
*
|
|
958
|
+
* For a usage example, refer to the Docs page in storybook.
|
|
959
|
+
*/
|
|
960
|
+
var useFileUpload = (options = {}) => {
|
|
961
|
+
const $ofetch = createHttpClient(options.customFetchClientOptions);
|
|
962
|
+
const prepareUpload = (parentId, files) => {
|
|
963
|
+
const fileDictionary = Object.fromEntries(files.map((file) => {
|
|
964
|
+
return [knimeFileFormats.KNWF.getNameOrDefault(file, file.name), file];
|
|
965
|
+
}));
|
|
966
|
+
const items = Object.fromEntries(Object.entries(fileDictionary).map(([name, file]) => [name, {
|
|
967
|
+
itemContentType: getFileMimeType(file),
|
|
968
|
+
itemContentSize: file.size
|
|
969
|
+
}]));
|
|
970
|
+
return $ofetch(`/repository/${parentId}/manifest`, {
|
|
971
|
+
method: "POST",
|
|
972
|
+
body: { items }
|
|
973
|
+
}).then(({ items }) => {
|
|
974
|
+
return Object.keys(items).map((name) => {
|
|
975
|
+
const { uploadId } = items[name];
|
|
976
|
+
return {
|
|
977
|
+
uploadId,
|
|
978
|
+
file: fileDictionary[name]
|
|
979
|
+
};
|
|
980
|
+
});
|
|
981
|
+
});
|
|
982
|
+
};
|
|
983
|
+
const resolveFilePartUploadURL = (uploadId, partNumber) => {
|
|
984
|
+
return $ofetch(`/uploads/${uploadId}/parts/?partNumber=${partNumber}`, { method: "POST" });
|
|
985
|
+
};
|
|
986
|
+
const completeUpload = (uploadId, partIds) => {
|
|
987
|
+
return $ofetch(`/uploads/${uploadId}`, {
|
|
988
|
+
method: "POST",
|
|
989
|
+
body: partIds
|
|
990
|
+
});
|
|
991
|
+
};
|
|
992
|
+
const cancelUpload = (uploadId) => {
|
|
993
|
+
return $ofetch(`/uploads/${uploadId}`, { method: "DELETE" });
|
|
994
|
+
};
|
|
995
|
+
const unprocessedUploads = reactive(/* @__PURE__ */ new Set());
|
|
996
|
+
const isKNWFFile = (file) => knimeFileFormats.KNWF.matches(file);
|
|
997
|
+
let useUploadManagerResult = null;
|
|
998
|
+
const setProcessingCompleted = ({ uploadId }) => {
|
|
999
|
+
unprocessedUploads.delete(uploadId);
|
|
1000
|
+
};
|
|
1001
|
+
const setProcessingFailed = ({ uploadId, error }) => {
|
|
1002
|
+
unprocessedUploads.delete(uploadId);
|
|
1003
|
+
useUploadManagerResult?.setFailed(uploadId, error ?? /* @__PURE__ */ new Error("An error occurred when processing the file"));
|
|
1004
|
+
};
|
|
1005
|
+
useUploadManagerResult = useUploadManager({
|
|
1006
|
+
resolveFilePartUploadURL,
|
|
1007
|
+
onFileUploadComplete: ({ uploadId, filePartIds, parentId, name }) => {
|
|
1008
|
+
promise_exports.retryPromise({ fn: () => completeUpload(uploadId, filePartIds) }).then(() => {
|
|
1009
|
+
options.onFileUploadComplete?.({
|
|
1010
|
+
uploadId,
|
|
1011
|
+
filePartIds,
|
|
1012
|
+
parentId,
|
|
1013
|
+
name
|
|
1014
|
+
});
|
|
1015
|
+
}).catch((error) => {
|
|
1016
|
+
consola.error("Error attempting to complete upload", { error });
|
|
1017
|
+
setProcessingFailed({
|
|
1018
|
+
uploadId,
|
|
1019
|
+
error
|
|
1020
|
+
});
|
|
1021
|
+
});
|
|
1022
|
+
},
|
|
1023
|
+
onFileUploadFailed: ({ uploadId, error, parentId }) => {
|
|
1024
|
+
options?.onFileUploadFailed?.({
|
|
1025
|
+
uploadId,
|
|
1026
|
+
error,
|
|
1027
|
+
parentId
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
const getEnqueueableFiles = (files) => {
|
|
1032
|
+
const { totalPendingUploads } = useUploadManagerResult;
|
|
1033
|
+
const { maxUploadQueueSize = DEFAULT_MAX_UPLOAD_QUEUE_SIZE } = options;
|
|
1034
|
+
if (files.length + totalPendingUploads.value > maxUploadQueueSize) options.onUploadQueueSizeExceeded?.(maxUploadQueueSize);
|
|
1035
|
+
const effectiveQueueSize = Math.max(0, maxUploadQueueSize - totalPendingUploads.value);
|
|
1036
|
+
return files.slice(0, effectiveQueueSize);
|
|
1037
|
+
};
|
|
1038
|
+
const { uploadState, ...uploadManagerResultRest } = useUploadManagerResult;
|
|
1039
|
+
const uploadQueue = computed(() => {
|
|
1040
|
+
return Object.values(uploadState.value).map((uploadItem) => {
|
|
1041
|
+
if (uploadItem.status === "complete" && unprocessedUploads.has(uploadItem.id)) return {
|
|
1042
|
+
id: uploadItem.id,
|
|
1043
|
+
name: uploadItem.name,
|
|
1044
|
+
parentId: uploadItem.parentId,
|
|
1045
|
+
size: uploadItem.size,
|
|
1046
|
+
status: "processing"
|
|
1047
|
+
};
|
|
1048
|
+
return uploadItem;
|
|
1049
|
+
});
|
|
1050
|
+
});
|
|
1051
|
+
const prepareQueueSize = ref(0);
|
|
1052
|
+
return {
|
|
1053
|
+
...uploadManagerResultRest,
|
|
1054
|
+
uploadQueue,
|
|
1055
|
+
unprocessedUploads: readonly(unprocessedUploads),
|
|
1056
|
+
setProcessingCompleted,
|
|
1057
|
+
setProcessingFailed,
|
|
1058
|
+
isPreparingUpload: computed(() => prepareQueueSize.value > 0),
|
|
1059
|
+
totalFilesBeingPrepared: computed(() => prepareQueueSize.value),
|
|
1060
|
+
start: async (parentId, files, options = {}) => {
|
|
1061
|
+
const { isFileWithProcessing = isKNWFFile } = options;
|
|
1062
|
+
const enqueueableFiles = getEnqueueableFiles(files);
|
|
1063
|
+
const uploadSizeLimitBytes = getUploadSizeLimitBytes();
|
|
1064
|
+
const oversizedFiles = enqueueableFiles.filter((file) => file.size > uploadSizeLimitBytes);
|
|
1065
|
+
if (oversizedFiles.length > 0) throw new rfcErrors.RFCError({
|
|
1066
|
+
title: "Upload size limit exceeded",
|
|
1067
|
+
status: 413,
|
|
1068
|
+
details: oversizedFiles.map((file) => `${file.name} (${formatFileSize(file.size)}) exceeds the upload limit of ${formatFileSize(uploadSizeLimitBytes)}.`)
|
|
1069
|
+
});
|
|
1070
|
+
try {
|
|
1071
|
+
if (enqueueableFiles.length === 0) return [];
|
|
1072
|
+
prepareQueueSize.value += enqueueableFiles.length;
|
|
1073
|
+
const uploadPayload = await promise_exports.retryPromise({
|
|
1074
|
+
fn: () => prepareUpload(parentId, enqueueableFiles),
|
|
1075
|
+
excludeError: (error) => Boolean(error.statusCode && error.statusCode < 500),
|
|
1076
|
+
retryDelayMS: DEFAULT_RETRY_DELAY_MS
|
|
1077
|
+
});
|
|
1078
|
+
uploadPayload.forEach(({ uploadId, file }) => {
|
|
1079
|
+
if (isFileWithProcessing(file)) unprocessedUploads.add(uploadId);
|
|
1080
|
+
});
|
|
1081
|
+
const uploadIds = uploadPayload.map((upload) => upload.uploadId);
|
|
1082
|
+
useUploadManagerResult.start(parentId, uploadPayload);
|
|
1083
|
+
return uploadIds;
|
|
1084
|
+
} catch (error) {
|
|
1085
|
+
throw rfcErrors.tryParse(error) ?? error;
|
|
1086
|
+
} finally {
|
|
1087
|
+
prepareQueueSize.value -= enqueueableFiles.length;
|
|
1088
|
+
}
|
|
1089
|
+
},
|
|
1090
|
+
cancel: (uploadId) => {
|
|
1091
|
+
useUploadManagerResult.cancel(uploadId);
|
|
1092
|
+
cancelUpload(uploadId).catch((error) => {
|
|
1093
|
+
consola.error("There was a problem cancelling the upload", { error });
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1045
1097
|
};
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1098
|
+
//#endregion
|
|
1099
|
+
//#region src/dataTransfer/download/useDownloadArtifact.ts
|
|
1100
|
+
var DEFAULT_MAX_RETRIES = null;
|
|
1101
|
+
var DEFAULT_POLLING_INTERVAL = 2e3;
|
|
1102
|
+
var hasDownloadUrl = (response) => {
|
|
1103
|
+
return "downloadUrl" in response;
|
|
1051
1104
|
};
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1105
|
+
var isAbortError = (error) => error instanceof DOMException && error.name === "AbortError";
|
|
1106
|
+
/**
|
|
1107
|
+
* This composable supports multiple concurrent downloads. Each time you call ```start```, the following behavior applies:
|
|
1108
|
+
*
|
|
1109
|
+
* 1. **Immediate-ready artifact**: If the first status check for the artifact returns `READY`, then there's no polling and no need
|
|
1110
|
+
* to add it to `downloadItems`. The download URL is opened in the same tab immediately.
|
|
1111
|
+
*
|
|
1112
|
+
* 2. **Not-ready artifact**: If the artifact is not yet ready (e.g. needs zipping), then we add a record to `downloadItems` with status `IN_PROGRESS`
|
|
1113
|
+
* and repeatedly poll until it becomes `READY`, the polling times out, or the user aborts.
|
|
1114
|
+
*
|
|
1115
|
+
* **Usage**:
|
|
1116
|
+
* ```
|
|
1117
|
+
* const { start, cancel, downloadItems } = useDownloadArtifact({ ...options });
|
|
1118
|
+
*
|
|
1119
|
+
* // start a download
|
|
1120
|
+
* await start({ itemId: "1234", version: 1 });
|
|
1121
|
+
*
|
|
1122
|
+
* // if needed, cancel it:
|
|
1123
|
+
* cancel("1234");
|
|
1124
|
+
*
|
|
1125
|
+
* // downloadItems (as a computed array) will reflect the current statuses.
|
|
1126
|
+
* ```
|
|
1127
|
+
*/
|
|
1128
|
+
var useDownloadArtifact = (options = {}) => {
|
|
1129
|
+
const $ofetch = createHttpClient(options.customFetchClientOptions);
|
|
1130
|
+
const downloadItems = ref({});
|
|
1131
|
+
const abortControllers = {};
|
|
1132
|
+
const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
1133
|
+
const pollingInterval = options.pollingInterval ?? DEFAULT_POLLING_INTERVAL;
|
|
1134
|
+
const cancel = (downloadId) => {
|
|
1135
|
+
const itemController = abortControllers[downloadId];
|
|
1136
|
+
if (itemController) {
|
|
1137
|
+
itemController.abort();
|
|
1138
|
+
delete abortControllers[downloadId];
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
const removeItem = (downloadId) => {
|
|
1142
|
+
cancel(downloadId);
|
|
1143
|
+
delete downloadItems.value[downloadId];
|
|
1144
|
+
};
|
|
1145
|
+
const fetchDownloadStatus = ({ downloadId, signal }) => {
|
|
1146
|
+
return $ofetch(`/downloads/${downloadId}/status`, {
|
|
1147
|
+
method: "GET",
|
|
1148
|
+
signal
|
|
1149
|
+
});
|
|
1150
|
+
};
|
|
1151
|
+
const openDownload = (downloadUrl) => {
|
|
1152
|
+
const a = document.createElement("a");
|
|
1153
|
+
a.target = "_blank";
|
|
1154
|
+
a.href = downloadUrl;
|
|
1155
|
+
a.download = "";
|
|
1156
|
+
a.style.display = "none";
|
|
1157
|
+
document.body.appendChild(a);
|
|
1158
|
+
a.click();
|
|
1159
|
+
document.body.removeChild(a);
|
|
1160
|
+
};
|
|
1161
|
+
/**
|
|
1162
|
+
* Makes a request to the download status endpoint for the given download item.
|
|
1163
|
+
* Returns a boolean that is true if a terminal state has been reached (download is ready,
|
|
1164
|
+
* download failed, or user aborted download)
|
|
1165
|
+
* @param downloadId the `downloadId`
|
|
1166
|
+
* @param signal the abort signal passed to the request to be able to abort it
|
|
1167
|
+
* @returns a boolean indicating whether a terminal state has been reached
|
|
1168
|
+
*/
|
|
1169
|
+
const handlePollingResult = async ({ downloadId, signal }) => {
|
|
1170
|
+
try {
|
|
1171
|
+
const statusResponse = await fetchDownloadStatus({
|
|
1172
|
+
downloadId,
|
|
1173
|
+
signal
|
|
1174
|
+
});
|
|
1175
|
+
if (statusResponse?.status === "READY" && statusResponse?.downloadUrl) {
|
|
1176
|
+
downloadItems.value[downloadId].status = "READY";
|
|
1177
|
+
downloadItems.value[downloadId].downloadUrl = statusResponse.downloadUrl;
|
|
1178
|
+
openDownload(statusResponse.downloadUrl);
|
|
1179
|
+
return true;
|
|
1180
|
+
} else if (statusResponse?.status === "FAILED") {
|
|
1181
|
+
let details = "Download failed";
|
|
1182
|
+
if (statusResponse?.statusMessage) details += `: ${statusResponse.statusMessage}`;
|
|
1183
|
+
throw new Error(details);
|
|
1184
|
+
}
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
consola.error("Error fetching status:", error);
|
|
1187
|
+
if (!(isAbortError(error) || error instanceof FetchError && isAbortError(error.cause))) {
|
|
1188
|
+
downloadItems.value[downloadId].status = "FAILED";
|
|
1189
|
+
downloadItems.value[downloadId].failureDetails = error instanceof Error ? error.message : error;
|
|
1190
|
+
}
|
|
1191
|
+
return true;
|
|
1192
|
+
}
|
|
1193
|
+
return false;
|
|
1194
|
+
};
|
|
1195
|
+
const pollDownloadItem = async ({ itemId, downloadId, version, name }) => {
|
|
1196
|
+
const controller = new AbortController();
|
|
1197
|
+
abortControllers[downloadId] = controller;
|
|
1198
|
+
const signal = controller.signal;
|
|
1199
|
+
let itemName = `${name}`;
|
|
1200
|
+
if (version) itemName += ` (${version})`;
|
|
1201
|
+
downloadItems.value[downloadId] = {
|
|
1202
|
+
itemId,
|
|
1203
|
+
...version && { version },
|
|
1204
|
+
downloadId,
|
|
1205
|
+
name: itemName,
|
|
1206
|
+
status: "IN_PROGRESS"
|
|
1207
|
+
};
|
|
1208
|
+
const abortListener = () => {
|
|
1209
|
+
controller.signal.removeEventListener("abort", abortListener);
|
|
1210
|
+
if (!downloadItems.value[downloadId]) return;
|
|
1211
|
+
downloadItems.value[downloadId].status = "CANCELLED";
|
|
1212
|
+
downloadItems.value[downloadId].failureDetails = "Download was cancelled manually";
|
|
1213
|
+
};
|
|
1214
|
+
controller.signal.addEventListener("abort", abortListener);
|
|
1215
|
+
let tries = 0;
|
|
1216
|
+
while (maxRetries === null || tries < maxRetries) {
|
|
1217
|
+
tries++;
|
|
1218
|
+
await sleep(pollingInterval);
|
|
1219
|
+
if (await handlePollingResult({
|
|
1220
|
+
downloadId,
|
|
1221
|
+
signal
|
|
1222
|
+
})) break;
|
|
1223
|
+
}
|
|
1224
|
+
delete abortControllers[downloadId];
|
|
1225
|
+
if (maxRetries !== null && tries >= maxRetries) {
|
|
1226
|
+
downloadItems.value[downloadId].status = "FAILED";
|
|
1227
|
+
downloadItems.value[downloadId].failureDetails = "Download timed out";
|
|
1228
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
const start = async ({ itemId, version, name }) => {
|
|
1231
|
+
try {
|
|
1232
|
+
const response = await $ofetch(`/repository/${itemId}/artifact`, {
|
|
1233
|
+
method: "GET",
|
|
1234
|
+
query: { version }
|
|
1235
|
+
});
|
|
1236
|
+
if (hasDownloadUrl(response)) {
|
|
1237
|
+
openDownload(response.downloadUrl);
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
await pollDownloadItem({
|
|
1241
|
+
itemId,
|
|
1242
|
+
version,
|
|
1243
|
+
downloadId: response.downloadId,
|
|
1244
|
+
name
|
|
1245
|
+
});
|
|
1246
|
+
} catch (error) {
|
|
1247
|
+
throw rfcErrors.tryParse(error) ?? error;
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
const totalItemsBeingZipped = computed(() => Object.values(downloadItems.value).filter(({ status }) => status === "IN_PROGRESS").length);
|
|
1251
|
+
const resetState = () => {
|
|
1252
|
+
for (const [downloadId, controller] of Object.entries(abortControllers)) {
|
|
1253
|
+
controller?.abort();
|
|
1254
|
+
delete abortControllers[downloadId];
|
|
1255
|
+
}
|
|
1256
|
+
downloadItems.value = {};
|
|
1257
|
+
};
|
|
1258
|
+
return {
|
|
1259
|
+
/**
|
|
1260
|
+
* Initiates a download of the given itemId and version.
|
|
1261
|
+
*/
|
|
1262
|
+
start,
|
|
1263
|
+
/**
|
|
1264
|
+
* Cancels the ongoing download for a given itemId (if any).
|
|
1265
|
+
* Sets the status in `downloadItems` to `CANCELLED`.
|
|
1266
|
+
*/
|
|
1267
|
+
cancel,
|
|
1268
|
+
/**
|
|
1269
|
+
* Removes item from `downloadItems`.
|
|
1270
|
+
*/
|
|
1271
|
+
removeItem,
|
|
1272
|
+
/**
|
|
1273
|
+
* A computed array of all current items that are being zipped and prepared for download.
|
|
1274
|
+
*/
|
|
1275
|
+
downloadItems: computed(() => Object.values(downloadItems.value)),
|
|
1276
|
+
/**
|
|
1277
|
+
* The total number of zipping items in progress.
|
|
1278
|
+
*/
|
|
1279
|
+
totalItemsBeingZipped,
|
|
1280
|
+
/**
|
|
1281
|
+
* Resets the state of the composable, clearing all downloads and controllers.
|
|
1282
|
+
*/
|
|
1283
|
+
resetState,
|
|
1284
|
+
/**
|
|
1285
|
+
* Opens a download from the given url
|
|
1286
|
+
*/
|
|
1287
|
+
openDownload
|
|
1288
|
+
};
|
|
1236
1289
|
};
|
|
1290
|
+
//#endregion
|
|
1291
|
+
export { API_BASE_URL, DownloadProgressPanel_default as DownloadProgressPanel, UploadProgressPanel_default as UploadProgressPanel, createHttpClient, defaultConfig, useDownloadArtifact, useFileUpload };
|
|
1237
1292
|
|
|
1238
|
-
|
|
1239
|
-
//# sourceMappingURL=dataTransfer.js.map
|
|
1293
|
+
//# sourceMappingURL=dataTransfer.js.map
|