@knime/product-features 3.0.1 → 4.0.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.
@@ -1 +1 @@
1
- {"version":3,"file":"dataTransfer.js","names":[],"sources":["../src/dataTransfer/shared/ui/CollapsiblePanel/useIconFlipper.ts","../src/dataTransfer/shared/ui/CollapsiblePanel/CollapsiblePanel.vue","../src/dataTransfer/shared/ui/CollapsiblePanel/CollapsiblePanel.vue","../src/dataTransfer/shared/ui/ProgressItem/ProgressItem.vue","../src/dataTransfer/shared/ui/ProgressItem/ProgressItem.vue","../src/dataTransfer/shared/ui/ProgressList/ProgressList.vue","../src/dataTransfer/download/ui/DownloadProgressPanelItem.vue","../src/dataTransfer/download/ui/DownloadProgressPanelItem.vue","../src/dataTransfer/download/ui/DownloadProgressPanel.vue","../src/dataTransfer/download/ui/DownloadProgressPanel.vue","../src/dataTransfer/download/useDownloadArtifact.ts","../src/dataTransfer/upload/UploadProgressPanelItem.vue","../src/dataTransfer/upload/UploadProgressPanelItem.vue","../src/dataTransfer/upload/UploadProgressPanel.vue","../src/dataTransfer/upload/UploadProgressPanel.vue","../src/dataTransfer/upload/useUploadManager.ts","../src/dataTransfer/upload/useFileUpload.ts"],"sourcesContent":["import { type CSSProperties, type ComputedRef, computed } from \"vue\";\n\ntype UseIconFlipperOptions = {\n active: ComputedRef<boolean>;\n initialRotation?: `${string}deg`;\n};\n\nexport const useIconFlipper = (options: UseIconFlipperOptions) => {\n const iconFlipStyles = computed<CSSProperties>(() => {\n const declaration: Partial<CSSProperties> = {\n transition: \"transform 0.2s linear\",\n transform: \"scaleY(1)\",\n };\n\n if (options.initialRotation) {\n declaration.rotate = `${options.initialRotation}`;\n }\n\n const transform = options.active.value ? \"scaleY(-1)\" : undefined;\n\n return { ...declaration, transform };\n });\n\n return { iconFlipStyles };\n};\n","<script setup lang=\"ts\">\nimport { computed, useId } from \"vue\";\n\nimport { KdsButton } from \"@knime/kds-components\";\n\nimport type { CollapsiblePanelProps } from \"./types\";\nimport { useIconFlipper } from \"./useIconFlipper\";\n\nconst { headline, canBeDismissed = false } =\n defineProps<CollapsiblePanelProps>();\n\nconst emit = defineEmits<{\n close: [];\n}>();\n\nconst modelValue = defineModel<boolean>({ default: true });\n\nconst { iconFlipStyles } = useIconFlipper({\n active: computed(() => modelValue.value),\n initialRotation: \"180deg\",\n});\nconst panelContentId = useId();\n</script>\n\n<template>\n <div class=\"collapsible-panel\" :class=\"{ collapsed: !modelValue }\">\n <div class=\"title-bar\">\n {{ headline }}\n <div class=\"actions\">\n <KdsButton\n size=\"small\"\n variant=\"transparent\"\n leading-icon=\"chevron-up\"\n :aria-expanded=\"modelValue\"\n :aria-controls=\"panelContentId\"\n :ariaLabel=\"modelValue ? 'Collapse' : 'Expand'\"\n :style=\"iconFlipStyles\"\n @click=\"modelValue = !modelValue\"\n />\n\n <KdsButton\n v-if=\"canBeDismissed\"\n size=\"small\"\n variant=\"transparent\"\n ariaLabel=\"Close\"\n leading-icon=\"x-close\"\n @click=\"emit('close')\"\n />\n </div>\n </div>\n\n <Transition name=\"expand\">\n <div v-show=\"modelValue\" :id=\"panelContentId\" class=\"panel-content\">\n <slot />\n </div>\n </Transition>\n </div>\n</template>\n\n<style scoped>\n.collapsible-panel {\n background: var(--kds-color-surface-default);\n border-radius: var(--kds-spacing-container-0-25x);\n box-shadow: var(--kds-elevation-level-3);\n\n & .title-bar {\n display: flex;\n align-items: center;\n height: var(--kds-dimension-component-height-2-5x);\n padding: var(--kds-spacing-container-0-5x)\n var(--kds-spacing-container-0-75x);\n font: var(--kds-font-base-title-small-strong);\n color: var(--kds-color-text-and-icon-neutral);\n\n & .actions {\n display: flex;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n margin-left: auto;\n }\n }\n\n &:not(.collapsed) {\n & .title-bar {\n border-bottom: var(--kds-border-base-subtle);\n }\n }\n}\n\n.panel-content {\n overflow: hidden;\n}\n\n.expand-enter-active,\n.expand-leave-active {\n display: grid;\n overflow: hidden;\n transition:\n grid-template-rows 0.2s ease-in-out,\n opacity 0.2s ease-in-out;\n}\n\n.expand-enter-from,\n.expand-leave-to {\n grid-template-rows: 0fr;\n opacity: 0;\n}\n\n.expand-enter-to,\n.expand-leave-from {\n grid-template-rows: 1fr;\n opacity: 1;\n}\n\n.panel-content > :deep(*) {\n min-height: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useId } from \"vue\";\n\nimport { KdsButton } from \"@knime/kds-components\";\n\nimport type { CollapsiblePanelProps } from \"./types\";\nimport { useIconFlipper } from \"./useIconFlipper\";\n\nconst { headline, canBeDismissed = false } =\n defineProps<CollapsiblePanelProps>();\n\nconst emit = defineEmits<{\n close: [];\n}>();\n\nconst modelValue = defineModel<boolean>({ default: true });\n\nconst { iconFlipStyles } = useIconFlipper({\n active: computed(() => modelValue.value),\n initialRotation: \"180deg\",\n});\nconst panelContentId = useId();\n</script>\n\n<template>\n <div class=\"collapsible-panel\" :class=\"{ collapsed: !modelValue }\">\n <div class=\"title-bar\">\n {{ headline }}\n <div class=\"actions\">\n <KdsButton\n size=\"small\"\n variant=\"transparent\"\n leading-icon=\"chevron-up\"\n :aria-expanded=\"modelValue\"\n :aria-controls=\"panelContentId\"\n :ariaLabel=\"modelValue ? 'Collapse' : 'Expand'\"\n :style=\"iconFlipStyles\"\n @click=\"modelValue = !modelValue\"\n />\n\n <KdsButton\n v-if=\"canBeDismissed\"\n size=\"small\"\n variant=\"transparent\"\n ariaLabel=\"Close\"\n leading-icon=\"x-close\"\n @click=\"emit('close')\"\n />\n </div>\n </div>\n\n <Transition name=\"expand\">\n <div v-show=\"modelValue\" :id=\"panelContentId\" class=\"panel-content\">\n <slot />\n </div>\n </Transition>\n </div>\n</template>\n\n<style scoped>\n.collapsible-panel {\n background: var(--kds-color-surface-default);\n border-radius: var(--kds-spacing-container-0-25x);\n box-shadow: var(--kds-elevation-level-3);\n\n & .title-bar {\n display: flex;\n align-items: center;\n height: var(--kds-dimension-component-height-2-5x);\n padding: var(--kds-spacing-container-0-5x)\n var(--kds-spacing-container-0-75x);\n font: var(--kds-font-base-title-small-strong);\n color: var(--kds-color-text-and-icon-neutral);\n\n & .actions {\n display: flex;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n margin-left: auto;\n }\n }\n\n &:not(.collapsed) {\n & .title-bar {\n border-bottom: var(--kds-border-base-subtle);\n }\n }\n}\n\n.panel-content {\n overflow: hidden;\n}\n\n.expand-enter-active,\n.expand-leave-active {\n display: grid;\n overflow: hidden;\n transition:\n grid-template-rows 0.2s ease-in-out,\n opacity 0.2s ease-in-out;\n}\n\n.expand-enter-from,\n.expand-leave-to {\n grid-template-rows: 0fr;\n opacity: 0;\n}\n\n.expand-enter-to,\n.expand-leave-from {\n grid-template-rows: 1fr;\n opacity: 1;\n}\n\n.panel-content > :deep(*) {\n min-height: 0;\n}\n</style>\n","<script lang=\"ts\" setup>\nimport {\n KdsBadge,\n KdsButton,\n KdsIcon,\n KdsProgressBar,\n vKdsTooltip,\n} from \"@knime/kds-components\";\n\nimport { type ProgressItemProps } from \"./types\";\n\ndefineProps<ProgressItemProps>();\n</script>\n\n<template>\n <div class=\"progress-wrapper\">\n <div :class=\"['progress-item', { padded: progress === undefined }]\">\n <div class=\"item-info\">\n <KdsIcon :name=\"icon\" />\n\n <div class=\"item-name\">\n <div v-kds-tooltip:truncated=\"headline\" class=\"title\">\n {{ headline }}\n </div>\n <span v-if=\"subtitle\" class=\"subtitle\">\n {{ subtitle }}\n </span>\n </div>\n </div>\n\n <div class=\"item-action\">\n <KdsBadge\n v-if=\"badge\"\n :variant=\"badge.variant\"\n :label=\"badge.text\"\n :leading-icon=\"badge.icon\"\n size=\"xxsmall\"\n />\n\n <template v-for=\"action in actions.slice(0, 2)\" :key=\"action.id\">\n <KdsButton\n :leading-icon=\"action.icon\"\n :ariaLabel=\"action.label\"\n variant=\"transparent\"\n @click=\"action.handler\"\n />\n </template>\n </div>\n </div>\n\n <div v-if=\"typeof progress === 'number'\" class=\"progress\">\n <KdsProgressBar :percentage=\"progress\" size=\"medium\" />\n </div>\n </div>\n</template>\n\n<style scoped>\n.progress-wrapper {\n --base-padding: var(--kds-spacing-container-1x);\n --progress-bar-height: var(--kds-dimension-component-height-0-25x);\n --progress-bar-distance: var(--kds-spacing-container-0-25x);\n\n display: flex;\n flex-direction: column;\n gap: var(--progress-bar-distance);\n height: var(--kds-dimension-component-height-4x);\n padding: var(--base-padding);\n}\n\n.progress-item {\n display: flex;\n align-items: center;\n width: 100%;\n height: 100%;\n\n /* add extra margin to prevent content jump when a progress bars appears/disappears */\n &.padded {\n margin-bottom: calc(\n var(--progress-bar-height) + var(--progress-bar-distance)\n );\n }\n}\n\n.progress {\n min-height: var(--progress-bar-height);\n}\n\n.item-info {\n display: flex;\n flex-grow: 1;\n gap: var(--kds-spacing-container-0-25x);\n overflow: hidden;\n\n & .item-name {\n display: flex;\n flex-grow: 1;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n justify-content: center;\n overflow: hidden;\n color: var(--kds-color-text-and-icon-neutral);\n\n & .title {\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n font: var(--kds-font-base-interactive-small);\n white-space: nowrap;\n }\n\n & .subtitle {\n font: var(--kds-font-base-subtext-small);\n }\n }\n}\n\n.item-action {\n display: flex;\n flex-shrink: 0;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n}\n</style>\n","<script lang=\"ts\" setup>\nimport {\n KdsBadge,\n KdsButton,\n KdsIcon,\n KdsProgressBar,\n vKdsTooltip,\n} from \"@knime/kds-components\";\n\nimport { type ProgressItemProps } from \"./types\";\n\ndefineProps<ProgressItemProps>();\n</script>\n\n<template>\n <div class=\"progress-wrapper\">\n <div :class=\"['progress-item', { padded: progress === undefined }]\">\n <div class=\"item-info\">\n <KdsIcon :name=\"icon\" />\n\n <div class=\"item-name\">\n <div v-kds-tooltip:truncated=\"headline\" class=\"title\">\n {{ headline }}\n </div>\n <span v-if=\"subtitle\" class=\"subtitle\">\n {{ subtitle }}\n </span>\n </div>\n </div>\n\n <div class=\"item-action\">\n <KdsBadge\n v-if=\"badge\"\n :variant=\"badge.variant\"\n :label=\"badge.text\"\n :leading-icon=\"badge.icon\"\n size=\"xxsmall\"\n />\n\n <template v-for=\"action in actions.slice(0, 2)\" :key=\"action.id\">\n <KdsButton\n :leading-icon=\"action.icon\"\n :ariaLabel=\"action.label\"\n variant=\"transparent\"\n @click=\"action.handler\"\n />\n </template>\n </div>\n </div>\n\n <div v-if=\"typeof progress === 'number'\" class=\"progress\">\n <KdsProgressBar :percentage=\"progress\" size=\"medium\" />\n </div>\n </div>\n</template>\n\n<style scoped>\n.progress-wrapper {\n --base-padding: var(--kds-spacing-container-1x);\n --progress-bar-height: var(--kds-dimension-component-height-0-25x);\n --progress-bar-distance: var(--kds-spacing-container-0-25x);\n\n display: flex;\n flex-direction: column;\n gap: var(--progress-bar-distance);\n height: var(--kds-dimension-component-height-4x);\n padding: var(--base-padding);\n}\n\n.progress-item {\n display: flex;\n align-items: center;\n width: 100%;\n height: 100%;\n\n /* add extra margin to prevent content jump when a progress bars appears/disappears */\n &.padded {\n margin-bottom: calc(\n var(--progress-bar-height) + var(--progress-bar-distance)\n );\n }\n}\n\n.progress {\n min-height: var(--progress-bar-height);\n}\n\n.item-info {\n display: flex;\n flex-grow: 1;\n gap: var(--kds-spacing-container-0-25x);\n overflow: hidden;\n\n & .item-name {\n display: flex;\n flex-grow: 1;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n justify-content: center;\n overflow: hidden;\n color: var(--kds-color-text-and-icon-neutral);\n\n & .title {\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n font: var(--kds-font-base-interactive-small);\n white-space: nowrap;\n }\n\n & .subtitle {\n font: var(--kds-font-base-subtext-small);\n }\n }\n}\n\n.item-action {\n display: flex;\n flex-shrink: 0;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n}\n</style>\n","<template>\n <div class=\"progress-list\" tabindex=\"0\">\n <slot />\n </div>\n</template>\n\n<style scoped>\n.progress-list {\n max-height: var(--kds-dimension-component-height-20x);\n overflow-y: auto;\n\n &:deep(.progress-wrapper):not(:first-child) {\n border-top: var(--kds-border-base-subtle);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport type { KdsBadgeVariant, KdsIconName } from \"@knime/kds-components\";\n\nimport {\n ProgressItem,\n type ProgressItemAction,\n type ProgressItemBadge,\n} from \"../../shared/ui\";\n\nimport type { DownloadItem } from \"./types\";\n\ntype Props = {\n item: DownloadItem;\n};\n\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [];\n download: [];\n}>();\n\nconst statusMapper = {\n IN_PROGRESS: [\"Zipping\", \"info\", \"reload\"],\n READY: [\"Ready\", \"success\", \"circle-info\"],\n CANCELLED: [\"Cancelled\", \"error\", \"circle-error\"],\n FAILED: [\"Failed\", \"error\", \"circle-error\"],\n} satisfies Record<\n DownloadItem[\"status\"],\n [string, KdsBadgeVariant, KdsIconName]\n>;\n\nconst badge = computed<ProgressItemBadge>(() => {\n const [text, variant, icon] = statusMapper[props.item.status];\n\n return {\n text,\n variant,\n icon,\n } satisfies ProgressItemBadge;\n});\n\nconst isCancellable = computed(() => props.item.status === \"IN_PROGRESS\");\nconst isReady = computed(() => props.item.status === \"READY\");\n\nconst actions = computed<Array<ProgressItemAction>>(() => {\n const actions: ProgressItemAction[] = [];\n\n if (isCancellable.value) {\n actions.push({\n id: \"cancel\",\n icon: \"x-close\",\n label: \"Cancel\",\n handler: () => emit(\"cancel\"),\n });\n }\n\n if (isReady.value) {\n actions.push({\n id: \"download\",\n icon: \"cloud-download\",\n label: \"Download again\",\n handler: () => emit(\"download\"),\n });\n }\n\n return actions;\n});\n</script>\n\n<template>\n <ProgressItem\n :id=\"item.downloadId\"\n icon=\"file\"\n :headline=\"item.name\"\n :badge=\"badge\"\n :actions=\"actions\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport type { KdsBadgeVariant, KdsIconName } from \"@knime/kds-components\";\n\nimport {\n ProgressItem,\n type ProgressItemAction,\n type ProgressItemBadge,\n} from \"../../shared/ui\";\n\nimport type { DownloadItem } from \"./types\";\n\ntype Props = {\n item: DownloadItem;\n};\n\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [];\n download: [];\n}>();\n\nconst statusMapper = {\n IN_PROGRESS: [\"Zipping\", \"info\", \"reload\"],\n READY: [\"Ready\", \"success\", \"circle-info\"],\n CANCELLED: [\"Cancelled\", \"error\", \"circle-error\"],\n FAILED: [\"Failed\", \"error\", \"circle-error\"],\n} satisfies Record<\n DownloadItem[\"status\"],\n [string, KdsBadgeVariant, KdsIconName]\n>;\n\nconst badge = computed<ProgressItemBadge>(() => {\n const [text, variant, icon] = statusMapper[props.item.status];\n\n return {\n text,\n variant,\n icon,\n } satisfies ProgressItemBadge;\n});\n\nconst isCancellable = computed(() => props.item.status === \"IN_PROGRESS\");\nconst isReady = computed(() => props.item.status === \"READY\");\n\nconst actions = computed<Array<ProgressItemAction>>(() => {\n const actions: ProgressItemAction[] = [];\n\n if (isCancellable.value) {\n actions.push({\n id: \"cancel\",\n icon: \"x-close\",\n label: \"Cancel\",\n handler: () => emit(\"cancel\"),\n });\n }\n\n if (isReady.value) {\n actions.push({\n id: \"download\",\n icon: \"cloud-download\",\n label: \"Download again\",\n handler: () => emit(\"download\"),\n });\n }\n\n return actions;\n});\n</script>\n\n<template>\n <ProgressItem\n :id=\"item.downloadId\"\n icon=\"file\"\n :headline=\"item.name\"\n :badge=\"badge\"\n :actions=\"actions\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, watch } from \"vue\";\n\nimport { CollapsiblePanel, ProgressList } from \"../../shared/ui\";\n\nimport DownloadProgressPanelItem from \"./DownloadProgressPanelItem.vue\";\nimport type { DownloadItem } from \"./types\";\n\ntype Props = {\n /**\n * List of current download items that should be displayed\n */\n items: DownloadItem[];\n};\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [item: DownloadItem];\n download: [item: DownloadItem];\n close: [];\n}>();\n\nconst expanded = defineModel<boolean>(\"expanded\", { default: true });\n\nconst totalItems = computed(() => props.items.length);\nconst totalDownloadedItems = computed(\n () => props.items.filter(({ status }) => status === \"READY\").length,\n);\n\nconst headline = computed(() => {\n const suffix = `file${totalItems.value > 1 ? \"s\" : \"\"}`;\n return `Downloaded ${totalDownloadedItems.value} of ${totalItems.value} ${suffix}`;\n});\n\nconst hasItemsInProgress = computed(() =>\n props.items.some(({ status }) => status === \"IN_PROGRESS\"),\n);\n\nwatch(\n () => props.items?.length,\n (newValue, oldValue) => {\n if (oldValue > 0 && !newValue) {\n emit(\"close\");\n }\n },\n);\n</script>\n\n<template>\n <CollapsiblePanel\n v-model=\"expanded\"\n :headline=\"headline\"\n class=\"download-panel\"\n :can-be-dismissed=\"!hasItemsInProgress\"\n @close=\"emit('close')\"\n >\n <div class=\"download-panel-content\">\n <ProgressList>\n <DownloadProgressPanelItem\n v-for=\"item in items\"\n :key=\"item.downloadId\"\n :item=\"item\"\n @cancel=\"emit('cancel', item)\"\n @download=\"emit('download', item)\"\n />\n </ProgressList>\n </div>\n </CollapsiblePanel>\n</template>\n\n<style scoped>\n.download-panel {\n width: var(--kds-dimension-component-width-20x);\n max-width: var(--kds-dimension-component-width-20x);\n}\n\n.download-panel-content {\n max-height: var(--kds-dimension-component-height-20x);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, watch } from \"vue\";\n\nimport { CollapsiblePanel, ProgressList } from \"../../shared/ui\";\n\nimport DownloadProgressPanelItem from \"./DownloadProgressPanelItem.vue\";\nimport type { DownloadItem } from \"./types\";\n\ntype Props = {\n /**\n * List of current download items that should be displayed\n */\n items: DownloadItem[];\n};\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [item: DownloadItem];\n download: [item: DownloadItem];\n close: [];\n}>();\n\nconst expanded = defineModel<boolean>(\"expanded\", { default: true });\n\nconst totalItems = computed(() => props.items.length);\nconst totalDownloadedItems = computed(\n () => props.items.filter(({ status }) => status === \"READY\").length,\n);\n\nconst headline = computed(() => {\n const suffix = `file${totalItems.value > 1 ? \"s\" : \"\"}`;\n return `Downloaded ${totalDownloadedItems.value} of ${totalItems.value} ${suffix}`;\n});\n\nconst hasItemsInProgress = computed(() =>\n props.items.some(({ status }) => status === \"IN_PROGRESS\"),\n);\n\nwatch(\n () => props.items?.length,\n (newValue, oldValue) => {\n if (oldValue > 0 && !newValue) {\n emit(\"close\");\n }\n },\n);\n</script>\n\n<template>\n <CollapsiblePanel\n v-model=\"expanded\"\n :headline=\"headline\"\n class=\"download-panel\"\n :can-be-dismissed=\"!hasItemsInProgress\"\n @close=\"emit('close')\"\n >\n <div class=\"download-panel-content\">\n <ProgressList>\n <DownloadProgressPanelItem\n v-for=\"item in items\"\n :key=\"item.downloadId\"\n :item=\"item\"\n @cancel=\"emit('cancel', item)\"\n @download=\"emit('download', item)\"\n />\n </ProgressList>\n </div>\n </CollapsiblePanel>\n</template>\n\n<style scoped>\n.download-panel {\n width: var(--kds-dimension-component-width-20x);\n max-width: var(--kds-dimension-component-width-20x);\n}\n\n.download-panel-content {\n max-height: var(--kds-dimension-component-height-20x);\n}\n</style>\n","import { computed, ref } from \"vue\";\nimport { FetchError, type FetchOptions } from \"ofetch\";\n\nimport { sleep } from \"@knime/utils\";\n\nimport { rfcErrors } from \"../../rfcErrors\";\nimport { createHttpClient } from \"../shared/httpClient\";\n\nimport type { DownloadItem as DownloadItemExternal } from \"./ui/types\";\n\nconst DEFAULT_MAX_RETRIES = null;\nconst DEFAULT_POLLING_INTERVAL = 2000;\n\ntype ArtifactStatusResponse = {\n downloadId?: string;\n status?: string;\n statusMessage?: string;\n lastUpdated?: string;\n downloadUrl?: string;\n};\n\n// Initial request can have either download url (if it can be downloaded immediately) or download id (to poll for the status)\ntype RequestDownloadResponse =\n | {\n downloadId: string;\n }\n | {\n downloadUrl: string;\n };\n\n// type guard\nconst hasDownloadUrl = (\n response: RequestDownloadResponse,\n): response is { downloadUrl: string } => {\n return \"downloadUrl\" in response;\n};\n\n/**\n * The version of the artifact to download.\n */\ntype ItemVersion = number | \"current-state\" | \"most-recent\";\n\n/**\n * Tracks the state of each concurrent download.\n */\ntype DownloadItem = DownloadItemExternal & {\n itemId: string;\n version?: ItemVersion;\n};\n\nexport type UseDownloadArtifactOptions = {\n /**\n * The maximum number of times to poll for the download URL. Default is `null` (no limit).\n */\n maxRetries?: number;\n /**\n * The interval in milliseconds between each poll. Default is `2000` ms.\n */\n pollingInterval?: number;\n /**\n * Options to customize the fetch client used internally to make http requests.\n * e.g: headers, baseUrl, etc\n */\n customFetchClientOptions?: FetchOptions;\n};\n\nconst isAbortError = (error: unknown) =>\n error instanceof DOMException && error.name === \"AbortError\";\n\n/**\n * This composable supports multiple concurrent downloads. Each time you call ```start```, the following behavior applies:\n *\n * 1. **Immediate-ready artifact**: If the first status check for the artifact returns `READY`, then there's no polling and no need\n * to add it to `downloadItems`. The download URL is opened in the same tab immediately.\n *\n * 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`\n * and repeatedly poll until it becomes `READY`, the polling times out, or the user aborts.\n *\n * **Usage**:\n * ```\n * const { start, cancel, downloadItems } = useDownloadArtifact({ ...options });\n *\n * // start a download\n * await start({ itemId: \"1234\", version: 1 });\n *\n * // if needed, cancel it:\n * cancel(\"1234\");\n *\n * // downloadItems (as a computed array) will reflect the current statuses.\n * ```\n */\nexport const useDownloadArtifact = (\n options: UseDownloadArtifactOptions = {},\n) => {\n const $ofetch = createHttpClient(options.customFetchClientOptions);\n\n const downloadItems = ref<Record<string, DownloadItem>>({});\n const abortControllers: Record<string, AbortController> = {};\n\n // Use fallback values if none are provided\n const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\n const pollingInterval = options.pollingInterval ?? DEFAULT_POLLING_INTERVAL;\n\n const cancel = (downloadId: string) => {\n const itemController = abortControllers[downloadId];\n if (itemController) {\n itemController.abort();\n delete abortControllers[downloadId];\n }\n };\n\n const removeItem = (downloadId: string) => {\n cancel(downloadId);\n delete downloadItems.value[downloadId];\n };\n\n const fetchDownloadStatus = ({\n downloadId,\n signal,\n }: {\n downloadId: string;\n signal?: AbortSignal;\n }) => {\n return $ofetch<ArtifactStatusResponse>(`/downloads/${downloadId}/status`, {\n method: \"GET\",\n signal,\n });\n };\n\n const openDownload = (downloadUrl: string) => {\n const a = document.createElement(\"a\");\n a.target = \"_blank\";\n a.href = downloadUrl;\n a.download = \"\";\n a.style.display = \"none\";\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n };\n\n /**\n * Makes a request to the download status endpoint for the given download item.\n * Returns a boolean that is true if a terminal state has been reached (download is ready,\n * download failed, or user aborted download)\n * @param downloadId the `downloadId`\n * @param signal the abort signal passed to the request to be able to abort it\n * @returns a boolean indicating whether a terminal state has been reached\n */\n const handlePollingResult = async ({\n downloadId,\n signal,\n }: {\n downloadId: string;\n signal: AbortSignal;\n }) => {\n try {\n const statusResponse = await fetchDownloadStatus({\n downloadId,\n signal,\n });\n\n if (statusResponse?.status === \"READY\" && statusResponse?.downloadUrl) {\n downloadItems.value[downloadId].status = \"READY\";\n downloadItems.value[downloadId].downloadUrl =\n statusResponse.downloadUrl;\n // open download in a new tab\n openDownload(statusResponse.downloadUrl);\n return true;\n } else if (statusResponse?.status === \"FAILED\") {\n let details = \"Download failed\";\n if (statusResponse?.statusMessage) {\n details += `: ${statusResponse.statusMessage}`;\n }\n throw new Error(details);\n }\n } catch (error: unknown) {\n consola.error(\"Error fetching status:\", error);\n // https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort\n const isAborted =\n isAbortError(error) ||\n (error instanceof FetchError && isAbortError(error.cause));\n\n if (!isAborted) {\n downloadItems.value[downloadId].status = \"FAILED\";\n downloadItems.value[downloadId].failureDetails =\n error instanceof Error ? error.message : (error as string);\n }\n return true;\n }\n return false;\n };\n\n const pollDownloadItem = async ({\n itemId,\n downloadId,\n version,\n name,\n }: {\n itemId: string;\n downloadId: string;\n version?: ItemVersion;\n name: string;\n }) => {\n const controller = new AbortController();\n abortControllers[downloadId] = controller;\n\n const signal = controller.signal;\n\n let itemName = `${name}`;\n if (version) {\n itemName += ` (${version})`;\n }\n\n downloadItems.value[downloadId] = {\n itemId,\n ...(version && { version }),\n downloadId,\n name: itemName,\n status: \"IN_PROGRESS\",\n };\n\n const abortListener = () => {\n controller.signal.removeEventListener(\"abort\", abortListener);\n if (!downloadItems.value[downloadId]) {\n return;\n }\n downloadItems.value[downloadId].status = \"CANCELLED\";\n downloadItems.value[downloadId].failureDetails =\n \"Download was cancelled manually\";\n };\n controller.signal.addEventListener(\"abort\", abortListener);\n\n let tries = 0;\n // eslint-disable-next-line no-unmodified-loop-condition\n while (maxRetries === null || tries < maxRetries) {\n tries++;\n await sleep(pollingInterval);\n\n const isTerminalState = await handlePollingResult({ downloadId, signal });\n\n if (isTerminalState) {\n break;\n }\n }\n\n delete abortControllers[downloadId];\n\n if (maxRetries !== null && tries >= maxRetries) {\n downloadItems.value[downloadId].status = \"FAILED\";\n downloadItems.value[downloadId].failureDetails = \"Download timed out\";\n }\n };\n\n const start = async ({\n itemId,\n version,\n name,\n }: {\n itemId: string;\n name: string;\n version?: ItemVersion;\n }) => {\n try {\n const response = await $ofetch<RequestDownloadResponse>(\n `/repository/${itemId}/artifact`,\n {\n method: \"GET\",\n query: { version },\n },\n );\n\n // if download is ready, open url immediately\n if (hasDownloadUrl(response)) {\n openDownload(response.downloadUrl);\n return;\n }\n\n // otherwise start polling download status\n await pollDownloadItem({\n itemId,\n version,\n downloadId: response.downloadId,\n name,\n });\n } catch (error: unknown) {\n // here only errors thrown by the initial requests are caught;\n // errors occurring while polling will be handled in pollDownloadItem\n throw rfcErrors.tryParse(error) ?? error;\n }\n };\n\n const totalItemsBeingZipped = computed(\n () =>\n Object.values(downloadItems.value).filter(\n ({ status }) => status === \"IN_PROGRESS\",\n ).length,\n );\n\n const resetState = () => {\n for (const [downloadId, controller] of Object.entries(abortControllers)) {\n controller?.abort();\n delete abortControllers[downloadId];\n }\n downloadItems.value = {};\n };\n\n const downloadItemsArray = computed(() => Object.values(downloadItems.value));\n\n return {\n /**\n * Initiates a download of the given itemId and version.\n */\n start,\n\n /**\n * Cancels the ongoing download for a given itemId (if any).\n * Sets the status in `downloadItems` to `CANCELLED`.\n */\n cancel,\n\n /**\n * Removes item from `downloadItems`.\n */\n removeItem,\n\n /**\n * A computed array of all current items that are being zipped and prepared for download.\n */\n downloadItems: downloadItemsArray,\n\n /**\n * The total number of zipping items in progress.\n */\n totalItemsBeingZipped,\n\n /**\n * Resets the state of the composable, clearing all downloads and controllers.\n */\n resetState,\n\n /**\n * Opens a download from the given url\n */\n openDownload,\n };\n};\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport type { KdsBadgeVariant, KdsIconName } from \"@knime/kds-components\";\nimport {\n formatFileSize,\n getFileExtension,\n knimeFileFormats,\n} from \"@knime/utils\";\n\nimport {\n ProgressItem,\n type ProgressItemAction,\n type ProgressItemBadge,\n} from \"../shared/ui\";\n\nimport type { UploadItem, UploadItemStatus } from \"./types\";\n\ntype Props = {\n item: UploadItem;\n};\n\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [];\n viewDetails: [];\n}>();\n\nconst subtitle = computed(() => {\n if (props.item.type === \"file\") {\n const progressed = formatFileSize(\n (props.item.size / 100) * (props.item.progress ?? 0),\n );\n\n const total = formatFileSize(props.item.size);\n return `${progressed} of ${total}`;\n }\n\n if (props.item.type === \"directory\") {\n const total = props.item.totalEntries;\n const progressed = props.item.uploadedEntries;\n\n return `${progressed} of ${total} file${total > 1 ? \"s\" : \"\"}`;\n }\n\n return \"\";\n});\n\nconst progressPercentageValue = computed(() => {\n if (props.item.type === \"file\") {\n return props.item.progress;\n }\n\n if (props.item.type === \"directory\") {\n // The KdsProgressBar only accepts percentages, but for directory items\n // the consumer supplies different values so we must derive the percentage\n const progressPercentage =\n (props.item.uploadedEntries * 100) / props.item.totalEntries;\n\n return progressPercentage;\n }\n\n return undefined;\n});\n\nconst statusMapper = {\n inprogress: [\"Uploading\", \"info\", \"reload\"],\n failed: [\"Failed\", \"error\", \"circle-error\"],\n complete: [\"Uploaded\", \"success\", \"circle-success\"],\n cancelled: [\"Cancelled\", \"error\", \"circle-error\"],\n processing: [\"Processing\", \"info\", \"reload\"],\n \"partial-success\": [\"Partial success\", \"warning\", \"warning\"],\n} satisfies Record<UploadItemStatus, [string, KdsBadgeVariant, KdsIconName]>;\n\nconst itemIcon = computed<KdsIconName>(() => {\n if (props.item.type === \"directory\") {\n return \"folder\";\n }\n\n const extension = getFileExtension(props.item.name);\n return extension === knimeFileFormats.KNWF.extension ? \"workflow\" : \"file\";\n});\n\nconst badge = computed<ProgressItemBadge | undefined>(() => {\n if (!props.item.status) {\n return undefined;\n }\n\n const [text, variant, icon] = statusMapper[props.item.status];\n\n return { text, variant, icon } satisfies ProgressItemBadge;\n});\n\nconst isCancellable = computed(() => {\n if (\n props.item.actions === undefined ||\n props.item.actions.cancellable === undefined\n ) {\n return true;\n }\n\n return props.item.actions.cancellable;\n});\n\nconst shouldShowCancelAction = computed(\n () => props.item.status === \"inprogress\" && isCancellable.value,\n);\n\nconst isInspectable = computed(() => {\n if (\n props.item.actions === undefined ||\n props.item.actions.inspectable === undefined\n ) {\n return false;\n }\n\n return props.item.actions.inspectable;\n});\n\nconst shouldShowViewDetailsAction = computed(\n () => props.item.status === \"partial-success\" && isInspectable.value,\n);\n\nconst actions = computed<Array<ProgressItemAction>>(() => {\n const result: ProgressItemAction[] = [];\n\n if (shouldShowCancelAction.value) {\n result.push({\n id: \"cancel\",\n icon: \"x-close\",\n label: \"Cancel\",\n handler: () => emit(\"cancel\"),\n });\n }\n\n if (shouldShowViewDetailsAction.value) {\n result.push({\n id: \"view-details\",\n icon: \"circle-info\",\n label: \"View details\",\n handler: () => emit(\"viewDetails\"),\n });\n }\n\n return result;\n});\n</script>\n\n<template>\n <ProgressItem\n :id=\"item.id\"\n :headline=\"item.name\"\n :subtitle=\"subtitle\"\n :icon=\"itemIcon\"\n :progress=\"progressPercentageValue\"\n :badge=\"badge\"\n :actions=\"actions\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport type { KdsBadgeVariant, KdsIconName } from \"@knime/kds-components\";\nimport {\n formatFileSize,\n getFileExtension,\n knimeFileFormats,\n} from \"@knime/utils\";\n\nimport {\n ProgressItem,\n type ProgressItemAction,\n type ProgressItemBadge,\n} from \"../shared/ui\";\n\nimport type { UploadItem, UploadItemStatus } from \"./types\";\n\ntype Props = {\n item: UploadItem;\n};\n\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [];\n viewDetails: [];\n}>();\n\nconst subtitle = computed(() => {\n if (props.item.type === \"file\") {\n const progressed = formatFileSize(\n (props.item.size / 100) * (props.item.progress ?? 0),\n );\n\n const total = formatFileSize(props.item.size);\n return `${progressed} of ${total}`;\n }\n\n if (props.item.type === \"directory\") {\n const total = props.item.totalEntries;\n const progressed = props.item.uploadedEntries;\n\n return `${progressed} of ${total} file${total > 1 ? \"s\" : \"\"}`;\n }\n\n return \"\";\n});\n\nconst progressPercentageValue = computed(() => {\n if (props.item.type === \"file\") {\n return props.item.progress;\n }\n\n if (props.item.type === \"directory\") {\n // The KdsProgressBar only accepts percentages, but for directory items\n // the consumer supplies different values so we must derive the percentage\n const progressPercentage =\n (props.item.uploadedEntries * 100) / props.item.totalEntries;\n\n return progressPercentage;\n }\n\n return undefined;\n});\n\nconst statusMapper = {\n inprogress: [\"Uploading\", \"info\", \"reload\"],\n failed: [\"Failed\", \"error\", \"circle-error\"],\n complete: [\"Uploaded\", \"success\", \"circle-success\"],\n cancelled: [\"Cancelled\", \"error\", \"circle-error\"],\n processing: [\"Processing\", \"info\", \"reload\"],\n \"partial-success\": [\"Partial success\", \"warning\", \"warning\"],\n} satisfies Record<UploadItemStatus, [string, KdsBadgeVariant, KdsIconName]>;\n\nconst itemIcon = computed<KdsIconName>(() => {\n if (props.item.type === \"directory\") {\n return \"folder\";\n }\n\n const extension = getFileExtension(props.item.name);\n return extension === knimeFileFormats.KNWF.extension ? \"workflow\" : \"file\";\n});\n\nconst badge = computed<ProgressItemBadge | undefined>(() => {\n if (!props.item.status) {\n return undefined;\n }\n\n const [text, variant, icon] = statusMapper[props.item.status];\n\n return { text, variant, icon } satisfies ProgressItemBadge;\n});\n\nconst isCancellable = computed(() => {\n if (\n props.item.actions === undefined ||\n props.item.actions.cancellable === undefined\n ) {\n return true;\n }\n\n return props.item.actions.cancellable;\n});\n\nconst shouldShowCancelAction = computed(\n () => props.item.status === \"inprogress\" && isCancellable.value,\n);\n\nconst isInspectable = computed(() => {\n if (\n props.item.actions === undefined ||\n props.item.actions.inspectable === undefined\n ) {\n return false;\n }\n\n return props.item.actions.inspectable;\n});\n\nconst shouldShowViewDetailsAction = computed(\n () => props.item.status === \"partial-success\" && isInspectable.value,\n);\n\nconst actions = computed<Array<ProgressItemAction>>(() => {\n const result: ProgressItemAction[] = [];\n\n if (shouldShowCancelAction.value) {\n result.push({\n id: \"cancel\",\n icon: \"x-close\",\n label: \"Cancel\",\n handler: () => emit(\"cancel\"),\n });\n }\n\n if (shouldShowViewDetailsAction.value) {\n result.push({\n id: \"view-details\",\n icon: \"circle-info\",\n label: \"View details\",\n handler: () => emit(\"viewDetails\"),\n });\n }\n\n return result;\n});\n</script>\n\n<template>\n <ProgressItem\n :id=\"item.id\"\n :headline=\"item.name\"\n :subtitle=\"subtitle\"\n :icon=\"itemIcon\"\n :progress=\"progressPercentageValue\"\n :badge=\"badge\"\n :actions=\"actions\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport { KdsButton, KdsLoadingSkeleton } from \"@knime/kds-components\";\n\nimport { CollapsiblePanel, ProgressList } from \"../shared/ui\";\n\nimport UploadProgressPanelItem from \"./UploadProgressPanelItem.vue\";\nimport type { UploadItem, UploadProgressPanelProps } from \"./types\";\n\nconst {\n items,\n placeholderItems = 0,\n canRetry = false,\n} = defineProps<UploadProgressPanelProps>();\n\nconst emit = defineEmits<{\n cancel: [item: UploadItem];\n viewDetails: [item: UploadItem];\n retry: [];\n close: [];\n}>();\n\nconst expanded = defineModel<boolean>(\"expanded\", { default: true });\n\nconst totalItems = computed(() => items.length);\nconst uploadedItems = computed(\n () => items.filter(({ status }) => status === \"complete\").length,\n);\n\nconst headline = computed(() => {\n const suffix = `item${totalItems.value > 1 ? \"s\" : \"\"}`;\n return `Uploaded ${uploadedItems.value} of ${totalItems.value} ${suffix}`;\n});\n\nconst hasSomeItemInProgress = computed(\n () =>\n items.some(\n ({ status }) => status === \"inprogress\" || status === \"processing\",\n ) || placeholderItems > 0,\n);\n</script>\n\n<template>\n <CollapsiblePanel\n v-model=\"expanded\"\n :headline=\"headline\"\n class=\"upload-panel\"\n :can-be-dismissed=\"!hasSomeItemInProgress\"\n @close=\"emit('close')\"\n >\n <div class=\"upload-panel-content\">\n <ProgressList>\n <UploadProgressPanelItem\n v-for=\"item in items\"\n :key=\"item.id\"\n :item=\"item\"\n @cancel=\"emit('cancel', item)\"\n @view-details=\"emit('viewDetails', item)\"\n />\n </ProgressList>\n\n <template v-if=\"placeholderItems > 0\">\n <div\n v-for=\"skeleton in placeholderItems\"\n :key=\"skeleton\"\n class=\"skeleton-container\"\n >\n <KdsLoadingSkeleton variant=\"icon-large\" />\n <div class=\"skeleton-text-content\">\n <KdsLoadingSkeleton variant=\"label\" />\n <KdsLoadingSkeleton variant=\"text\" />\n </div>\n </div>\n </template>\n\n <div v-if=\"canRetry && !hasSomeItemInProgress\" class=\"retry-bar\">\n <KdsButton\n size=\"small\"\n variant=\"outlined\"\n label=\"Retry failed items\"\n leading-icon=\"reload\"\n @click=\"emit('retry')\"\n />\n </div>\n </div>\n </CollapsiblePanel>\n</template>\n\n<style scoped>\n.upload-panel {\n width: var(--kds-dimension-component-width-20x);\n max-width: var(--kds-dimension-component-width-20x);\n}\n\n.upload-panel-content {\n display: flex;\n flex-direction: column;\n}\n\n.retry-bar {\n display: flex;\n justify-content: flex-end;\n padding: var(--kds-spacing-container-0-5x) var(--kds-spacing-container-0-75x);\n border-top: var(--kds-border-base-subtle);\n}\n\n.skeleton-container {\n display: grid;\n grid-template-columns: min-content 1fr;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n min-height: var(--kds-dimension-component-height-4x);\n padding: 0 var(--kds-spacing-container-1x);\n border-top: var(--kds-border-base-subtle);\n\n & .skeleton-text-content {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport { KdsButton, KdsLoadingSkeleton } from \"@knime/kds-components\";\n\nimport { CollapsiblePanel, ProgressList } from \"../shared/ui\";\n\nimport UploadProgressPanelItem from \"./UploadProgressPanelItem.vue\";\nimport type { UploadItem, UploadProgressPanelProps } from \"./types\";\n\nconst {\n items,\n placeholderItems = 0,\n canRetry = false,\n} = defineProps<UploadProgressPanelProps>();\n\nconst emit = defineEmits<{\n cancel: [item: UploadItem];\n viewDetails: [item: UploadItem];\n retry: [];\n close: [];\n}>();\n\nconst expanded = defineModel<boolean>(\"expanded\", { default: true });\n\nconst totalItems = computed(() => items.length);\nconst uploadedItems = computed(\n () => items.filter(({ status }) => status === \"complete\").length,\n);\n\nconst headline = computed(() => {\n const suffix = `item${totalItems.value > 1 ? \"s\" : \"\"}`;\n return `Uploaded ${uploadedItems.value} of ${totalItems.value} ${suffix}`;\n});\n\nconst hasSomeItemInProgress = computed(\n () =>\n items.some(\n ({ status }) => status === \"inprogress\" || status === \"processing\",\n ) || placeholderItems > 0,\n);\n</script>\n\n<template>\n <CollapsiblePanel\n v-model=\"expanded\"\n :headline=\"headline\"\n class=\"upload-panel\"\n :can-be-dismissed=\"!hasSomeItemInProgress\"\n @close=\"emit('close')\"\n >\n <div class=\"upload-panel-content\">\n <ProgressList>\n <UploadProgressPanelItem\n v-for=\"item in items\"\n :key=\"item.id\"\n :item=\"item\"\n @cancel=\"emit('cancel', item)\"\n @view-details=\"emit('viewDetails', item)\"\n />\n </ProgressList>\n\n <template v-if=\"placeholderItems > 0\">\n <div\n v-for=\"skeleton in placeholderItems\"\n :key=\"skeleton\"\n class=\"skeleton-container\"\n >\n <KdsLoadingSkeleton variant=\"icon-large\" />\n <div class=\"skeleton-text-content\">\n <KdsLoadingSkeleton variant=\"label\" />\n <KdsLoadingSkeleton variant=\"text\" />\n </div>\n </div>\n </template>\n\n <div v-if=\"canRetry && !hasSomeItemInProgress\" class=\"retry-bar\">\n <KdsButton\n size=\"small\"\n variant=\"outlined\"\n label=\"Retry failed items\"\n leading-icon=\"reload\"\n @click=\"emit('retry')\"\n />\n </div>\n </div>\n </CollapsiblePanel>\n</template>\n\n<style scoped>\n.upload-panel {\n width: var(--kds-dimension-component-width-20x);\n max-width: var(--kds-dimension-component-width-20x);\n}\n\n.upload-panel-content {\n display: flex;\n flex-direction: column;\n}\n\n.retry-bar {\n display: flex;\n justify-content: flex-end;\n padding: var(--kds-spacing-container-0-5x) var(--kds-spacing-container-0-75x);\n border-top: var(--kds-border-base-subtle);\n}\n\n.skeleton-container {\n display: grid;\n grid-template-columns: min-content 1fr;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n min-height: var(--kds-dimension-component-height-4x);\n padding: 0 var(--kds-spacing-container-1x);\n border-top: var(--kds-border-base-subtle);\n\n & .skeleton-text-content {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n }\n}\n</style>\n","import { computed, ref } from \"vue\";\n\nimport { type ChunkUploadManagerConfig, createUploadManager } from \"../core\";\n\nimport type { FileUploadItem, UploadItem } from \"./types\";\n\ntype OnCompletePayload = Parameters<\n NonNullable<ChunkUploadManagerConfig[\"onFileUploadComplete\"]>\n>[0] & { parentId: string; name: string };\n\ntype OnFailedPayload = Parameters<\n NonNullable<ChunkUploadManagerConfig[\"onFileUploadFailed\"]>\n>[0] & { parentId: string };\n\ntype UseUploadManagerOptions = Omit<\n ChunkUploadManagerConfig,\n \"onProgress\" | \"onFileUploadComplete\" | \"onFileUploadFailed\"\n> & {\n onFileUploadComplete?: (payload: OnCompletePayload) => void;\n onFileUploadFailed?: (payload: OnFailedPayload) => void;\n /**\n * Determines the status to assign a file once its raw upload finishes,\n * before calling `onFileUploadComplete`. This helps for files that need further\n * server-side processing\n *\n * Defaults to `complete`\n */\n resolveCompleteStatus?: (\n payload: OnCompletePayload,\n ) => \"complete\" | \"processing\";\n};\n\n/**\n * This composable wraps the `uploadManager` utility\n * in order to provide reactive state which will work tightly with the `UploadProgressPanel` component\n *\n * The reactive state returned (`uploadItems`) will update whenever the progress\n * or status of each item being uploaded changes\n */\nexport const useUploadManager = (options: UseUploadManagerOptions) => {\n const { resolveCompleteStatus, ...restOptions } = options;\n const uploadState = ref<Record<string, UploadItem>>({});\n\n const { uploadFiles, cancel, setFailed } = createUploadManager({\n ...restOptions,\n\n onFileUploadComplete: (params) => {\n const { parentId, name } = uploadState.value[params.uploadId];\n const payload = { ...params, parentId, name };\n uploadState.value[params.uploadId].status =\n resolveCompleteStatus?.(payload) ?? \"complete\";\n options.onFileUploadComplete?.(payload);\n },\n\n onFileUploadFailed: ({ uploadId, error }) => {\n const { parentId } = uploadState.value[uploadId];\n uploadState.value[uploadId] = {\n ...uploadState.value[uploadId],\n status: \"failed\",\n failureDetails: error.message,\n };\n options.onFileUploadFailed?.({ uploadId, error, parentId });\n },\n\n onProgress: (uploadId, progress) => {\n if (uploadState.value[uploadId].status === \"inprogress\") {\n (uploadState.value[uploadId] as FileUploadItem).progress = progress;\n }\n },\n });\n\n const start = async (\n parentId: string,\n uploadPayload: Array<{ uploadId: string; file: File }>,\n ) => {\n try {\n uploadPayload.forEach(({ uploadId, file }) => {\n uploadState.value[uploadId] = {\n id: uploadId,\n name: file.name,\n size: file.size,\n progress: 0,\n type: \"file\",\n status: \"inprogress\",\n parentId,\n };\n });\n\n return await uploadFiles(uploadPayload);\n } catch (error) {\n consola.error(\"Error uploading files\", error);\n return [];\n }\n };\n\n const canCancel = (uploadId: string) =>\n uploadState.value[uploadId]?.status === \"inprogress\" ||\n uploadState.value[uploadId]?.status === \"failed\";\n\n const removeItem = (uploadId: string) => {\n if (canCancel(uploadId)) {\n cancel(uploadId);\n }\n\n delete uploadState.value[uploadId];\n };\n\n const resetState = () => {\n uploadState.value = {};\n };\n\n const uploadItems = computed(() => Object.values(uploadState.value));\n\n const hasPendingUploads = computed(() =>\n uploadItems.value.some(({ status }) => status === \"inprogress\"),\n );\n\n const totalPendingUploads = computed(\n () =>\n uploadItems.value.filter(({ status }) => status === \"inprogress\").length,\n );\n\n return {\n uploadState,\n hasPendingUploads,\n totalPendingUploads,\n start,\n cancel: (uploadId: string) => {\n cancel(uploadId);\n uploadState.value[uploadId].status = \"cancelled\";\n },\n canCancel,\n removeItem,\n resetState,\n setFailed: (uploadId: string, error: Error) => {\n uploadState.value[uploadId] = {\n ...uploadState.value[uploadId],\n status: \"failed\",\n failureDetails: error.message,\n };\n setFailed(uploadId);\n const { parentId } = uploadState.value[uploadId];\n options.onFileUploadFailed?.({ parentId, uploadId, error });\n },\n };\n};\n","import { computed, ref } from \"vue\";\nimport type { FetchError, FetchOptions } from \"ofetch\";\n\nimport {\n formatFileSize,\n getFileExtension,\n knimeFileFormats,\n promise,\n} from \"@knime/utils\";\n\nimport { rfcErrors } from \"../../rfcErrors\";\nimport { createUploadClient, createUploadStatusPoller } from \"../core\";\n\nimport type { UploadItem } from \"./types\";\nimport { useUploadManager } from \"./useUploadManager\";\n\nconst DEFAULT_MAX_UPLOAD_QUEUE_SIZE = 10;\n// eslint-disable-next-line no-magic-numbers\nconst DEFAULT_UPLOAD_SIZE_LIMIT_BYTES = 5 * 1024 * 1024 * 1024;\n\nconst DEFAULT_RETRY_DELAY_MS = 50;\n\n// Catalog & artifacts services currently have a limit for 5 GiB for\n// upload and copy/move operations but it is not communicated to clients.\n// Repeat the constant here to be able to still make a pre-check. See NXT-4510.\nconst getUploadSizeLimitBytes = () => DEFAULT_UPLOAD_SIZE_LIMIT_BYTES;\n\n/**\n * Renames any File whose name repeats an earlier one in the list, appending\n * a \" (n)\" suffix (before the extension, if any) - including a name produced\n * by this same disambiguation, so a literal \"name (1).ext\" already in the\n * list can't collide with a generated one\n */\nconst disambiguateFiles = (files: File[]): File[] => {\n const usedNames = new Set<string>();\n\n return files.map((file) => {\n if (!usedNames.has(file.name)) {\n usedNames.add(file.name);\n return file;\n }\n\n const extension = getFileExtension(file.name);\n const base = extension\n ? file.name.slice(0, -(extension.length + 1))\n : file.name;\n\n let counter = 1;\n let candidate = extension\n ? `${base} (${counter}).${extension}`\n : `${base} (${counter})`;\n\n while (usedNames.has(candidate)) {\n counter += 1;\n candidate = extension\n ? `${base} (${counter}).${extension}`\n : `${base} (${counter})`;\n }\n\n usedNames.add(candidate);\n return new File([file], candidate, {\n type: file.type,\n lastModified: file.lastModified,\n });\n });\n};\n\ntype UseFileUploadOptions = {\n /**\n * Max number of concurrent uploads allowed in the upload queue.\n * When an upload is started with a number of files that exceed the pending\n * uploads then only files up-to the max queue size will be processed and the rest\n * will be ignored. This will also call the `onUploadQueueSizeExceeded` callback\n */\n maxUploadQueueSize?: number;\n /**\n * Callback that triggers every time any file in the queue completes its upload\n * @returns\n */\n onFileUploadComplete?: (payload: {\n uploadId: string;\n filePartIds: Record<number, string>;\n parentId: string;\n name: string;\n }) => void;\n /**\n * Callback that triggers every time any file in the queue fails its upload\n * @returns\n */\n onFileUploadFailed?: (payload: {\n uploadId: string;\n error: Error;\n parentId: string;\n }) => void;\n /**\n * Callback that triggers when a upload batch is started (via the `start` function) but the amount\n * of items to upload exceed the max configured size of the queue.\n * @returns\n */\n onUploadQueueSizeExceeded?: (maxQueueSize: number) => void;\n /**\n * Options to customize the fetch client used internally to make http requests.\n * e.g: headers, baseUrl, etc\n */\n customFetchClientOptions?: FetchOptions;\n\n /**\n * Whether the manifest endpoint is flat (`/manifest`) rather than nested\n * under a repository item (`/repository/{parentId}/manifest`). Used for\n * targets whose upload endpoint isn't scoped by a repository parent,\n * e.g. environment extension DB driver uploads.\n */\n shouldUseFlatManifestPath?: boolean;\n\n isFileWithProcessing?: (fileName: string) => boolean;\n};\n\n/**\n * This composable handles uploads of data to a hub repository item (space or workflow group).\n * It is a multi-step process which is described as follows:\n *\n * Step 1:\n * First we need to prepare an upload. The items will be uploaded to a `parent` container\n * identified by its id. (When `shouldUseFlatManifestPath` is set, the manifest endpoint isn't\n * scoped by a parent, so the `parentId` passed to `start` is unused for that request.)\n * Then, for each file we want to upload, we must provide paths relative\n * to the parent that identify each file. Example:\n * ```\n * {\n * \"items\": {\n * \"my-file.txt\": { itemContentType: \"text/plain\", itemContentSize: 5000 },\n * \"folder/my-file.zip\": { itemContentType: \"application/zip\", itemContentSize: 5000 },\n * }\n * }\n * ```\n * This describes 2 items, one placed in the parent's root and another inside a folder.\n * The response for this would look like:\n * ```\n * {\n * \"items\": {\n * \"my-file.txt\": { uploadId: \"id1\" },\n * \"folder/my-file.zip\": { uploadId: \"id2\" },\n * }\n * }\n * ```\n * Now, each corresponding `uploadId` uniquely identifies the upload process for\n * their respective files.\n *\n * Step 2:\n * Next, we need to start uploading the file parts. Technically, you _can_ just upload\n * each file as one big file \"part\", but practically that is not recommended because a hiccup\n * in the connection means you would have to start the upload from scratch; instead, we split\n * the files in smaller parts.\n * The upload manager helper will take care of splitting the file. However, the responsibility\n * of this composable is to supply a function to resolve the upload URL. This upload URL is obtained\n * by POSTing to an endpoint with the `uploadId` and the `partNumber` and this will return\n * the data required to make an upload to a presigned URL; this will be called for each file part.\n *\n * Step 3:\n * Completion:\n * Once the upload of a single file is complete, the upload manager will trigger the `onFileUploadComplete`\n * callback, which then will supply the ids of all the different parts that got uploaded for a given `uploadId`\n * Then we need to \"complete\" the upload by sending a request which provides the `uploadId` and a list of\n * the ids of all the parts. Note that some backends don't return the ids of the file parts after a successful\n * upload of a part, in this case empty strings or null can be used instead of the part id.\n *\n * Cancellation:\n * Alternative to completion, you can also cancel an upload. For this, the composable makes a DELETE request\n * providing the corresponding `uploadId` which will effectively cancel it.\n *\n * For a usage example, refer to the Docs page in storybook.\n */\nexport const useFileUpload = (options: UseFileUploadOptions = {}) => {\n const uploadClient = createUploadClient({\n customFetchClientOptions: options.customFetchClientOptions,\n shouldUseFlatManifestPath: options.shouldUseFlatManifestPath,\n });\n\n let useUploadManagerResult: ReturnType<typeof useUploadManager> | null = null;\n\n const getItemFromQueue = (uploadId: string) => {\n const { uploadState } = useUploadManagerResult!;\n\n if (!uploadState.value[uploadId]) {\n consola.warn(\n `Cannot get item in queue. Upload id \"${uploadId}\" doesn't exist`,\n );\n return undefined;\n }\n\n return uploadState.value[uploadId];\n };\n\n const updateItemInQueue = (uploadId: string, data: Partial<UploadItem>) => {\n const { uploadState } = useUploadManagerResult!;\n\n if (!uploadState.value[uploadId]) {\n consola.warn(\n `Cannot update item in queue. Upload id \"${uploadId}\" doesn't exist`,\n );\n return;\n }\n\n uploadState.value = {\n ...uploadState.value,\n [uploadId]: {\n ...uploadState.value[uploadId],\n ...data,\n } as UploadItem,\n };\n };\n\n const statusPoller = createUploadStatusPoller(uploadClient, {\n onComplete: (uploadId) =>\n updateItemInQueue(uploadId, { status: \"complete\" }),\n\n onFail: (uploadId, statusMessage) =>\n updateItemInQueue(uploadId, {\n status: \"failed\",\n failureDetails: statusMessage,\n }),\n });\n\n useUploadManagerResult = useUploadManager({\n resolveFilePartUploadURL: uploadClient.resolveFilePartUploadURL,\n\n resolveCompleteStatus: ({ name }) =>\n options.isFileWithProcessing?.(name) ? \"processing\" : \"complete\",\n\n onFileUploadComplete: ({ uploadId, filePartIds, name, parentId }) => {\n promise\n .retryPromise({\n fn: () => uploadClient.completeUpload(uploadId, filePartIds),\n })\n .then(() => {\n if (options.isFileWithProcessing?.(name)) {\n statusPoller.track(uploadId);\n }\n\n options.onFileUploadComplete?.({\n uploadId,\n filePartIds,\n name,\n parentId,\n });\n })\n .catch((error) => {\n consola.error(\"Error attempting to complete upload\", { error });\n useUploadManagerResult!.setFailed(uploadId, error);\n });\n },\n\n onFileUploadFailed: ({ uploadId, error, parentId }) => {\n options?.onFileUploadFailed?.({ uploadId, error, parentId });\n },\n });\n\n const getEnqueueableFiles = (files: File[]): File[] => {\n const { totalPendingUploads } = useUploadManagerResult;\n\n const { maxUploadQueueSize = DEFAULT_MAX_UPLOAD_QUEUE_SIZE } = options;\n\n if (files.length + totalPendingUploads.value > maxUploadQueueSize) {\n options.onUploadQueueSizeExceeded?.(maxUploadQueueSize);\n }\n\n // only keep as many files as the queue allows\n const effectiveQueueSize = Math.max(\n 0,\n maxUploadQueueSize - totalPendingUploads.value,\n );\n return files.slice(0, effectiveQueueSize);\n };\n\n const { uploadState, ...uploadManagerResultRest } = useUploadManagerResult;\n\n const prepareQueueSize = ref(0);\n\n const uploadQueue = computed<UploadItem[]>(() =>\n Object.values(uploadState.value),\n );\n\n return {\n ...uploadManagerResultRest,\n\n uploadQueue,\n getItemFromQueue,\n updateItemInQueue,\n\n isPreparingUpload: computed(() => prepareQueueSize.value > 0),\n totalFilesBeingPrepared: computed(() => prepareQueueSize.value),\n\n start: async (parentId: string, files: File[]): Promise<string[]> => {\n const enqueueableFiles = getEnqueueableFiles(files);\n const uploadSizeLimitBytes = getUploadSizeLimitBytes();\n const oversizedFiles = enqueueableFiles.filter(\n (file) => file.size > uploadSizeLimitBytes,\n );\n\n if (oversizedFiles.length > 0) {\n throw new rfcErrors.RFCError({\n title: \"Upload size limit exceeded\",\n status: 413,\n details: oversizedFiles.map(\n (file) =>\n `${file.name} (${formatFileSize(file.size)}) exceeds the upload limit of ${formatFileSize(uploadSizeLimitBytes)}.`,\n ),\n });\n }\n\n try {\n if (enqueueableFiles.length === 0) {\n return [];\n }\n\n prepareQueueSize.value += enqueueableFiles.length;\n\n const disambiguatedFiles = disambiguateFiles(enqueueableFiles);\n\n const items = disambiguatedFiles.map((file) => ({\n // strip extension for workflows. otherwise, the extension gets baked into the name\n // of the uploaded file and the backend appends an extra extension.\n // unnecessary for other file types\n path: knimeFileFormats.KNWF.getNameOrDefault(file, file.name),\n file,\n }));\n\n const uploadPayload = await promise.retryPromise({\n fn: () => uploadClient.prepareUpload(parentId, items),\n excludeError: (error: FetchError) =>\n // eslint-disable-next-line no-magic-numbers\n Boolean(error.statusCode && error.statusCode < 500),\n retryDelayMS: DEFAULT_RETRY_DELAY_MS,\n });\n\n const uploadIdByFile = new Map(\n uploadPayload.map((upload) => [upload.file, upload.uploadId]),\n );\n const uploadIds = disambiguatedFiles.map((file) =>\n uploadIdByFile.get(file)!,\n );\n\n // accept floating promise, errors are handled inside the function\n useUploadManagerResult.start(parentId, uploadPayload);\n\n return uploadIds;\n } catch (error) {\n throw rfcErrors.tryParse(error) ?? error;\n } finally {\n // errors can only be thrown in the prepareUpload call, useUploadManagerResult.start does its own error handling\n prepareQueueSize.value -= enqueueableFiles.length;\n }\n },\n\n cancel: (uploadId: string) => {\n useUploadManagerResult.cancel(uploadId);\n uploadClient.cancelUpload(uploadId).catch((error: unknown) => {\n consola.error(\"There was a problem cancelling the upload\", { error });\n });\n },\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"dataTransfer.js","names":[],"sources":["../src/dataTransfer/shared/ui/CollapsiblePanel/useIconFlipper.ts","../src/dataTransfer/shared/ui/CollapsiblePanel/CollapsiblePanel.vue","../src/dataTransfer/shared/ui/CollapsiblePanel/CollapsiblePanel.vue","../src/dataTransfer/shared/ui/ProgressItem/ProgressItem.vue","../src/dataTransfer/shared/ui/ProgressItem/ProgressItem.vue","../src/dataTransfer/shared/ui/ProgressList/ProgressList.vue","../src/dataTransfer/download/ui/DownloadProgressPanelItem.vue","../src/dataTransfer/download/ui/DownloadProgressPanelItem.vue","../src/dataTransfer/download/ui/DownloadProgressPanel.vue","../src/dataTransfer/download/ui/DownloadProgressPanel.vue","../src/dataTransfer/download/useDownloadArtifact.ts","../src/dataTransfer/upload/UploadProgressPanelItem.vue","../src/dataTransfer/upload/UploadProgressPanelItem.vue","../src/dataTransfer/upload/UploadProgressPanel.vue","../src/dataTransfer/upload/UploadProgressPanel.vue","../src/dataTransfer/upload/useUploadManager.ts","../src/dataTransfer/upload/useFileUpload.ts"],"sourcesContent":["import { type CSSProperties, type ComputedRef, computed } from \"vue\";\n\ntype UseIconFlipperOptions = {\n active: ComputedRef<boolean>;\n initialRotation?: `${string}deg`;\n};\n\nexport const useIconFlipper = (options: UseIconFlipperOptions) => {\n const iconFlipStyles = computed<CSSProperties>(() => {\n const declaration: Partial<CSSProperties> = {\n transition: \"transform 0.2s linear\",\n transform: \"scaleY(1)\",\n };\n\n if (options.initialRotation) {\n declaration.rotate = `${options.initialRotation}`;\n }\n\n const transform = options.active.value ? \"scaleY(-1)\" : undefined;\n\n return { ...declaration, transform };\n });\n\n return { iconFlipStyles };\n};\n","<script setup lang=\"ts\">\nimport { computed, useId } from \"vue\";\n\nimport { KdsButton } from \"@knime/kds-components\";\n\nimport type { CollapsiblePanelProps } from \"./types\";\nimport { useIconFlipper } from \"./useIconFlipper\";\n\nconst { headline, canBeDismissed = false } =\n defineProps<CollapsiblePanelProps>();\n\nconst emit = defineEmits<{\n close: [];\n}>();\n\nconst modelValue = defineModel<boolean>({ default: true });\n\nconst { iconFlipStyles } = useIconFlipper({\n active: computed(() => modelValue.value),\n initialRotation: \"180deg\",\n});\nconst panelContentId = useId();\n</script>\n\n<template>\n <div class=\"collapsible-panel\" :class=\"{ collapsed: !modelValue }\">\n <div class=\"title-bar\">\n {{ headline }}\n <div class=\"actions\">\n <KdsButton\n size=\"small\"\n variant=\"transparent\"\n leading-icon=\"chevron-up\"\n :aria-expanded=\"modelValue\"\n :aria-controls=\"panelContentId\"\n :ariaLabel=\"modelValue ? 'Collapse' : 'Expand'\"\n :style=\"iconFlipStyles\"\n @click=\"modelValue = !modelValue\"\n />\n\n <KdsButton\n v-if=\"canBeDismissed\"\n size=\"small\"\n variant=\"transparent\"\n ariaLabel=\"Close\"\n leading-icon=\"x-close\"\n @click=\"emit('close')\"\n />\n </div>\n </div>\n\n <Transition name=\"expand\">\n <div v-show=\"modelValue\" :id=\"panelContentId\" class=\"panel-content\">\n <slot />\n </div>\n </Transition>\n </div>\n</template>\n\n<style scoped>\n.collapsible-panel {\n background: var(--kds-color-surface-default);\n border-radius: var(--kds-spacing-container-0-25x);\n box-shadow: var(--kds-elevation-level-3);\n\n & .title-bar {\n display: flex;\n align-items: center;\n height: var(--kds-dimension-component-height-2-5x);\n padding: var(--kds-spacing-container-0-5x)\n var(--kds-spacing-container-0-75x);\n font: var(--kds-font-base-title-small-strong);\n color: var(--kds-color-text-and-icon-neutral);\n\n & .actions {\n display: flex;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n margin-left: auto;\n }\n }\n\n &:not(.collapsed) {\n & .title-bar {\n border-bottom: var(--kds-border-base-subtle);\n }\n }\n}\n\n.panel-content {\n overflow: hidden;\n}\n\n.expand-enter-active,\n.expand-leave-active {\n display: grid;\n overflow: hidden;\n transition:\n grid-template-rows 0.2s ease-in-out,\n opacity 0.2s ease-in-out;\n}\n\n.expand-enter-from,\n.expand-leave-to {\n grid-template-rows: 0fr;\n opacity: 0;\n}\n\n.expand-enter-to,\n.expand-leave-from {\n grid-template-rows: 1fr;\n opacity: 1;\n}\n\n.panel-content > :deep(*) {\n min-height: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useId } from \"vue\";\n\nimport { KdsButton } from \"@knime/kds-components\";\n\nimport type { CollapsiblePanelProps } from \"./types\";\nimport { useIconFlipper } from \"./useIconFlipper\";\n\nconst { headline, canBeDismissed = false } =\n defineProps<CollapsiblePanelProps>();\n\nconst emit = defineEmits<{\n close: [];\n}>();\n\nconst modelValue = defineModel<boolean>({ default: true });\n\nconst { iconFlipStyles } = useIconFlipper({\n active: computed(() => modelValue.value),\n initialRotation: \"180deg\",\n});\nconst panelContentId = useId();\n</script>\n\n<template>\n <div class=\"collapsible-panel\" :class=\"{ collapsed: !modelValue }\">\n <div class=\"title-bar\">\n {{ headline }}\n <div class=\"actions\">\n <KdsButton\n size=\"small\"\n variant=\"transparent\"\n leading-icon=\"chevron-up\"\n :aria-expanded=\"modelValue\"\n :aria-controls=\"panelContentId\"\n :ariaLabel=\"modelValue ? 'Collapse' : 'Expand'\"\n :style=\"iconFlipStyles\"\n @click=\"modelValue = !modelValue\"\n />\n\n <KdsButton\n v-if=\"canBeDismissed\"\n size=\"small\"\n variant=\"transparent\"\n ariaLabel=\"Close\"\n leading-icon=\"x-close\"\n @click=\"emit('close')\"\n />\n </div>\n </div>\n\n <Transition name=\"expand\">\n <div v-show=\"modelValue\" :id=\"panelContentId\" class=\"panel-content\">\n <slot />\n </div>\n </Transition>\n </div>\n</template>\n\n<style scoped>\n.collapsible-panel {\n background: var(--kds-color-surface-default);\n border-radius: var(--kds-spacing-container-0-25x);\n box-shadow: var(--kds-elevation-level-3);\n\n & .title-bar {\n display: flex;\n align-items: center;\n height: var(--kds-dimension-component-height-2-5x);\n padding: var(--kds-spacing-container-0-5x)\n var(--kds-spacing-container-0-75x);\n font: var(--kds-font-base-title-small-strong);\n color: var(--kds-color-text-and-icon-neutral);\n\n & .actions {\n display: flex;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n margin-left: auto;\n }\n }\n\n &:not(.collapsed) {\n & .title-bar {\n border-bottom: var(--kds-border-base-subtle);\n }\n }\n}\n\n.panel-content {\n overflow: hidden;\n}\n\n.expand-enter-active,\n.expand-leave-active {\n display: grid;\n overflow: hidden;\n transition:\n grid-template-rows 0.2s ease-in-out,\n opacity 0.2s ease-in-out;\n}\n\n.expand-enter-from,\n.expand-leave-to {\n grid-template-rows: 0fr;\n opacity: 0;\n}\n\n.expand-enter-to,\n.expand-leave-from {\n grid-template-rows: 1fr;\n opacity: 1;\n}\n\n.panel-content > :deep(*) {\n min-height: 0;\n}\n</style>\n","<script lang=\"ts\" setup>\nimport {\n KdsBadge,\n KdsButton,\n KdsIcon,\n KdsProgressBar,\n vKdsTooltip,\n} from \"@knime/kds-components\";\n\nimport { type ProgressItemProps } from \"./types\";\n\ndefineProps<ProgressItemProps>();\n</script>\n\n<template>\n <div class=\"progress-wrapper\">\n <div :class=\"['progress-item', { padded: progress === undefined }]\">\n <div class=\"item-info\">\n <KdsIcon :name=\"icon\" />\n\n <div class=\"item-name\">\n <div v-kds-tooltip:truncated=\"headline\" class=\"title\">\n {{ headline }}\n </div>\n <span v-if=\"subtitle\" class=\"subtitle\">\n {{ subtitle }}\n </span>\n </div>\n </div>\n\n <div class=\"item-action\">\n <KdsBadge\n v-if=\"badge\"\n :variant=\"badge.variant\"\n :label=\"badge.text\"\n :leading-icon=\"badge.icon\"\n size=\"xxsmall\"\n />\n\n <template v-for=\"action in actions.slice(0, 2)\" :key=\"action.id\">\n <KdsButton\n :leading-icon=\"action.icon\"\n :ariaLabel=\"action.label\"\n variant=\"transparent\"\n @click=\"action.handler\"\n />\n </template>\n </div>\n </div>\n\n <div v-if=\"typeof progress === 'number'\" class=\"progress\">\n <KdsProgressBar :percentage=\"progress\" size=\"medium\" />\n </div>\n </div>\n</template>\n\n<style scoped>\n.progress-wrapper {\n --base-padding: var(--kds-spacing-container-1x);\n --progress-bar-height: var(--kds-dimension-component-height-0-25x);\n --progress-bar-distance: var(--kds-spacing-container-0-25x);\n\n display: flex;\n flex-direction: column;\n gap: var(--progress-bar-distance);\n height: var(--kds-dimension-component-height-4x);\n padding: var(--base-padding);\n}\n\n.progress-item {\n display: flex;\n align-items: center;\n width: 100%;\n height: 100%;\n\n /* add extra margin to prevent content jump when a progress bars appears/disappears */\n &.padded {\n margin-bottom: calc(\n var(--progress-bar-height) + var(--progress-bar-distance)\n );\n }\n}\n\n.progress {\n min-height: var(--progress-bar-height);\n}\n\n.item-info {\n display: flex;\n flex-grow: 1;\n gap: var(--kds-spacing-container-0-25x);\n overflow: hidden;\n\n & .item-name {\n display: flex;\n flex-grow: 1;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n justify-content: center;\n overflow: hidden;\n color: var(--kds-color-text-and-icon-neutral);\n\n & .title {\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n font: var(--kds-font-base-interactive-small);\n white-space: nowrap;\n }\n\n & .subtitle {\n font: var(--kds-font-base-subtext-small);\n }\n }\n}\n\n.item-action {\n display: flex;\n flex-shrink: 0;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n}\n</style>\n","<script lang=\"ts\" setup>\nimport {\n KdsBadge,\n KdsButton,\n KdsIcon,\n KdsProgressBar,\n vKdsTooltip,\n} from \"@knime/kds-components\";\n\nimport { type ProgressItemProps } from \"./types\";\n\ndefineProps<ProgressItemProps>();\n</script>\n\n<template>\n <div class=\"progress-wrapper\">\n <div :class=\"['progress-item', { padded: progress === undefined }]\">\n <div class=\"item-info\">\n <KdsIcon :name=\"icon\" />\n\n <div class=\"item-name\">\n <div v-kds-tooltip:truncated=\"headline\" class=\"title\">\n {{ headline }}\n </div>\n <span v-if=\"subtitle\" class=\"subtitle\">\n {{ subtitle }}\n </span>\n </div>\n </div>\n\n <div class=\"item-action\">\n <KdsBadge\n v-if=\"badge\"\n :variant=\"badge.variant\"\n :label=\"badge.text\"\n :leading-icon=\"badge.icon\"\n size=\"xxsmall\"\n />\n\n <template v-for=\"action in actions.slice(0, 2)\" :key=\"action.id\">\n <KdsButton\n :leading-icon=\"action.icon\"\n :ariaLabel=\"action.label\"\n variant=\"transparent\"\n @click=\"action.handler\"\n />\n </template>\n </div>\n </div>\n\n <div v-if=\"typeof progress === 'number'\" class=\"progress\">\n <KdsProgressBar :percentage=\"progress\" size=\"medium\" />\n </div>\n </div>\n</template>\n\n<style scoped>\n.progress-wrapper {\n --base-padding: var(--kds-spacing-container-1x);\n --progress-bar-height: var(--kds-dimension-component-height-0-25x);\n --progress-bar-distance: var(--kds-spacing-container-0-25x);\n\n display: flex;\n flex-direction: column;\n gap: var(--progress-bar-distance);\n height: var(--kds-dimension-component-height-4x);\n padding: var(--base-padding);\n}\n\n.progress-item {\n display: flex;\n align-items: center;\n width: 100%;\n height: 100%;\n\n /* add extra margin to prevent content jump when a progress bars appears/disappears */\n &.padded {\n margin-bottom: calc(\n var(--progress-bar-height) + var(--progress-bar-distance)\n );\n }\n}\n\n.progress {\n min-height: var(--progress-bar-height);\n}\n\n.item-info {\n display: flex;\n flex-grow: 1;\n gap: var(--kds-spacing-container-0-25x);\n overflow: hidden;\n\n & .item-name {\n display: flex;\n flex-grow: 1;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n justify-content: center;\n overflow: hidden;\n color: var(--kds-color-text-and-icon-neutral);\n\n & .title {\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n font: var(--kds-font-base-interactive-small);\n white-space: nowrap;\n }\n\n & .subtitle {\n font: var(--kds-font-base-subtext-small);\n }\n }\n}\n\n.item-action {\n display: flex;\n flex-shrink: 0;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n}\n</style>\n","<template>\n <div class=\"progress-list\" tabindex=\"0\">\n <slot />\n </div>\n</template>\n\n<style scoped>\n.progress-list {\n max-height: var(--kds-dimension-component-height-20x);\n overflow-y: auto;\n\n &:deep(.progress-wrapper):not(:first-child) {\n border-top: var(--kds-border-base-subtle);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport type { KdsBadgeVariant, KdsIconName } from \"@knime/kds-components\";\n\nimport {\n ProgressItem,\n type ProgressItemAction,\n type ProgressItemBadge,\n} from \"../../shared/ui\";\n\nimport type { DownloadItem } from \"./types\";\n\ntype Props = {\n item: DownloadItem;\n};\n\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [];\n download: [];\n}>();\n\nconst statusMapper = {\n IN_PROGRESS: [\"Zipping\", \"info\", \"reload\"],\n READY: [\"Ready\", \"success\", \"circle-info\"],\n CANCELLED: [\"Cancelled\", \"error\", \"circle-error\"],\n FAILED: [\"Failed\", \"error\", \"circle-error\"],\n} satisfies Record<\n DownloadItem[\"status\"],\n [string, KdsBadgeVariant, KdsIconName]\n>;\n\nconst badge = computed<ProgressItemBadge>(() => {\n const [text, variant, icon] = statusMapper[props.item.status];\n\n return {\n text,\n variant,\n icon,\n } satisfies ProgressItemBadge;\n});\n\nconst isCancellable = computed(() => props.item.status === \"IN_PROGRESS\");\nconst isReady = computed(() => props.item.status === \"READY\");\n\nconst actions = computed<Array<ProgressItemAction>>(() => {\n const actions: ProgressItemAction[] = [];\n\n if (isCancellable.value) {\n actions.push({\n id: \"cancel\",\n icon: \"x-close\",\n label: \"Cancel\",\n handler: () => emit(\"cancel\"),\n });\n }\n\n if (isReady.value) {\n actions.push({\n id: \"download\",\n icon: \"cloud-download\",\n label: \"Download again\",\n handler: () => emit(\"download\"),\n });\n }\n\n return actions;\n});\n</script>\n\n<template>\n <ProgressItem\n :id=\"item.downloadId\"\n icon=\"file\"\n :headline=\"item.name\"\n :badge=\"badge\"\n :actions=\"actions\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport type { KdsBadgeVariant, KdsIconName } from \"@knime/kds-components\";\n\nimport {\n ProgressItem,\n type ProgressItemAction,\n type ProgressItemBadge,\n} from \"../../shared/ui\";\n\nimport type { DownloadItem } from \"./types\";\n\ntype Props = {\n item: DownloadItem;\n};\n\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [];\n download: [];\n}>();\n\nconst statusMapper = {\n IN_PROGRESS: [\"Zipping\", \"info\", \"reload\"],\n READY: [\"Ready\", \"success\", \"circle-info\"],\n CANCELLED: [\"Cancelled\", \"error\", \"circle-error\"],\n FAILED: [\"Failed\", \"error\", \"circle-error\"],\n} satisfies Record<\n DownloadItem[\"status\"],\n [string, KdsBadgeVariant, KdsIconName]\n>;\n\nconst badge = computed<ProgressItemBadge>(() => {\n const [text, variant, icon] = statusMapper[props.item.status];\n\n return {\n text,\n variant,\n icon,\n } satisfies ProgressItemBadge;\n});\n\nconst isCancellable = computed(() => props.item.status === \"IN_PROGRESS\");\nconst isReady = computed(() => props.item.status === \"READY\");\n\nconst actions = computed<Array<ProgressItemAction>>(() => {\n const actions: ProgressItemAction[] = [];\n\n if (isCancellable.value) {\n actions.push({\n id: \"cancel\",\n icon: \"x-close\",\n label: \"Cancel\",\n handler: () => emit(\"cancel\"),\n });\n }\n\n if (isReady.value) {\n actions.push({\n id: \"download\",\n icon: \"cloud-download\",\n label: \"Download again\",\n handler: () => emit(\"download\"),\n });\n }\n\n return actions;\n});\n</script>\n\n<template>\n <ProgressItem\n :id=\"item.downloadId\"\n icon=\"file\"\n :headline=\"item.name\"\n :badge=\"badge\"\n :actions=\"actions\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, watch } from \"vue\";\n\nimport { CollapsiblePanel, ProgressList } from \"../../shared/ui\";\n\nimport DownloadProgressPanelItem from \"./DownloadProgressPanelItem.vue\";\nimport type { DownloadItem } from \"./types\";\n\ntype Props = {\n /**\n * List of current download items that should be displayed\n */\n items: DownloadItem[];\n};\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [item: DownloadItem];\n download: [item: DownloadItem];\n close: [];\n}>();\n\nconst expanded = defineModel<boolean>(\"expanded\", { default: true });\n\nconst totalItems = computed(() => props.items.length);\nconst totalDownloadedItems = computed(\n () => props.items.filter(({ status }) => status === \"READY\").length,\n);\n\nconst headline = computed(() => {\n const suffix = `file${totalItems.value > 1 ? \"s\" : \"\"}`;\n return `Downloaded ${totalDownloadedItems.value} of ${totalItems.value} ${suffix}`;\n});\n\nconst hasItemsInProgress = computed(() =>\n props.items.some(({ status }) => status === \"IN_PROGRESS\"),\n);\n\nwatch(\n () => props.items?.length,\n (newValue, oldValue) => {\n if (oldValue > 0 && !newValue) {\n emit(\"close\");\n }\n },\n);\n</script>\n\n<template>\n <CollapsiblePanel\n v-model=\"expanded\"\n :headline=\"headline\"\n class=\"download-panel\"\n :can-be-dismissed=\"!hasItemsInProgress\"\n @close=\"emit('close')\"\n >\n <div class=\"download-panel-content\">\n <ProgressList>\n <DownloadProgressPanelItem\n v-for=\"item in items\"\n :key=\"item.downloadId\"\n :item=\"item\"\n @cancel=\"emit('cancel', item)\"\n @download=\"emit('download', item)\"\n />\n </ProgressList>\n </div>\n </CollapsiblePanel>\n</template>\n\n<style scoped>\n.download-panel {\n width: var(--kds-dimension-component-width-20x);\n max-width: var(--kds-dimension-component-width-20x);\n}\n\n.download-panel-content {\n max-height: var(--kds-dimension-component-height-20x);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, watch } from \"vue\";\n\nimport { CollapsiblePanel, ProgressList } from \"../../shared/ui\";\n\nimport DownloadProgressPanelItem from \"./DownloadProgressPanelItem.vue\";\nimport type { DownloadItem } from \"./types\";\n\ntype Props = {\n /**\n * List of current download items that should be displayed\n */\n items: DownloadItem[];\n};\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [item: DownloadItem];\n download: [item: DownloadItem];\n close: [];\n}>();\n\nconst expanded = defineModel<boolean>(\"expanded\", { default: true });\n\nconst totalItems = computed(() => props.items.length);\nconst totalDownloadedItems = computed(\n () => props.items.filter(({ status }) => status === \"READY\").length,\n);\n\nconst headline = computed(() => {\n const suffix = `file${totalItems.value > 1 ? \"s\" : \"\"}`;\n return `Downloaded ${totalDownloadedItems.value} of ${totalItems.value} ${suffix}`;\n});\n\nconst hasItemsInProgress = computed(() =>\n props.items.some(({ status }) => status === \"IN_PROGRESS\"),\n);\n\nwatch(\n () => props.items?.length,\n (newValue, oldValue) => {\n if (oldValue > 0 && !newValue) {\n emit(\"close\");\n }\n },\n);\n</script>\n\n<template>\n <CollapsiblePanel\n v-model=\"expanded\"\n :headline=\"headline\"\n class=\"download-panel\"\n :can-be-dismissed=\"!hasItemsInProgress\"\n @close=\"emit('close')\"\n >\n <div class=\"download-panel-content\">\n <ProgressList>\n <DownloadProgressPanelItem\n v-for=\"item in items\"\n :key=\"item.downloadId\"\n :item=\"item\"\n @cancel=\"emit('cancel', item)\"\n @download=\"emit('download', item)\"\n />\n </ProgressList>\n </div>\n </CollapsiblePanel>\n</template>\n\n<style scoped>\n.download-panel {\n width: var(--kds-dimension-component-width-20x);\n max-width: var(--kds-dimension-component-width-20x);\n}\n\n.download-panel-content {\n max-height: var(--kds-dimension-component-height-20x);\n}\n</style>\n","import { computed, ref } from \"vue\";\nimport { FetchError, type FetchOptions } from \"ofetch\";\n\nimport { sleep } from \"@knime/utils\";\n\nimport { rfcErrors } from \"../../rfcErrors\";\nimport { createHttpClient } from \"../shared/httpClient\";\n\nimport type { DownloadItem as DownloadItemExternal } from \"./ui/types\";\n\nconst DEFAULT_MAX_RETRIES = null;\nconst DEFAULT_POLLING_INTERVAL = 2000;\n\ntype ArtifactStatusResponse = {\n downloadId?: string;\n status?: string;\n statusMessage?: string;\n lastUpdated?: string;\n downloadUrl?: string;\n};\n\n// Initial request can have either download url (if it can be downloaded immediately) or download id (to poll for the status)\ntype RequestDownloadResponse =\n | {\n downloadId: string;\n }\n | {\n downloadUrl: string;\n };\n\n// type guard\nconst hasDownloadUrl = (\n response: RequestDownloadResponse,\n): response is { downloadUrl: string } => {\n return \"downloadUrl\" in response;\n};\n\n/**\n * The version of the artifact to download.\n */\ntype ItemVersion = number | \"current-state\" | \"most-recent\";\n\n/**\n * Tracks the state of each concurrent download.\n */\ntype DownloadItem = DownloadItemExternal & {\n itemId: string;\n version?: ItemVersion;\n};\n\nexport type UseDownloadArtifactOptions = {\n /**\n * The maximum number of times to poll for the download URL. Default is `null` (no limit).\n */\n maxRetries?: number;\n /**\n * The interval in milliseconds between each poll. Default is `2000` ms.\n */\n pollingInterval?: number;\n /**\n * Options to customize the fetch client used internally to make http requests.\n * e.g: headers, baseUrl, etc\n */\n customFetchClientOptions?: FetchOptions;\n};\n\nconst isAbortError = (error: unknown) =>\n error instanceof DOMException && error.name === \"AbortError\";\n\n/**\n * This composable supports multiple concurrent downloads. Each time you call ```start```, the following behavior applies:\n *\n * 1. **Immediate-ready artifact**: If the first status check for the artifact returns `READY`, then there's no polling and no need\n * to add it to `downloadItems`. The download URL is opened in the same tab immediately.\n *\n * 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`\n * and repeatedly poll until it becomes `READY`, the polling times out, or the user aborts.\n *\n * **Usage**:\n * ```\n * const { start, cancel, downloadItems } = useDownloadArtifact({ ...options });\n *\n * // start a download\n * await start({ itemId: \"1234\", version: 1 });\n *\n * // if needed, cancel it:\n * cancel(\"1234\");\n *\n * // downloadItems (as a computed array) will reflect the current statuses.\n * ```\n */\nexport const useDownloadArtifact = (\n options: UseDownloadArtifactOptions = {},\n) => {\n const $ofetch = createHttpClient(options.customFetchClientOptions);\n\n const downloadItems = ref<Record<string, DownloadItem>>({});\n const abortControllers: Record<string, AbortController> = {};\n\n // Use fallback values if none are provided\n const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\n const pollingInterval = options.pollingInterval ?? DEFAULT_POLLING_INTERVAL;\n\n const cancel = (downloadId: string) => {\n const itemController = abortControllers[downloadId];\n if (itemController) {\n itemController.abort();\n delete abortControllers[downloadId];\n }\n };\n\n const removeItem = (downloadId: string) => {\n cancel(downloadId);\n delete downloadItems.value[downloadId];\n };\n\n const fetchDownloadStatus = ({\n downloadId,\n signal,\n }: {\n downloadId: string;\n signal?: AbortSignal;\n }) => {\n return $ofetch<ArtifactStatusResponse>(`/downloads/${downloadId}/status`, {\n method: \"GET\",\n signal,\n });\n };\n\n const openDownload = (downloadUrl: string) => {\n const a = document.createElement(\"a\");\n a.target = \"_blank\";\n a.href = downloadUrl;\n a.download = \"\";\n a.style.display = \"none\";\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n };\n\n /**\n * Makes a request to the download status endpoint for the given download item.\n * Returns a boolean that is true if a terminal state has been reached (download is ready,\n * download failed, or user aborted download)\n * @param downloadId the `downloadId`\n * @param signal the abort signal passed to the request to be able to abort it\n * @returns a boolean indicating whether a terminal state has been reached\n */\n const handlePollingResult = async ({\n downloadId,\n signal,\n }: {\n downloadId: string;\n signal: AbortSignal;\n }) => {\n try {\n const statusResponse = await fetchDownloadStatus({\n downloadId,\n signal,\n });\n\n if (statusResponse?.status === \"READY\" && statusResponse?.downloadUrl) {\n downloadItems.value[downloadId].status = \"READY\";\n downloadItems.value[downloadId].downloadUrl =\n statusResponse.downloadUrl;\n // open download in a new tab\n openDownload(statusResponse.downloadUrl);\n return true;\n } else if (statusResponse?.status === \"FAILED\") {\n let details = \"Download failed\";\n if (statusResponse?.statusMessage) {\n details += `: ${statusResponse.statusMessage}`;\n }\n throw new Error(details);\n }\n } catch (error: unknown) {\n consola.error(\"Error fetching status:\", error);\n // https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort\n const isAborted =\n isAbortError(error) ||\n (error instanceof FetchError && isAbortError(error.cause));\n\n if (!isAborted) {\n downloadItems.value[downloadId].status = \"FAILED\";\n downloadItems.value[downloadId].failureDetails =\n error instanceof Error ? error.message : (error as string);\n }\n return true;\n }\n return false;\n };\n\n const pollDownloadItem = async ({\n itemId,\n downloadId,\n version,\n name,\n }: {\n itemId: string;\n downloadId: string;\n version?: ItemVersion;\n name: string;\n }) => {\n const controller = new AbortController();\n abortControllers[downloadId] = controller;\n\n const signal = controller.signal;\n\n let itemName = `${name}`;\n if (version) {\n itemName += ` (${version})`;\n }\n\n downloadItems.value[downloadId] = {\n itemId,\n ...(version && { version }),\n downloadId,\n name: itemName,\n status: \"IN_PROGRESS\",\n };\n\n const abortListener = () => {\n controller.signal.removeEventListener(\"abort\", abortListener);\n if (!downloadItems.value[downloadId]) {\n return;\n }\n downloadItems.value[downloadId].status = \"CANCELLED\";\n downloadItems.value[downloadId].failureDetails =\n \"Download was cancelled manually\";\n };\n controller.signal.addEventListener(\"abort\", abortListener);\n\n let tries = 0;\n // eslint-disable-next-line no-unmodified-loop-condition\n while (maxRetries === null || tries < maxRetries) {\n tries++;\n await sleep(pollingInterval);\n\n const isTerminalState = await handlePollingResult({ downloadId, signal });\n\n if (isTerminalState) {\n break;\n }\n }\n\n delete abortControllers[downloadId];\n\n if (maxRetries !== null && tries >= maxRetries) {\n downloadItems.value[downloadId].status = \"FAILED\";\n downloadItems.value[downloadId].failureDetails = \"Download timed out\";\n }\n };\n\n const start = async ({\n itemId,\n version,\n name,\n }: {\n itemId: string;\n name: string;\n version?: ItemVersion;\n }) => {\n try {\n const response = await $ofetch<RequestDownloadResponse>(\n `/repository/${itemId}/artifact`,\n {\n method: \"GET\",\n query: { version },\n },\n );\n\n // if download is ready, open url immediately\n if (hasDownloadUrl(response)) {\n openDownload(response.downloadUrl);\n return;\n }\n\n // otherwise start polling download status\n await pollDownloadItem({\n itemId,\n version,\n downloadId: response.downloadId,\n name,\n });\n } catch (error: unknown) {\n // here only errors thrown by the initial requests are caught;\n // errors occurring while polling will be handled in pollDownloadItem\n throw rfcErrors.tryParse(error) ?? error;\n }\n };\n\n const totalItemsBeingZipped = computed(\n () =>\n Object.values(downloadItems.value).filter(\n ({ status }) => status === \"IN_PROGRESS\",\n ).length,\n );\n\n const resetState = () => {\n for (const [downloadId, controller] of Object.entries(abortControllers)) {\n controller?.abort();\n delete abortControllers[downloadId];\n }\n downloadItems.value = {};\n };\n\n const downloadItemsArray = computed(() => Object.values(downloadItems.value));\n\n return {\n /**\n * Initiates a download of the given itemId and version.\n */\n start,\n\n /**\n * Cancels the ongoing download for a given itemId (if any).\n * Sets the status in `downloadItems` to `CANCELLED`.\n */\n cancel,\n\n /**\n * Removes item from `downloadItems`.\n */\n removeItem,\n\n /**\n * A computed array of all current items that are being zipped and prepared for download.\n */\n downloadItems: downloadItemsArray,\n\n /**\n * The total number of zipping items in progress.\n */\n totalItemsBeingZipped,\n\n /**\n * Resets the state of the composable, clearing all downloads and controllers.\n */\n resetState,\n\n /**\n * Opens a download from the given url\n */\n openDownload,\n };\n};\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport type { KdsBadgeVariant, KdsIconName } from \"@knime/kds-components\";\nimport {\n formatFileSize,\n getFileExtension,\n knimeFileFormats,\n} from \"@knime/utils\";\n\nimport {\n ProgressItem,\n type ProgressItemAction,\n type ProgressItemBadge,\n} from \"../shared/ui\";\n\nimport type { UploadItem, UploadItemStatus } from \"./types\";\n\ntype Props = {\n item: UploadItem;\n};\n\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [];\n viewDetails: [];\n}>();\n\nconst subtitle = computed(() => {\n if (props.item.type === \"file\") {\n const progressed = formatFileSize(\n (props.item.size / 100) * (props.item.progress ?? 0),\n );\n\n const total = formatFileSize(props.item.size);\n return `${progressed} of ${total}`;\n }\n\n if (props.item.type === \"directory\") {\n const total = props.item.totalEntries;\n const progressed = props.item.uploadedEntries;\n\n return `${progressed} of ${total} file${total > 1 ? \"s\" : \"\"}`;\n }\n\n return \"\";\n});\n\nconst progressPercentageValue = computed(() => {\n if (props.item.type === \"file\") {\n return props.item.progress;\n }\n\n if (props.item.type === \"directory\") {\n // The KdsProgressBar only accepts percentages, but for directory items\n // the consumer supplies different values so we must derive the percentage\n const progressPercentage =\n (props.item.uploadedEntries * 100) / props.item.totalEntries;\n\n return progressPercentage;\n }\n\n return undefined;\n});\n\nconst statusMapper = {\n inprogress: [\"Uploading\", \"info\", \"reload\"],\n failed: [\"Failed\", \"error\", \"circle-error\"],\n complete: [\"Uploaded\", \"success\", \"circle-success\"],\n cancelled: [\"Cancelled\", \"error\", \"circle-error\"],\n processing: [\"Processing\", \"info\", \"reload\"],\n \"partial-success\": [\"Partially uploaded\", \"warning\", \"warning\"],\n} satisfies Record<UploadItemStatus, [string, KdsBadgeVariant, KdsIconName]>;\n\nconst itemIcon = computed<KdsIconName>(() => {\n if (props.item.type === \"directory\") {\n return \"folder\";\n }\n\n const extension = getFileExtension(props.item.name);\n return extension === knimeFileFormats.KNWF.extension ? \"workflow\" : \"file\";\n});\n\nconst badge = computed<ProgressItemBadge | undefined>(() => {\n if (!props.item.status) {\n return undefined;\n }\n\n const [text, variant, icon] = statusMapper[props.item.status];\n\n return { text, variant, icon } satisfies ProgressItemBadge;\n});\n\nconst isCancellable = computed(() => {\n if (\n props.item.actions === undefined ||\n props.item.actions.cancellable === undefined\n ) {\n return true;\n }\n\n return props.item.actions.cancellable;\n});\n\nconst shouldShowCancelAction = computed(\n () => props.item.status === \"inprogress\" && isCancellable.value,\n);\n\nconst isInspectable = computed(() => {\n if (\n props.item.actions === undefined ||\n props.item.actions.inspectable === undefined\n ) {\n return false;\n }\n\n return props.item.actions.inspectable;\n});\n\nconst shouldShowViewDetailsAction = computed(\n () => props.item.status === \"partial-success\" && isInspectable.value,\n);\n\nconst actions = computed<Array<ProgressItemAction>>(() => {\n const result: ProgressItemAction[] = [];\n\n if (shouldShowCancelAction.value) {\n result.push({\n id: \"cancel\",\n icon: \"x-close\",\n label: \"Cancel\",\n handler: () => emit(\"cancel\"),\n });\n }\n\n if (shouldShowViewDetailsAction.value) {\n result.push({\n id: \"view-details\",\n icon: \"circle-info\",\n label: \"View details\",\n handler: () => emit(\"viewDetails\"),\n });\n }\n\n return result;\n});\n</script>\n\n<template>\n <ProgressItem\n :id=\"item.id\"\n :headline=\"item.name\"\n :subtitle=\"subtitle\"\n :icon=\"itemIcon\"\n :progress=\"progressPercentageValue\"\n :badge=\"badge\"\n :actions=\"actions\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport type { KdsBadgeVariant, KdsIconName } from \"@knime/kds-components\";\nimport {\n formatFileSize,\n getFileExtension,\n knimeFileFormats,\n} from \"@knime/utils\";\n\nimport {\n ProgressItem,\n type ProgressItemAction,\n type ProgressItemBadge,\n} from \"../shared/ui\";\n\nimport type { UploadItem, UploadItemStatus } from \"./types\";\n\ntype Props = {\n item: UploadItem;\n};\n\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [];\n viewDetails: [];\n}>();\n\nconst subtitle = computed(() => {\n if (props.item.type === \"file\") {\n const progressed = formatFileSize(\n (props.item.size / 100) * (props.item.progress ?? 0),\n );\n\n const total = formatFileSize(props.item.size);\n return `${progressed} of ${total}`;\n }\n\n if (props.item.type === \"directory\") {\n const total = props.item.totalEntries;\n const progressed = props.item.uploadedEntries;\n\n return `${progressed} of ${total} file${total > 1 ? \"s\" : \"\"}`;\n }\n\n return \"\";\n});\n\nconst progressPercentageValue = computed(() => {\n if (props.item.type === \"file\") {\n return props.item.progress;\n }\n\n if (props.item.type === \"directory\") {\n // The KdsProgressBar only accepts percentages, but for directory items\n // the consumer supplies different values so we must derive the percentage\n const progressPercentage =\n (props.item.uploadedEntries * 100) / props.item.totalEntries;\n\n return progressPercentage;\n }\n\n return undefined;\n});\n\nconst statusMapper = {\n inprogress: [\"Uploading\", \"info\", \"reload\"],\n failed: [\"Failed\", \"error\", \"circle-error\"],\n complete: [\"Uploaded\", \"success\", \"circle-success\"],\n cancelled: [\"Cancelled\", \"error\", \"circle-error\"],\n processing: [\"Processing\", \"info\", \"reload\"],\n \"partial-success\": [\"Partially uploaded\", \"warning\", \"warning\"],\n} satisfies Record<UploadItemStatus, [string, KdsBadgeVariant, KdsIconName]>;\n\nconst itemIcon = computed<KdsIconName>(() => {\n if (props.item.type === \"directory\") {\n return \"folder\";\n }\n\n const extension = getFileExtension(props.item.name);\n return extension === knimeFileFormats.KNWF.extension ? \"workflow\" : \"file\";\n});\n\nconst badge = computed<ProgressItemBadge | undefined>(() => {\n if (!props.item.status) {\n return undefined;\n }\n\n const [text, variant, icon] = statusMapper[props.item.status];\n\n return { text, variant, icon } satisfies ProgressItemBadge;\n});\n\nconst isCancellable = computed(() => {\n if (\n props.item.actions === undefined ||\n props.item.actions.cancellable === undefined\n ) {\n return true;\n }\n\n return props.item.actions.cancellable;\n});\n\nconst shouldShowCancelAction = computed(\n () => props.item.status === \"inprogress\" && isCancellable.value,\n);\n\nconst isInspectable = computed(() => {\n if (\n props.item.actions === undefined ||\n props.item.actions.inspectable === undefined\n ) {\n return false;\n }\n\n return props.item.actions.inspectable;\n});\n\nconst shouldShowViewDetailsAction = computed(\n () => props.item.status === \"partial-success\" && isInspectable.value,\n);\n\nconst actions = computed<Array<ProgressItemAction>>(() => {\n const result: ProgressItemAction[] = [];\n\n if (shouldShowCancelAction.value) {\n result.push({\n id: \"cancel\",\n icon: \"x-close\",\n label: \"Cancel\",\n handler: () => emit(\"cancel\"),\n });\n }\n\n if (shouldShowViewDetailsAction.value) {\n result.push({\n id: \"view-details\",\n icon: \"circle-info\",\n label: \"View details\",\n handler: () => emit(\"viewDetails\"),\n });\n }\n\n return result;\n});\n</script>\n\n<template>\n <ProgressItem\n :id=\"item.id\"\n :headline=\"item.name\"\n :subtitle=\"subtitle\"\n :icon=\"itemIcon\"\n :progress=\"progressPercentageValue\"\n :badge=\"badge\"\n :actions=\"actions\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport { KdsButton, KdsLoadingSkeleton } from \"@knime/kds-components\";\n\nimport { CollapsiblePanel, ProgressList } from \"../shared/ui\";\n\nimport UploadProgressPanelItem from \"./UploadProgressPanelItem.vue\";\nimport type { UploadItem, UploadProgressPanelProps } from \"./types\";\n\nconst {\n items,\n placeholderItems = 0,\n canRetry = false,\n} = defineProps<UploadProgressPanelProps>();\n\nconst emit = defineEmits<{\n cancel: [item: UploadItem];\n viewDetails: [item: UploadItem];\n retry: [];\n close: [];\n}>();\n\nconst expanded = defineModel<boolean>(\"expanded\", { default: true });\n\nconst totalItems = computed(() => items.length);\nconst uploadedItems = computed(\n () => items.filter(({ status }) => status === \"complete\").length,\n);\n\nconst headline = computed(() => {\n const suffix = `item${totalItems.value > 1 ? \"s\" : \"\"}`;\n return `Uploaded ${uploadedItems.value} of ${totalItems.value} ${suffix}`;\n});\n\nconst hasSomeItemInProgress = computed(\n () =>\n items.some(\n ({ status }) => status === \"inprogress\" || status === \"processing\",\n ) || placeholderItems > 0,\n);\n</script>\n\n<template>\n <CollapsiblePanel\n v-model=\"expanded\"\n :headline=\"headline\"\n class=\"upload-panel\"\n :can-be-dismissed=\"!hasSomeItemInProgress\"\n @close=\"emit('close')\"\n >\n <div class=\"upload-panel-content\">\n <ProgressList>\n <UploadProgressPanelItem\n v-for=\"item in items\"\n :key=\"item.id\"\n :item=\"item\"\n @cancel=\"emit('cancel', item)\"\n @view-details=\"emit('viewDetails', item)\"\n />\n </ProgressList>\n\n <template v-if=\"placeholderItems > 0\">\n <div\n v-for=\"skeleton in placeholderItems\"\n :key=\"skeleton\"\n class=\"skeleton-container\"\n >\n <KdsLoadingSkeleton variant=\"icon-large\" />\n <div class=\"skeleton-text-content\">\n <KdsLoadingSkeleton variant=\"label\" />\n <KdsLoadingSkeleton variant=\"text\" />\n </div>\n </div>\n </template>\n\n <div v-if=\"canRetry && !hasSomeItemInProgress\" class=\"retry-bar\">\n <KdsButton\n size=\"small\"\n variant=\"outlined\"\n label=\"Retry failed items\"\n leading-icon=\"reload\"\n @click=\"emit('retry')\"\n />\n </div>\n </div>\n </CollapsiblePanel>\n</template>\n\n<style scoped>\n.upload-panel {\n width: var(--kds-dimension-component-width-20x);\n max-width: var(--kds-dimension-component-width-20x);\n}\n\n.upload-panel-content {\n display: flex;\n flex-direction: column;\n}\n\n.retry-bar {\n display: flex;\n justify-content: flex-end;\n padding: var(--kds-spacing-container-0-5x) var(--kds-spacing-container-0-75x);\n border-top: var(--kds-border-base-subtle);\n}\n\n.skeleton-container {\n display: grid;\n grid-template-columns: min-content 1fr;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n min-height: var(--kds-dimension-component-height-4x);\n padding: 0 var(--kds-spacing-container-1x);\n border-top: var(--kds-border-base-subtle);\n\n & .skeleton-text-content {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport { KdsButton, KdsLoadingSkeleton } from \"@knime/kds-components\";\n\nimport { CollapsiblePanel, ProgressList } from \"../shared/ui\";\n\nimport UploadProgressPanelItem from \"./UploadProgressPanelItem.vue\";\nimport type { UploadItem, UploadProgressPanelProps } from \"./types\";\n\nconst {\n items,\n placeholderItems = 0,\n canRetry = false,\n} = defineProps<UploadProgressPanelProps>();\n\nconst emit = defineEmits<{\n cancel: [item: UploadItem];\n viewDetails: [item: UploadItem];\n retry: [];\n close: [];\n}>();\n\nconst expanded = defineModel<boolean>(\"expanded\", { default: true });\n\nconst totalItems = computed(() => items.length);\nconst uploadedItems = computed(\n () => items.filter(({ status }) => status === \"complete\").length,\n);\n\nconst headline = computed(() => {\n const suffix = `item${totalItems.value > 1 ? \"s\" : \"\"}`;\n return `Uploaded ${uploadedItems.value} of ${totalItems.value} ${suffix}`;\n});\n\nconst hasSomeItemInProgress = computed(\n () =>\n items.some(\n ({ status }) => status === \"inprogress\" || status === \"processing\",\n ) || placeholderItems > 0,\n);\n</script>\n\n<template>\n <CollapsiblePanel\n v-model=\"expanded\"\n :headline=\"headline\"\n class=\"upload-panel\"\n :can-be-dismissed=\"!hasSomeItemInProgress\"\n @close=\"emit('close')\"\n >\n <div class=\"upload-panel-content\">\n <ProgressList>\n <UploadProgressPanelItem\n v-for=\"item in items\"\n :key=\"item.id\"\n :item=\"item\"\n @cancel=\"emit('cancel', item)\"\n @view-details=\"emit('viewDetails', item)\"\n />\n </ProgressList>\n\n <template v-if=\"placeholderItems > 0\">\n <div\n v-for=\"skeleton in placeholderItems\"\n :key=\"skeleton\"\n class=\"skeleton-container\"\n >\n <KdsLoadingSkeleton variant=\"icon-large\" />\n <div class=\"skeleton-text-content\">\n <KdsLoadingSkeleton variant=\"label\" />\n <KdsLoadingSkeleton variant=\"text\" />\n </div>\n </div>\n </template>\n\n <div v-if=\"canRetry && !hasSomeItemInProgress\" class=\"retry-bar\">\n <KdsButton\n size=\"small\"\n variant=\"outlined\"\n label=\"Retry failed items\"\n leading-icon=\"reload\"\n @click=\"emit('retry')\"\n />\n </div>\n </div>\n </CollapsiblePanel>\n</template>\n\n<style scoped>\n.upload-panel {\n width: var(--kds-dimension-component-width-20x);\n max-width: var(--kds-dimension-component-width-20x);\n}\n\n.upload-panel-content {\n display: flex;\n flex-direction: column;\n}\n\n.retry-bar {\n display: flex;\n justify-content: flex-end;\n padding: var(--kds-spacing-container-0-5x) var(--kds-spacing-container-0-75x);\n border-top: var(--kds-border-base-subtle);\n}\n\n.skeleton-container {\n display: grid;\n grid-template-columns: min-content 1fr;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n min-height: var(--kds-dimension-component-height-4x);\n padding: 0 var(--kds-spacing-container-1x);\n border-top: var(--kds-border-base-subtle);\n\n & .skeleton-text-content {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n }\n}\n</style>\n","import { computed, ref } from \"vue\";\n\nimport { type ChunkUploadManagerConfig, createUploadManager } from \"../core\";\n\nimport type { FileUploadItem, UploadItem } from \"./types\";\n\ntype OnCompletePayload = Parameters<\n NonNullable<ChunkUploadManagerConfig[\"onFileUploadComplete\"]>\n>[0] & { parentId: string; name: string };\n\ntype OnFailedPayload = Parameters<\n NonNullable<ChunkUploadManagerConfig[\"onFileUploadFailed\"]>\n>[0] & { parentId: string };\n\ntype UseUploadManagerOptions = Omit<\n ChunkUploadManagerConfig,\n \"onProgress\" | \"onFileUploadComplete\" | \"onFileUploadFailed\"\n> & {\n onFileUploadComplete?: (payload: OnCompletePayload) => void;\n onFileUploadFailed?: (payload: OnFailedPayload) => void;\n /**\n * Determines the status to assign a file once its raw upload finishes,\n * before calling `onFileUploadComplete`. This helps for files that need further\n * server-side processing\n *\n * Defaults to `complete`\n */\n resolveCompleteStatus?: (\n payload: OnCompletePayload,\n ) => \"complete\" | \"processing\";\n};\n\n/**\n * This composable wraps the `uploadManager` utility\n * in order to provide reactive state which will work tightly with the `UploadProgressPanel` component\n *\n * The reactive state returned (`uploadItems`) will update whenever the progress\n * or status of each item being uploaded changes\n */\nexport const useUploadManager = (options: UseUploadManagerOptions) => {\n const { resolveCompleteStatus, ...restOptions } = options;\n const uploadState = ref<Record<string, UploadItem>>({});\n\n const { uploadFiles, cancel, setFailed } = createUploadManager({\n ...restOptions,\n\n onFileUploadComplete: (params) => {\n const { parentId, name } = uploadState.value[params.uploadId];\n const payload = { ...params, parentId, name };\n uploadState.value[params.uploadId].status =\n resolveCompleteStatus?.(payload) ?? \"complete\";\n options.onFileUploadComplete?.(payload);\n },\n\n onFileUploadFailed: ({ uploadId, error }) => {\n const { parentId } = uploadState.value[uploadId];\n uploadState.value[uploadId] = {\n ...uploadState.value[uploadId],\n status: \"failed\",\n failureDetails: error.message,\n };\n options.onFileUploadFailed?.({ uploadId, error, parentId });\n },\n\n onProgress: (uploadId, progress) => {\n if (uploadState.value[uploadId].status === \"inprogress\") {\n (uploadState.value[uploadId] as FileUploadItem).progress = progress;\n }\n },\n });\n\n const start = async (\n parentId: string,\n uploadPayload: Array<{ uploadId: string; file: File }>,\n ) => {\n try {\n uploadPayload.forEach(({ uploadId, file }) => {\n uploadState.value[uploadId] = {\n id: uploadId,\n name: file.name,\n size: file.size,\n progress: 0,\n type: \"file\",\n status: \"inprogress\",\n parentId,\n };\n });\n\n return await uploadFiles(uploadPayload);\n } catch (error) {\n consola.error(\"Error uploading files\", error);\n return [];\n }\n };\n\n const canCancel = (uploadId: string) =>\n uploadState.value[uploadId]?.status === \"inprogress\" ||\n uploadState.value[uploadId]?.status === \"failed\";\n\n const removeItem = (uploadId: string) => {\n if (canCancel(uploadId)) {\n cancel(uploadId);\n }\n\n delete uploadState.value[uploadId];\n };\n\n const resetState = () => {\n uploadState.value = {};\n };\n\n const uploadItems = computed(() => Object.values(uploadState.value));\n\n const hasPendingUploads = computed(() =>\n uploadItems.value.some(({ status }) => status === \"inprogress\"),\n );\n\n const totalPendingUploads = computed(\n () =>\n uploadItems.value.filter(({ status }) => status === \"inprogress\").length,\n );\n\n return {\n uploadState,\n hasPendingUploads,\n totalPendingUploads,\n start,\n cancel: (uploadId: string) => {\n cancel(uploadId);\n uploadState.value[uploadId].status = \"cancelled\";\n },\n canCancel,\n removeItem,\n resetState,\n setFailed: (uploadId: string, error: Error) => {\n uploadState.value[uploadId] = {\n ...uploadState.value[uploadId],\n status: \"failed\",\n failureDetails: error.message,\n };\n setFailed(uploadId);\n const { parentId } = uploadState.value[uploadId];\n options.onFileUploadFailed?.({ parentId, uploadId, error });\n },\n };\n};\n","import { computed, ref } from \"vue\";\nimport type { FetchError, FetchOptions } from \"ofetch\";\n\nimport {\n formatFileSize,\n getFileExtension,\n knimeFileFormats,\n promise,\n} from \"@knime/utils\";\n\nimport { rfcErrors } from \"../../rfcErrors\";\nimport { createUploadClient, createUploadStatusPoller } from \"../core\";\n\nimport type { UploadItem } from \"./types\";\nimport { useUploadManager } from \"./useUploadManager\";\n\nconst DEFAULT_MAX_UPLOAD_QUEUE_SIZE = 10;\n// eslint-disable-next-line no-magic-numbers\nconst DEFAULT_UPLOAD_SIZE_LIMIT_BYTES = 5 * 1024 * 1024 * 1024;\n\nconst DEFAULT_RETRY_DELAY_MS = 50;\n\n// Catalog & artifacts services currently have a limit for 5 GiB for\n// upload and copy/move operations but it is not communicated to clients.\n// Repeat the constant here to be able to still make a pre-check. See NXT-4510.\nconst getUploadSizeLimitBytes = () => DEFAULT_UPLOAD_SIZE_LIMIT_BYTES;\n\n/**\n * Renames any File whose name repeats an earlier one in the list, appending\n * a \" (n)\" suffix (before the extension, if any) - including a name produced\n * by this same disambiguation, so a literal \"name (1).ext\" already in the\n * list can't collide with a generated one\n */\nconst disambiguateFiles = (files: File[]): File[] => {\n const usedNames = new Set<string>();\n\n return files.map((file) => {\n if (!usedNames.has(file.name)) {\n usedNames.add(file.name);\n return file;\n }\n\n const extension = getFileExtension(file.name);\n const base = extension\n ? file.name.slice(0, -(extension.length + 1))\n : file.name;\n\n let counter = 1;\n let candidate = extension\n ? `${base} (${counter}).${extension}`\n : `${base} (${counter})`;\n\n while (usedNames.has(candidate)) {\n counter += 1;\n candidate = extension\n ? `${base} (${counter}).${extension}`\n : `${base} (${counter})`;\n }\n\n usedNames.add(candidate);\n return new File([file], candidate, {\n type: file.type,\n lastModified: file.lastModified,\n });\n });\n};\n\ntype UseFileUploadOptions = {\n /**\n * Max number of concurrent uploads allowed in the upload queue.\n * When an upload is started with a number of files that exceed the pending\n * uploads then only files up-to the max queue size will be processed and the rest\n * will be ignored. This will also call the `onUploadQueueSizeExceeded` callback\n */\n maxUploadQueueSize?: number;\n /**\n * Callback that triggers every time any file in the queue completes its upload\n * @returns\n */\n onFileUploadComplete?: (payload: {\n uploadId: string;\n filePartIds: Record<number, string>;\n parentId: string;\n name: string;\n }) => void;\n /**\n * Callback that triggers every time any file in the queue fails its upload\n * @returns\n */\n onFileUploadFailed?: (payload: {\n uploadId: string;\n error: Error;\n parentId: string;\n }) => void;\n /**\n * Callback that triggers when a upload batch is started (via the `start` function) but the amount\n * of items to upload exceed the max configured size of the queue.\n * @returns\n */\n onUploadQueueSizeExceeded?: (maxQueueSize: number) => void;\n /**\n * Options to customize the fetch client used internally to make http requests.\n * e.g: headers, baseUrl, etc\n */\n customFetchClientOptions?: FetchOptions;\n\n /**\n * Whether the manifest endpoint is flat (`/manifest`) rather than nested\n * under a repository item (`/repository/{parentId}/manifest`). Used for\n * targets whose upload endpoint isn't scoped by a repository parent,\n * e.g. environment extension DB driver uploads.\n */\n shouldUseFlatManifestPath?: boolean;\n\n isFileWithProcessing?: (fileName: string) => boolean;\n};\n\n/**\n * This composable handles uploads of data to a hub repository item (space or workflow group).\n * It is a multi-step process which is described as follows:\n *\n * Step 1:\n * First we need to prepare an upload. The items will be uploaded to a `parent` container\n * identified by its id. (When `shouldUseFlatManifestPath` is set, the manifest endpoint isn't\n * scoped by a parent, so the `parentId` passed to `start` is unused for that request.)\n * Then, for each file we want to upload, we must provide paths relative\n * to the parent that identify each file. Example:\n * ```\n * {\n * \"items\": {\n * \"my-file.txt\": { itemContentType: \"text/plain\", itemContentSize: 5000 },\n * \"folder/my-file.zip\": { itemContentType: \"application/zip\", itemContentSize: 5000 },\n * }\n * }\n * ```\n * This describes 2 items, one placed in the parent's root and another inside a folder.\n * The response for this would look like:\n * ```\n * {\n * \"items\": {\n * \"my-file.txt\": { uploadId: \"id1\" },\n * \"folder/my-file.zip\": { uploadId: \"id2\" },\n * }\n * }\n * ```\n * Now, each corresponding `uploadId` uniquely identifies the upload process for\n * their respective files.\n *\n * Step 2:\n * Next, we need to start uploading the file parts. Technically, you _can_ just upload\n * each file as one big file \"part\", but practically that is not recommended because a hiccup\n * in the connection means you would have to start the upload from scratch; instead, we split\n * the files in smaller parts.\n * The upload manager helper will take care of splitting the file. However, the responsibility\n * of this composable is to supply a function to resolve the upload URL. This upload URL is obtained\n * by POSTing to an endpoint with the `uploadId` and the `partNumber` and this will return\n * the data required to make an upload to a presigned URL; this will be called for each file part.\n *\n * Step 3:\n * Completion:\n * Once the upload of a single file is complete, the upload manager will trigger the `onFileUploadComplete`\n * callback, which then will supply the ids of all the different parts that got uploaded for a given `uploadId`\n * Then we need to \"complete\" the upload by sending a request which provides the `uploadId` and a list of\n * the ids of all the parts. Note that some backends don't return the ids of the file parts after a successful\n * upload of a part, in this case empty strings or null can be used instead of the part id.\n *\n * Cancellation:\n * Alternative to completion, you can also cancel an upload. For this, the composable makes a DELETE request\n * providing the corresponding `uploadId` which will effectively cancel it.\n *\n * For a usage example, refer to the Docs page in storybook.\n */\nexport const useFileUpload = (options: UseFileUploadOptions = {}) => {\n const uploadClient = createUploadClient({\n customFetchClientOptions: options.customFetchClientOptions,\n shouldUseFlatManifestPath: options.shouldUseFlatManifestPath,\n });\n\n let useUploadManagerResult: ReturnType<typeof useUploadManager> | null = null;\n\n const getItemFromQueue = (uploadId: string) => {\n const { uploadState } = useUploadManagerResult!;\n\n if (!uploadState.value[uploadId]) {\n consola.warn(\n `Cannot get item in queue. Upload id \"${uploadId}\" doesn't exist`,\n );\n return undefined;\n }\n\n return uploadState.value[uploadId];\n };\n\n const updateItemInQueue = (uploadId: string, data: Partial<UploadItem>) => {\n const { uploadState } = useUploadManagerResult!;\n\n if (!uploadState.value[uploadId]) {\n consola.warn(\n `Cannot update item in queue. Upload id \"${uploadId}\" doesn't exist`,\n );\n return;\n }\n\n uploadState.value = {\n ...uploadState.value,\n [uploadId]: {\n ...uploadState.value[uploadId],\n ...data,\n } as UploadItem,\n };\n };\n\n const statusPoller = createUploadStatusPoller(uploadClient, {\n onComplete: (uploadId) =>\n updateItemInQueue(uploadId, { status: \"complete\" }),\n\n onFail: (uploadId, statusMessage) =>\n updateItemInQueue(uploadId, {\n status: \"failed\",\n failureDetails: statusMessage,\n }),\n });\n\n useUploadManagerResult = useUploadManager({\n resolveFilePartUploadURL: uploadClient.resolveFilePartUploadURL,\n\n resolveCompleteStatus: ({ name }) =>\n options.isFileWithProcessing?.(name) ? \"processing\" : \"complete\",\n\n onFileUploadComplete: ({ uploadId, filePartIds, name, parentId }) => {\n promise\n .retryPromise({\n fn: () => uploadClient.completeUpload(uploadId, filePartIds),\n })\n .then(() => {\n if (options.isFileWithProcessing?.(name)) {\n statusPoller.track(uploadId);\n }\n\n options.onFileUploadComplete?.({\n uploadId,\n filePartIds,\n name,\n parentId,\n });\n })\n .catch((error) => {\n consola.error(\"Error attempting to complete upload\", { error });\n useUploadManagerResult!.setFailed(uploadId, error);\n });\n },\n\n onFileUploadFailed: ({ uploadId, error, parentId }) => {\n options?.onFileUploadFailed?.({ uploadId, error, parentId });\n },\n });\n\n const getEnqueueableFiles = (files: File[]): File[] => {\n const { totalPendingUploads } = useUploadManagerResult;\n\n const { maxUploadQueueSize = DEFAULT_MAX_UPLOAD_QUEUE_SIZE } = options;\n\n if (files.length + totalPendingUploads.value > maxUploadQueueSize) {\n options.onUploadQueueSizeExceeded?.(maxUploadQueueSize);\n }\n\n // only keep as many files as the queue allows\n const effectiveQueueSize = Math.max(\n 0,\n maxUploadQueueSize - totalPendingUploads.value,\n );\n return files.slice(0, effectiveQueueSize);\n };\n\n const { uploadState, ...uploadManagerResultRest } = useUploadManagerResult;\n\n const prepareQueueSize = ref(0);\n\n const uploadQueue = computed<UploadItem[]>(() =>\n Object.values(uploadState.value),\n );\n\n return {\n ...uploadManagerResultRest,\n\n uploadQueue,\n getItemFromQueue,\n updateItemInQueue,\n\n isPreparingUpload: computed(() => prepareQueueSize.value > 0),\n totalFilesBeingPrepared: computed(() => prepareQueueSize.value),\n\n start: async (parentId: string, files: File[]): Promise<string[]> => {\n const enqueueableFiles = getEnqueueableFiles(files);\n const uploadSizeLimitBytes = getUploadSizeLimitBytes();\n const oversizedFiles = enqueueableFiles.filter(\n (file) => file.size > uploadSizeLimitBytes,\n );\n\n if (oversizedFiles.length > 0) {\n throw new rfcErrors.RFCError({\n title: \"Upload size limit exceeded\",\n status: 413,\n details: oversizedFiles.map(\n (file) =>\n `${file.name} (${formatFileSize(file.size)}) exceeds the upload limit of ${formatFileSize(uploadSizeLimitBytes)}.`,\n ),\n });\n }\n\n try {\n if (enqueueableFiles.length === 0) {\n return [];\n }\n\n prepareQueueSize.value += enqueueableFiles.length;\n\n const disambiguatedFiles = disambiguateFiles(enqueueableFiles);\n\n const items = disambiguatedFiles.map((file) => ({\n // strip extension for workflows. otherwise, the extension gets baked into the name\n // of the uploaded file and the backend appends an extra extension.\n // unnecessary for other file types\n path: knimeFileFormats.KNWF.getNameOrDefault(file, file.name),\n file,\n }));\n\n const uploadPayload = await promise.retryPromise({\n fn: () => uploadClient.prepareUpload(parentId, items),\n excludeError: (error: FetchError) =>\n // eslint-disable-next-line no-magic-numbers\n Boolean(error.statusCode && error.statusCode < 500),\n retryDelayMS: DEFAULT_RETRY_DELAY_MS,\n });\n\n const uploadIdByFile = new Map(\n uploadPayload.map((upload) => [upload.file, upload.uploadId]),\n );\n const uploadIds = disambiguatedFiles.map((file) =>\n uploadIdByFile.get(file)!,\n );\n\n // accept floating promise, errors are handled inside the function\n useUploadManagerResult.start(parentId, uploadPayload);\n\n return uploadIds;\n } catch (error) {\n throw rfcErrors.tryParse(error) ?? error;\n } finally {\n // errors can only be thrown in the prepareUpload call, useUploadManagerResult.start does its own error handling\n prepareQueueSize.value -= enqueueableFiles.length;\n }\n },\n\n cancel: (uploadId: string) => {\n useUploadManagerResult.cancel(uploadId);\n uploadClient.cancelUpload(uploadId).catch((error: unknown) => {\n consola.error(\"There was a problem cancelling the upload\", { error });\n });\n },\n };\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@knime/product-features",
3
- "version": "3.0.1",
3
+ "version": "4.0.0",
4
4
  "description": "Package containing composition patterns of KDS Components and KNIME business logic",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/knime/knime-designsystem.git",
8
8
  "directory": "packages/product-features"
9
9
  },
10
- "license": "GPL 3 and Additional Permissions according to Sec. 7 (SEE the file LICENSE)",
10
+ "license": "KNIME-Commercial-License-2.0",
11
11
  "author": "KNIME AG, Zurich, Switzerland",
12
12
  "keywords": [
13
13
  "knime",
@@ -51,9 +51,9 @@
51
51
  "CHANGELOG.md"
52
52
  ],
53
53
  "peerDependencies": {
54
- "@knime/kds-components": "^8.2.0",
55
- "@knime/kds-styles": "^8.2.0",
56
- "@vueuse/core": "~13.8.0",
54
+ "@knime/kds-components": "^9.0.0",
55
+ "@knime/kds-styles": "^9.0.0",
56
+ "@vueuse/core": "^14.0.0",
57
57
  "es-toolkit": "^1.49.0",
58
58
  "ofetch": "^1.5.1",
59
59
  "vue": "~3.5.0"
@@ -63,8 +63,8 @@
63
63
  "consola": "^3.4.2"
64
64
  },
65
65
  "devDependencies": {
66
- "@knime/kds-components": "^8.2.1",
67
- "@knime/kds-styles": "^8.2.1",
66
+ "@knime/kds-components": "^9.0.0",
67
+ "@knime/kds-styles": "^9.0.0",
68
68
  "@storybook/addon-docs": "^10.4.6",
69
69
  "@storybook/vue3-vite": "^10.4.6",
70
70
  "@vitejs/plugin-vue": "^6.0.8",