@knime/product-features 2.2.2 → 2.3.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/core-CVWznjtP.js +739 -0
  3. package/dist/core-CVWznjtP.js.map +1 -0
  4. package/dist/dataTransfer/core.js +2 -0
  5. package/dist/dataTransfer.css +12 -12
  6. package/dist/dataTransfer.js +420 -767
  7. package/dist/dataTransfer.js.map +1 -1
  8. package/dist/src/dataTransfer/core.d.ts +2 -0
  9. package/dist/src/dataTransfer/core.d.ts.map +1 -0
  10. package/dist/src/dataTransfer/index.d.ts +1 -2
  11. package/dist/src/dataTransfer/index.d.ts.map +1 -1
  12. package/dist/src/dataTransfer/upload/{ui/UploadProgressPanel.vue.d.ts → UploadProgressPanel.vue.d.ts} +2 -0
  13. package/dist/src/dataTransfer/upload/UploadProgressPanel.vue.d.ts.map +1 -0
  14. package/dist/src/dataTransfer/upload/{ui/UploadProgressPanelItem.vue.d.ts → UploadProgressPanelItem.vue.d.ts} +2 -1
  15. package/dist/src/dataTransfer/upload/UploadProgressPanelItem.vue.d.ts.map +1 -0
  16. package/dist/src/dataTransfer/upload/core/index.d.ts +3 -0
  17. package/dist/src/dataTransfer/upload/core/index.d.ts.map +1 -0
  18. package/dist/src/dataTransfer/upload/core/uploadClient/createUploadStatusPoller.d.ts +26 -0
  19. package/dist/src/dataTransfer/upload/core/uploadClient/createUploadStatusPoller.d.ts.map +1 -0
  20. package/dist/src/dataTransfer/upload/core/uploadClient/index.d.ts +3 -0
  21. package/dist/src/dataTransfer/upload/core/uploadClient/index.d.ts.map +1 -0
  22. package/dist/src/dataTransfer/upload/core/uploadClient/uploadClient.d.ts +59 -0
  23. package/dist/src/dataTransfer/upload/core/uploadClient/uploadClient.d.ts.map +1 -0
  24. package/dist/src/dataTransfer/upload/{manager/index.d.ts → core/uploadManager/chunkedUploadManager.d.ts} +21 -60
  25. package/dist/src/dataTransfer/upload/core/uploadManager/chunkedUploadManager.d.ts.map +1 -0
  26. package/dist/src/dataTransfer/upload/core/uploadManager/config.d.ts +12 -0
  27. package/dist/src/dataTransfer/upload/core/uploadManager/config.d.ts.map +1 -0
  28. package/dist/src/dataTransfer/upload/core/uploadManager/index.d.ts +5 -0
  29. package/dist/src/dataTransfer/upload/core/uploadManager/index.d.ts.map +1 -0
  30. package/dist/src/dataTransfer/upload/core/uploadManager/state.d.ts +30 -0
  31. package/dist/src/dataTransfer/upload/core/uploadManager/state.d.ts.map +1 -0
  32. package/dist/src/dataTransfer/upload/core/uploadManager/streamingUploadManager.d.ts +52 -0
  33. package/dist/src/dataTransfer/upload/core/uploadManager/streamingUploadManager.d.ts.map +1 -0
  34. package/dist/src/dataTransfer/upload/core/uploadManager/types.d.ts +46 -0
  35. package/dist/src/dataTransfer/upload/core/uploadManager/types.d.ts.map +1 -0
  36. package/dist/src/dataTransfer/upload/core/uploadManager/uploadPartBytes.d.ts +17 -0
  37. package/dist/src/dataTransfer/upload/core/uploadManager/uploadPartBytes.d.ts.map +1 -0
  38. package/dist/src/dataTransfer/upload/index.d.ts +6 -0
  39. package/dist/src/dataTransfer/upload/index.d.ts.map +1 -0
  40. package/dist/src/dataTransfer/upload/types.d.ts +80 -0
  41. package/dist/src/dataTransfer/upload/types.d.ts.map +1 -0
  42. package/dist/src/dataTransfer/upload/useFileUpload.d.ts +5 -13
  43. package/dist/src/dataTransfer/upload/useFileUpload.d.ts.map +1 -1
  44. package/dist/src/dataTransfer/upload/{ui/useUploadManager.d.ts → useUploadManager.d.ts} +17 -5
  45. package/dist/src/dataTransfer/upload/useUploadManager.d.ts.map +1 -0
  46. package/package.json +8 -4
  47. package/dist/src/dataTransfer/upload/manager/index.d.ts.map +0 -1
  48. package/dist/src/dataTransfer/upload/ui/UploadProgressPanel.vue.d.ts.map +0 -1
  49. package/dist/src/dataTransfer/upload/ui/UploadProgressPanelItem.vue.d.ts.map +0 -1
  50. package/dist/src/dataTransfer/upload/ui/index.d.ts +0 -5
  51. package/dist/src/dataTransfer/upload/ui/index.d.ts.map +0 -1
  52. package/dist/src/dataTransfer/upload/ui/types.d.ts +0 -60
  53. package/dist/src/dataTransfer/upload/ui/types.d.ts.map +0 -1
  54. package/dist/src/dataTransfer/upload/ui/useUploadManager.d.ts.map +0 -1
@@ -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/upload/ui/UploadProgressPanelItem.vue","../src/dataTransfer/upload/ui/UploadProgressPanelItem.vue","../src/dataTransfer/upload/ui/UploadProgressPanel.vue","../src/dataTransfer/upload/ui/UploadProgressPanel.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","../../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs","../../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/_internal/isUnsafeProperty.mjs","../../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/object/merge.mjs","../src/dataTransfer/shared/httpClient/constants.ts","../src/dataTransfer/shared/httpClient/createHttpClient.ts","../src/dataTransfer/upload/manager/index.ts","../src/dataTransfer/upload/ui/useUploadManager.ts","../src/dataTransfer/upload/useFileUpload.ts","../src/dataTransfer/download/useDownloadArtifact.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\";\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, WithProgress } from \"./types\";\n\ntype Props = {\n item: UploadItem;\n allowCancel: boolean;\n};\n\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [];\n}>();\n\nconst hasProgress = (item: UploadItem): item is WithProgress => {\n return item.status === \"inprogress\" || item.status === \"complete\";\n};\n\nconst totalFormatted = computed(() => {\n return formatFileSize(props.item.size);\n});\n\nconst progressedFormatted = computed(() => {\n if (hasProgress(props.item)) {\n const sizePercentage = (props.item.size / 100) * (props.item.progress ?? 0);\n return formatFileSize(sizePercentage);\n }\n\n return 0;\n});\n\nconst subtitle = computed(() =>\n props.item.status === \"inprogress\"\n ? `${progressedFormatted.value} of ${totalFormatted.value}`\n : `${totalFormatted.value}`,\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} satisfies Record<UploadItemStatus, [string, KdsBadgeVariant, KdsIconName]>;\n\nconst itemIcon = computed<KdsIconName>(() => {\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 shouldShowCancelAction = computed(\n () => props.allowCancel && props.item.status === \"inprogress\",\n);\n\nconst actions = computed<Array<ProgressItemAction>>(() => {\n return shouldShowCancelAction.value\n ? ([\n {\n id: \"cancel\",\n icon: \"x-close\",\n label: \"Cancel\",\n handler: () => emit(\"cancel\"),\n },\n ] satisfies ProgressItemAction[])\n : [];\n});\n</script>\n\n<template>\n <ProgressItem\n :id=\"item.id\"\n :headline=\"item.name\"\n :subtitle=\"subtitle\"\n :icon=\"itemIcon\"\n :progress=\"hasProgress(item) ? item.progress : undefined\"\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, WithProgress } from \"./types\";\n\ntype Props = {\n item: UploadItem;\n allowCancel: boolean;\n};\n\nconst props = defineProps<Props>();\n\nconst emit = defineEmits<{\n cancel: [];\n}>();\n\nconst hasProgress = (item: UploadItem): item is WithProgress => {\n return item.status === \"inprogress\" || item.status === \"complete\";\n};\n\nconst totalFormatted = computed(() => {\n return formatFileSize(props.item.size);\n});\n\nconst progressedFormatted = computed(() => {\n if (hasProgress(props.item)) {\n const sizePercentage = (props.item.size / 100) * (props.item.progress ?? 0);\n return formatFileSize(sizePercentage);\n }\n\n return 0;\n});\n\nconst subtitle = computed(() =>\n props.item.status === \"inprogress\"\n ? `${progressedFormatted.value} of ${totalFormatted.value}`\n : `${totalFormatted.value}`,\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} satisfies Record<UploadItemStatus, [string, KdsBadgeVariant, KdsIconName]>;\n\nconst itemIcon = computed<KdsIconName>(() => {\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 shouldShowCancelAction = computed(\n () => props.allowCancel && props.item.status === \"inprogress\",\n);\n\nconst actions = computed<Array<ProgressItemAction>>(() => {\n return shouldShowCancelAction.value\n ? ([\n {\n id: \"cancel\",\n icon: \"x-close\",\n label: \"Cancel\",\n handler: () => emit(\"cancel\"),\n },\n ] satisfies ProgressItemAction[])\n : [];\n});\n</script>\n\n<template>\n <ProgressItem\n :id=\"item.id\"\n :headline=\"item.name\"\n :subtitle=\"subtitle\"\n :icon=\"itemIcon\"\n :progress=\"hasProgress(item) ? item.progress : undefined\"\n :badge=\"badge\"\n :actions=\"actions\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport { 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 allowCancel = true,\n placeholderItems = 0,\n} = defineProps<UploadProgressPanelProps>();\n\nconst emit = defineEmits<{\n cancel: [item: UploadItem];\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 = `file${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 :allow-cancel=\"allowCancel\"\n @cancel=\"emit('cancel', 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 </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 max-height: var(--kds-dimension-component-height-20x);\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 { 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 allowCancel = true,\n placeholderItems = 0,\n} = defineProps<UploadProgressPanelProps>();\n\nconst emit = defineEmits<{\n cancel: [item: UploadItem];\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 = `file${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 :allow-cancel=\"allowCancel\"\n @cancel=\"emit('cancel', 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 </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 max-height: var(--kds-dimension-component-height-20x);\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 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","//#region src/predicate/isPlainObject.ts\n/**\n* Checks if a given value is a plain object.\n*\n* @param value - The value to check.\n* @returns True if the value is a plain object, otherwise false.\n*\n* @example\n* ```typescript\n* // ✅👇 True\n*\n* isPlainObject({ }); // ✅\n* isPlainObject({ key: 'value' }); // ✅\n* isPlainObject({ key: new Date() }); // ✅\n* isPlainObject(new Object()); // ✅\n* isPlainObject(Object.create(null)); // ✅\n* isPlainObject({ nested: { key: true} }); // ✅\n* isPlainObject(new Proxy({}, {})); // ✅\n* isPlainObject({ [Symbol('tag')]: 'A' }); // ✅\n*\n* // ✅👇 (cross-realms, node context, workers, ...)\n* const runInNewContext = await import('node:vm').then(\n* (mod) => mod.runInNewContext\n* );\n* isPlainObject(runInNewContext('({})')); // ✅\n*\n* // ❌👇 False\n*\n* class Test { };\n* isPlainObject(new Test()) // ❌\n* isPlainObject(10); // ❌\n* isPlainObject(null); // ❌\n* isPlainObject('hello'); // ❌\n* isPlainObject([]); // ❌\n* isPlainObject(new Date()); // ❌\n* isPlainObject(new Uint8Array([1])); // ❌\n* isPlainObject(Buffer.from('ABC')); // ❌\n* isPlainObject(Promise.resolve({})); // ❌\n* isPlainObject(Object.create({})); // ❌\n* isPlainObject(new (class Cls {})); // ❌\n* isPlainObject(globalThis); // ❌,\n* ```\n*/\nfunction isPlainObject(value) {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst proto = Object.getPrototypeOf(value);\n\tif (!(proto === null || proto === Object.prototype || Object.getPrototypeOf(proto) === null)) return false;\n\treturn Object.prototype.toString.call(value) === \"[object Object]\";\n}\n//#endregion\nexport { isPlainObject };\n","//#region src/_internal/isUnsafeProperty.ts\n/**\n* Checks if a property key is unsafe to modify directly.\n*\n* This function is used in functions like `merge` to prevent prototype pollution attacks\n* by identifying property keys that could modify the object's prototype chain or constructor.\n*\n* @param key - The property key to check\n* @returns `true` if the property is unsafe to modify directly, `false` otherwise\n* @internal\n*/\nfunction isUnsafeProperty(key) {\n\treturn key === \"__proto__\";\n}\n//#endregion\nexport { isUnsafeProperty };\n","import { isPlainObject } from \"../predicate/isPlainObject.mjs\";\nimport { isUnsafeProperty } from \"../_internal/isUnsafeProperty.mjs\";\n//#region src/object/merge.ts\n/**\n* Merges the properties of the source object into the target object.\n*\n* This function performs a deep merge, meaning nested objects and arrays are merged recursively.\n* If a property in the source object is an array or an object and the corresponding property in the target object is also an array or object, they will be merged.\n* If a property in the source object is undefined, it will not overwrite a defined property in the target object.\n*\n* Note that this function mutates the target object.\n*\n* @param target - The target object into which the source object properties will be merged. This object is modified in place.\n* @param source - The source object whose properties will be merged into the target object.\n* @returns The updated target object with properties from the source object merged in.\n*\n* @template T - Type of the target object.\n* @template S - Type of the source object.\n*\n* @example\n* const target = { a: 1, b: { x: 1, y: 2 } };\n* const source = { b: { y: 3, z: 4 }, c: 5 };\n*\n* const result = merge(target, source);\n* console.log(result);\n* // Output: { a: 1, b: { x: 1, y: 3, z: 4 }, c: 5 }\n*\n* @example\n* const target = { a: [1, 2], b: { x: 1 } };\n* const source = { a: [3], b: { y: 2 } };\n*\n* const result = merge(target, source);\n* console.log(result);\n* // Output: { a: [3, 2], b: { x: 1, y: 2 } }\n*\n* @example\n* const target = { a: null };\n* const source = { a: [1, 2, 3] };\n*\n* const result = merge(target, source);\n* console.log(result);\n* // Output: { a: [1, 2, 3] }\n*/\nfunction merge(target, source) {\n\tconst sourceKeys = Object.keys(source);\n\tfor (let i = 0; i < sourceKeys.length; i++) {\n\t\tconst key = sourceKeys[i];\n\t\tif (isUnsafeProperty(key)) continue;\n\t\tconst sourceValue = source[key];\n\t\tconst targetValue = target[key];\n\t\tif (isMergeableValue(sourceValue) && isMergeableValue(targetValue)) target[key] = merge(targetValue, sourceValue);\n\t\telse if (Array.isArray(sourceValue)) target[key] = merge([], sourceValue);\n\t\telse if (isPlainObject(sourceValue)) target[key] = merge({}, sourceValue);\n\t\telse if (targetValue === void 0 || sourceValue !== void 0) target[key] = sourceValue;\n\t}\n\treturn target;\n}\nfunction isMergeableValue(value) {\n\treturn isPlainObject(value) || Array.isArray(value);\n}\n//#endregion\nexport { merge };\n","export const API_BASE_URL = \"/_/api\";\n","import { merge } from \"es-toolkit/object\";\nimport { type FetchOptions, ofetch } from \"ofetch\";\n\nimport { API_BASE_URL } from \"./constants\";\n\nconst defaultConfig: FetchOptions = {\n baseURL: API_BASE_URL,\n headers: {\n \"Content-Type\": \"application/json\",\n\n // see: https://knime-com.atlassian.net/wiki/spaces/SPECS/pages/4126769212/API+Errors#Client-Request-Headers\n Accept: \"application/json; application/problem+json\",\n },\n} as const;\n\nconst createHttpClient = (customOptions?: FetchOptions) => {\n const { baseURL = API_BASE_URL, ...otherCustomOptions } = customOptions ?? {};\n\n return ofetch.create(\n merge({ baseURL, ...otherCustomOptions }, defaultConfig),\n );\n};\n\nexport { type FetchOptions, defaultConfig, createHttpClient };\n","import { promise as promiseUtils } from \"@knime/utils\";\n\n// eslint-disable-next-line no-magic-numbers\nconst CHUNK_SIZE = 50 * 1024 * 1024; // 50 MB per chunk\n\nconst DEFAULT_RETRY_COUNT = 5;\nconst DEFAULT_RETRY_DELAY_MS = 50;\n\nexport const UploadStates = {\n IDLE: \"idle\",\n IN_PROGRESS: \"inprogress\",\n CANCELLED: \"cancelled\",\n FAILED: \"failed\",\n COMPLETE: \"complete\",\n} as const;\n\nexport type UploadStateName = (typeof UploadStates)[keyof typeof UploadStates];\n\nexport type PartUploadResponse = {\n partId: string;\n};\n\nconst uploadChunkWithProgress = (params: {\n method: string;\n url: string;\n chunk: Blob;\n chunkIndex: number;\n onProgress: (progress: number) => void;\n abortSignal: AbortSignal;\n}) => {\n const { method, url, chunk, chunkIndex, onProgress, abortSignal } = params;\n\n const OK = 200;\n const CREATED = 201;\n\n return new Promise<{ partId: string }>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.open(method, url, true);\n\n abortSignal.addEventListener(\"abort\", () => xhr.abort());\n\n // Track the upload progress of the current chunk\n xhr.upload.onprogress = (event) => {\n if (event.lengthComputable) {\n onProgress(event.loaded);\n }\n };\n\n xhr.onload = () => {\n if (xhr.status === OK || xhr.status === CREATED) {\n const etag = xhr.getResponseHeader(\"ETag\");\n\n // 1. note that some backends don't return the etag header, then we just set an empty string\n // 2. remove possible doublequotes (\") around the etag and/or weak identifier (if either is present)\n // https://datatracker.ietf.org/doc/html/rfc7232#section-2.3\n resolve({ partId: etag?.replace(/^(?:W\\/)?\"|\"$/g, \"\") ?? \"\" });\n } else {\n consola.error(\"Failed or unexpected XHR response\", {\n xhrStatus: xhr.status,\n xhrResponse: xhr.response,\n chunkIndex,\n });\n reject(new Error(\"Failed to upload file\"));\n }\n };\n\n xhr.onerror = (event) => {\n consola.error(\"Failed to upload chunk\", {\n xhrErrorEvent: event,\n chunkIndex,\n });\n reject(new Error(\"Failed to upload file\"));\n };\n\n xhr.send(chunk);\n });\n};\n\nexport type UploaderManagerConfig = {\n /**\n * Function to resolve the metadata for a request to upload a file part\n * @param uploadId\n * @param partNumber\n * @returns\n */\n resolveFilePartUploadURL: (\n uploadId: string,\n partNumber: number,\n ) => Promise<{ method: string; url: string }>;\n\n /**\n * Callback that gets triggered when the whole file is uploaded.\n * @param\n * @returns\n */\n onFileUploadComplete?: (payload: {\n /**\n * Identifier of the upload for this file (must be unique)\n */\n uploadId: string;\n /**\n * Ids of all the uploaded file parts\n */\n filePartIds: Record<number, string>;\n }) => void;\n\n /**\n * Callback that gets triggered when an upload has failed.\n * @param uploadId\n * @param error the error that cause the failure if it exists\n * @returns\n */\n onFileUploadFailed?: (payload: { uploadId: string; error: Error }) => void;\n\n /**\n * Callback that gets triggered when the progress of a file part upload\n * for a specific upload id is updated\n * @param uploadId\n * @param progress\n * @returns\n */\n onProgress?: (uploadId: string, progress: number) => void;\n};\n\ntype UploadState = {\n uploadId: string;\n file: File;\n completedChunkCount: number;\n uploadedByteCount: number;\n state: UploadStateName;\n abortController: AbortController;\n};\n\n/**\n * Creates and returns an `uploadManager`, a comprehensive multi-file, multi-part upload handler.\n *\n * The `uploadManager` offers functionality for handling file uploads in parts and provides\n * several built-in mechanisms to ensure the process is reliable and adaptable.\n *\n * Key features include:\n *\n * - **Chunked File Uploads**: Splits provided files into manageable chunks and initiates the upload\n * for each part using the `resolveFilePartUploadURL` function provided in the configuration.\n *\n * - **Retry Mechanism**: Automatically retries failed upload attempts up to a maximum of 5 times\n * per failed request.\n *\n * - **Upload Cancellation**: Allows cancellation of ongoing uploads, giving users control over the process.\n *\n * - **Progress Tracking**: Monitors the progress of each file part upload and triggers the `onProgress`\n * callback to provide updates.\n *\n * - **Error Handling**: The returned `uploadFiles` function does not throw errors directly. Instead,\n * it manages retries and stops any failed uploads after the retry limit is reached, marking them as failed.\n * This ensures that the promise it returns completes gracefully without uncaught exceptions.\n *\n */\nexport const createUploadManager = (config: UploaderManagerConfig) => {\n const {\n resolveFilePartUploadURL: getUploadFilePartUrl,\n onFileUploadComplete,\n onFileUploadFailed,\n } = config;\n const uploadStateMap = new Map<string, UploadState>();\n\n const completedUploads = new Set<string>();\n const cancelledUploads = new Set<string>();\n const failedUploads = new Set<string>();\n\n const assertState = (uploadId: string, state: UploadStateName) =>\n uploadStateMap.has(uploadId) &&\n uploadStateMap.get(uploadId)!.state === state;\n\n const getUploadState = (uploadId: string) => uploadStateMap.get(uploadId);\n const setUploadState = (uploadId: string, state: Partial<UploadState>) => {\n uploadStateMap.set(uploadId, {\n ...uploadStateMap.get(uploadId)!,\n ...state,\n });\n };\n\n const isFailed = (uploadId: string) => assertState(uploadId, \"failed\");\n const isCancelled = (uploadId: string) => assertState(uploadId, \"cancelled\");\n\n const getTotalFileParts = (file: File) => Math.ceil(file.size / CHUNK_SIZE);\n\n const abortInFlightUpload = (uploadId: string, reason: string) => {\n const currentState = uploadStateMap.get(uploadId);\n\n if (!currentState) {\n return;\n }\n\n currentState.abortController.abort(\n new promiseUtils.AbortError(\n `Upload (${currentState.uploadId}) for file \"${currentState.file.name}\" aborted. Reason: \"${reason}\"`,\n ),\n );\n };\n\n const cancel = (uploadId: string) => {\n if (!uploadStateMap.has(uploadId)) {\n consola.error(\"cancel:: Invalid upload id\");\n return;\n }\n\n setUploadState(uploadId, { state: \"cancelled\" });\n\n abortInFlightUpload(uploadId, \"upload cancelled\");\n cancelledUploads.add(uploadId);\n };\n\n const setFailed = (uploadId: string, error: Error) => {\n if (!uploadStateMap.has(uploadId)) {\n consola.error(\"failed:: Invalid upload id\");\n return;\n }\n\n setUploadState(uploadId, { state: \"failed\" });\n onFileUploadFailed?.({ uploadId, error });\n failedUploads.add(uploadId);\n\n if (completedUploads.has(uploadId)) {\n completedUploads.delete(uploadId);\n }\n };\n\n const uploadFileFromChunkIndex = async (\n uploadId: string,\n file: File,\n initialChunkIndex = 0,\n ) => {\n const totalChunks = getTotalFileParts(file);\n const totalBytes = file.size;\n const completedPartIds: Record<number, string> = {};\n\n const { abortController, runAbortablePromise } =\n promiseUtils.createAbortablePromise();\n\n uploadStateMap.set(uploadId, {\n uploadId,\n file,\n state: \"inprogress\",\n uploadedByteCount: uploadStateMap.get(uploadId)?.uploadedByteCount ?? 0,\n completedChunkCount:\n uploadStateMap.get(uploadId)?.completedChunkCount ?? 0,\n abortController,\n });\n\n let chunkIndex = initialChunkIndex;\n let uploadedBytes = uploadStateMap.get(uploadId)?.uploadedByteCount;\n\n while (chunkIndex < totalChunks) {\n if (isCancelled(uploadId) || isFailed(uploadId)) {\n break;\n }\n\n const start = chunkIndex * CHUNK_SIZE;\n const end = Math.min(start + CHUNK_SIZE, file.size);\n\n const chunk = file.slice(start, end);\n\n try {\n const { method, url } = await promiseUtils.retryPromise({\n fn: () => getUploadFilePartUrl(uploadId, chunkIndex + 1),\n retryCount: DEFAULT_RETRY_COUNT,\n retryDelayMS: DEFAULT_RETRY_DELAY_MS,\n });\n\n consola.trace(\"File part upload url resolved\", { method, url });\n\n const onChunkUploadProgress = (uploadedChunkBytes: number) => {\n uploadedBytes = uploadedChunkBytes + start;\n setUploadState(uploadId, { uploadedByteCount: uploadedBytes });\n\n const progress = Math.round((uploadedBytes / totalBytes) * 100);\n\n config.onProgress?.(uploadId, progress);\n };\n\n const filePartResponse = await runAbortablePromise(() =>\n promiseUtils.retryPromise({\n fn: () =>\n uploadChunkWithProgress({\n method,\n url,\n chunk,\n chunkIndex,\n onProgress: onChunkUploadProgress,\n abortSignal: abortController.signal,\n }),\n retryCount: DEFAULT_RETRY_COUNT,\n retryDelayMS: DEFAULT_RETRY_DELAY_MS,\n }),\n );\n\n consola.trace(\n `${file.name}: Chunk ${chunkIndex + 1} of ${totalChunks} uploaded successfully`,\n );\n\n completedPartIds[chunkIndex + 1] = filePartResponse.partId;\n\n setUploadState(uploadId, {\n completedChunkCount:\n uploadStateMap.get(uploadId)!.completedChunkCount + 1,\n });\n } catch (error) {\n if (error instanceof promiseUtils.AbortError) {\n consola.info(\"Aborted::\", error);\n return;\n }\n\n setFailed(\n uploadId,\n error instanceof Error ? error : new Error(error as string),\n );\n return;\n }\n\n chunkIndex++;\n }\n\n if (isCancelled(uploadId) || isFailed(uploadId)) {\n return;\n }\n\n setUploadState(uploadId, {\n completedChunkCount: totalChunks,\n state: \"complete\",\n });\n completedUploads.add(uploadId);\n onFileUploadComplete?.({ uploadId, filePartIds: completedPartIds });\n };\n\n const uploadFiles = (files: Array<{ uploadId: string; file: File }>) => {\n const result: Array<Promise<unknown>> = [];\n\n for (const { uploadId, file } of files) {\n result.push(uploadFileFromChunkIndex(uploadId, file));\n }\n\n return Promise.allSettled(result).then(() => {\n // state cleanup\n const completed = [...completedUploads.values()];\n const cancelled = [...cancelledUploads.values()];\n const failed = [...failedUploads.values()];\n\n [...completed, ...cancelled, ...failed].forEach((uploadId) =>\n uploadStateMap.delete(uploadId),\n );\n\n completedUploads.clear();\n cancelledUploads.clear();\n failedUploads.clear();\n\n return {\n completed,\n cancelled,\n failed,\n };\n });\n };\n\n return {\n uploadFiles,\n\n getUploadState,\n isFailed,\n isCancelled,\n cancel,\n setFailed,\n };\n};\n","import { computed, ref } from \"vue\";\n\nimport { type UploaderManagerConfig, createUploadManager } from \"../manager\";\n\nimport type { UploadItem } from \"./types\";\n\ntype OnCompletePayload = Parameters<\n NonNullable<UploaderManagerConfig[\"onFileUploadComplete\"]>\n>[0] & { parentId: string; name: string };\n\ntype OnFailedPayload = Parameters<\n NonNullable<UploaderManagerConfig[\"onFileUploadFailed\"]>\n>[0] & { parentId: string };\n\ntype UseUploadManagerOptions = Omit<\n UploaderManagerConfig,\n \"onProgress\" | \"onFileUploadComplete\" | \"onFileUploadFailed\"\n> & {\n onFileUploadComplete?: (payload: OnCompletePayload) => void;\n onFileUploadFailed?: (payload: OnFailedPayload) => void;\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 uploadState = ref<Record<string, UploadItem>>({});\n\n const { uploadFiles, cancel, setFailed } = createUploadManager({\n ...options,\n\n onFileUploadComplete: (params) => {\n const { parentId, name } = uploadState.value[params.uploadId];\n uploadState.value[params.uploadId].status = \"complete\";\n options.onFileUploadComplete?.({ ...params, parentId, name });\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].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 status: \"inprogress\",\n parentId,\n };\n });\n\n await uploadFiles(uploadPayload);\n } catch (error) {\n consola.error(\"Error uploading files\", error);\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, error);\n },\n };\n};\n","import { computed, reactive, readonly, ref } from \"vue\";\nimport type { FetchError, FetchOptions } from \"ofetch\";\n\nimport {\n formatFileSize,\n getFileMimeType,\n knimeFileFormats,\n promise,\n} from \"@knime/utils\";\n\nimport { rfcErrors } from \"../../rfcErrors\";\nimport { createHttpClient } from \"../shared/httpClient\";\n\nimport type { UploadItem } from \"./ui\";\nimport { useUploadManager } from \"./ui/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\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/**\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. 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 *\n * For a usage example, refer to the Docs page in storybook.\n */\nexport const useFileUpload = (options: UseFileUploadOptions = {}) => {\n const $ofetch = createHttpClient(options.customFetchClientOptions);\n\n const prepareUpload = (\n parentId: string,\n files: Array<File>,\n ): Promise<Array<{ uploadId: string; file: File }>> => {\n const fileDictionary = Object.fromEntries(\n files.map((file) => {\n // strip extension for workflows\n const name = knimeFileFormats.KNWF.getNameOrDefault(file, file.name);\n\n return [name, file];\n }),\n );\n\n // map file dictionary to body payload of the prepare upload request\n const items: Record<\n string,\n { itemContentType: string; itemContentSize: number }\n > = Object.fromEntries(\n Object.entries(fileDictionary).map(([name, file]) => [\n name,\n {\n itemContentType: getFileMimeType(file),\n itemContentSize: file.size,\n },\n ]),\n );\n\n type PrepareUploadResponse = {\n items: Record<string, { uploadId: string }>;\n };\n\n return $ofetch<PrepareUploadResponse>(`/repository/${parentId}/manifest`, {\n method: \"POST\",\n body: { items },\n }).then(({ items }) => {\n return Object.keys(items).map((name) => {\n const { uploadId } = items[name];\n const file = fileDictionary[name];\n return { uploadId, file };\n });\n });\n };\n\n const resolveFilePartUploadURL = (uploadId: string, partNumber: number) => {\n type UploadURLResponse = {\n method: string;\n url: string;\n header: { Host: string };\n };\n\n return $ofetch<UploadURLResponse>(\n `/uploads/${uploadId}/parts/?partNumber=${partNumber}`,\n { method: \"POST\" },\n );\n };\n\n const completeUpload = (\n uploadId: string,\n partIds: Record<number, string>,\n ) => {\n return $ofetch(`/uploads/${uploadId}`, {\n method: \"POST\",\n body: partIds,\n });\n };\n\n const cancelUpload = (uploadId: string) => {\n return $ofetch(`/uploads/${uploadId}`, {\n method: \"DELETE\",\n });\n };\n\n // contains upload ids for files that have a processing step which was not finished yet\n const unprocessedUploads = reactive<Set<string>>(new Set());\n const isKNWFFile = (file: File) => knimeFileFormats.KNWF.matches(file);\n\n let useUploadManagerResult: ReturnType<typeof useUploadManager> | null = null;\n\n const setProcessingCompleted = ({ uploadId }: { uploadId: string }) => {\n unprocessedUploads.delete(uploadId);\n };\n\n const setProcessingFailed = ({\n uploadId,\n error,\n }: {\n uploadId: string;\n error?: Error;\n }) => {\n unprocessedUploads.delete(uploadId);\n useUploadManagerResult?.setFailed(\n uploadId,\n error ?? new Error(\"An error occurred when processing the file\"),\n );\n };\n\n useUploadManagerResult = useUploadManager({\n resolveFilePartUploadURL,\n\n onFileUploadComplete: ({ uploadId, filePartIds, parentId, name }) => {\n promise\n .retryPromise({ fn: () => completeUpload(uploadId, filePartIds) })\n .then(() => {\n options.onFileUploadComplete?.({\n uploadId,\n filePartIds,\n parentId,\n name,\n });\n })\n .catch((error) => {\n consola.error(\"Error attempting to complete upload\", { error });\n setProcessingFailed({ 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 uploadQueue = computed<UploadItem[]>(() => {\n return Object.values(uploadState.value).map((uploadItem) => {\n if (\n uploadItem.status === \"complete\" &&\n unprocessedUploads.has(uploadItem.id)\n ) {\n return {\n id: uploadItem.id,\n name: uploadItem.name,\n parentId: uploadItem.parentId,\n size: uploadItem.size,\n status: \"processing\" as const,\n };\n }\n\n return uploadItem;\n });\n });\n\n const prepareQueueSize = ref(0);\n\n return {\n ...uploadManagerResultRest,\n uploadQueue,\n unprocessedUploads: readonly(unprocessedUploads),\n setProcessingCompleted,\n setProcessingFailed,\n\n isPreparingUpload: computed(() => prepareQueueSize.value > 0),\n totalFilesBeingPrepared: computed(() => prepareQueueSize.value),\n\n start: async (\n parentId: string,\n files: File[],\n options: { isFileWithProcessing?: typeof isKNWFFile } = {},\n ) => {\n const { isFileWithProcessing = isKNWFFile } = options;\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 uploadPayload = await promise.retryPromise({\n fn: () => prepareUpload(parentId, enqueueableFiles),\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 uploadPayload.forEach(({ uploadId, file }) => {\n if (isFileWithProcessing(file)) {\n unprocessedUploads.add(uploadId);\n }\n });\n\n const uploadIds = uploadPayload.map((upload) => upload.uploadId);\n\n // accept floating promise, errors are handled inside the function\n\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 cancelUpload(uploadId).catch((error: unknown) => {\n consola.error(\"There was a problem cancelling the upload\", { error });\n });\n },\n };\n};\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"],"x_google_ignoreList":[14,15,16],"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\": [\"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 { 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 { items, placeholderItems = 0 } = defineProps<UploadProgressPanelProps>();\n\nconst emit = defineEmits<{\n cancel: [item: UploadItem];\n viewDetails: [item: UploadItem];\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 </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 max-height: var(--kds-dimension-component-height-20x);\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 { 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 { items, placeholderItems = 0 } = defineProps<UploadProgressPanelProps>();\n\nconst emit = defineEmits<{\n cancel: [item: UploadItem];\n viewDetails: [item: UploadItem];\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 </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 max-height: var(--kds-dimension-component-height-20x);\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 { formatFileSize, knimeFileFormats, promise } 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\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 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. 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 });\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[]) => {\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 items = enqueueableFiles.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 uploadIds = uploadPayload.map((upload) => upload.uploadId);\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":";;;;;;;;AAOA,IAAa,kBAAkB,YAAmC;CAgBhE,OAAO,EAAE,gBAfc,eAA8B;EACnD,MAAM,cAAsC;GAC1C,YAAY;GACZ,WAAW;EACb;EAEA,IAAI,QAAQ,iBACV,YAAY,SAAS,GAAG,QAAQ;EAGlC,MAAM,YAAY,QAAQ,OAAO,QAAQ,eAAe,KAAA;EAExD,OAAO;GAAE,GAAG;GAAa;EAAU;CACrC,CAES,EAAe;AAC1B;;;;;;;;;;;;;;;;;;;;;;;;;ECbA,MAAM,OAAO;EAIb,MAAM,aAAa,SAAoB,SAAA,YAAkB;EAEzD,MAAM,EAAE,mBAAmB,eAAe;GACxC,QAAQ,eAAe,WAAW,KAAK;GACvC,iBAAiB;EACnB,CAAC;EACD,MAAM,iBAAiB,MAAM;;uBAI3B,mBA+BM,OAAA,EA/BD,OAAK,eAAA,CAAC,qBAAmB,EAAA,WAAA,CAAuB,WAAA,MAAU,CAAA,CAAA,EAAA,GAAA,CAC7D,mBAuBM,OAvBN,cAuBM,CAAA,gBAAA,gBAtBD,QAAA,QAAQ,IAAG,KACd,CAAA,GAAA,mBAoBM,OApBN,cAoBM,CAnBJ,YASE,MAAA,SAAA,GAAA;IARA,MAAK;IACL,SAAQ;IACR,gBAAa;IACZ,iBAAe,WAAA;IACf,iBAAe,MAAA,cAAA;IACf,WAAW,WAAA,QAAU,aAAA;IACrB,OAAK,eAAE,MAAA,cAAA,CAAc;IACrB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,WAAA,QAAU,CAAI,WAAA;;;;;;OAIhB,QAAA,kBAAA,UAAA,GADR,YAOE,MAAA,SAAA,GAAA;;IALA,MAAK;IACL,SAAQ;IACR,WAAU;IACV,gBAAa;IACZ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;2CAKlB,YAIa,YAAA,EAJD,MAAK,SAAQ,GAAA;2BAGjB,CAAA,eAFN,mBAEM,OAAA;KAFoB,IAAI,MAAA,cAAA;KAAgB,OAAM;QAClD,WAAQ,KAAA,QAAA,WAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,GAAA,GAAA,YAAA,GAAA,CAAA,CAAA,OADG,WAAA,KAAU,CAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBErC3B,mBAsCM,OAtCN,cAsCM,CArCJ,mBAgCM,OAAA,EAhCA,OAAK,eAAA,CAAA,iBAAA,EAAA,QAA8B,QAAA,aAAa,KAAA,EAAS,CAAA,CAAA,EAAA,GAAA,CAC7D,mBAWM,OAXN,cAWM,CAVJ,YAAwB,MAAA,OAAA,GAAA,EAAd,MAAM,QAAA,KAAI,GAAA,MAAA,GAAA,CAAA,MAAA,CAAA,GAEpB,mBAOM,OAPN,YAOM,CAAA,gBAAA,UAAA,GANJ,mBAEM,OAFN,YAEM,CAAA,gBAAA,gBADD,QAAA,QAAQ,GAAA,CAAA,CAAA,CAAA,IAAA,CAAA;;IADiB,QAAA;IAAX;GAAS,CAAA,CAAA,GAGhB,QAAA,YAAA,UAAA,GAAZ,mBAEO,QAFP,YAEO,gBADF,QAAA,QAAQ,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,CAAA,CAAA,GAKjB,mBAiBM,OAjBN,YAiBM,CAfI,QAAA,SAAA,UAAA,GADR,YAME,MAAA,QAAA,GAAA;;IAJC,SAAS,QAAA,MAAM;IACf,OAAO,QAAA,MAAM;IACb,gBAAc,QAAA,MAAM;IACrB,MAAK;;;;;yDAGP,mBAOW,UAAA,MAAA,WAPgB,QAAA,QAAQ,MAAK,GAAA,CAAA,IAAvB,WAAM;wBACrB,YAKE,MAAA,SAAA,GAAA;UANkD,OAAO;KAExD,gBAAc,OAAO;KACrB,WAAW,OAAO;KACnB,SAAQ;KACP,SAAO,OAAO;;;;;;4BAML,QAAA,aAAQ,YAAA,UAAA,GAA1B,mBAEM,OAFN,YAEM,CADJ,YAAuD,MAAA,cAAA,GAAA;IAAtC,YAAY,QAAA;IAAU,MAAK;;;;;;;;;CElD3C,OAAM;CAAgB,UAAS;;;qBAApC,mBAEM,OAFN,cAEM,CADJ,WAAQ,KAAA,QAAA,WAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,CAAA;;;;;;;;;;ECeZ,MAAM,QAAQ;EAEd,MAAM,OAAO;EAKb,MAAM,eAAe;GACnB,aAAa;IAAC;IAAW;IAAQ;GAAQ;GACzC,OAAO;IAAC;IAAS;IAAW;GAAa;GACzC,WAAW;IAAC;IAAa;IAAS;GAAc;GAChD,QAAQ;IAAC;IAAU;IAAS;GAAc;EAC5C;EAKA,MAAM,QAAQ,eAAkC;GAC9C,MAAM,CAAC,MAAM,SAAS,QAAQ,aAAa,MAAM,KAAK;GAEtD,OAAO;IACL;IACA;IACA;GACF;EACF,CAAC;EAED,MAAM,gBAAgB,eAAe,MAAM,KAAK,WAAW,aAAa;EACxE,MAAM,UAAU,eAAe,MAAM,KAAK,WAAW,OAAO;EAE5D,MAAM,UAAU,eAA0C;GACxD,MAAM,UAAgC,CAAC;GAEvC,IAAI,cAAc,OAChB,QAAQ,KAAK;IACX,IAAI;IACJ,MAAM;IACN,OAAO;IACP,eAAe,KAAK,QAAQ;GAC9B,CAAC;GAGH,IAAI,QAAQ,OACV,QAAQ,KAAK;IACX,IAAI;IACJ,MAAM;IACN,OAAO;IACP,eAAe,KAAK,UAAU;GAChC,CAAC;GAGH,OAAO;EACT,CAAC;;uBAIC,YAME,MAAA,oBAAA,GAAA;IALC,IAAI,QAAA,KAAK;IACV,MAAK;IACJ,UAAU,QAAA,KAAK;IACf,OAAO,MAAA;IACP,SAAS,QAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEhEd,MAAM,QAAQ;EAEd,MAAM,OAAO;EAMb,MAAM,WAAW,SAAoB,SAAC,UAA6B;EAEnE,MAAM,aAAa,eAAe,MAAM,MAAM,MAAM;EACpD,MAAM,uBAAuB,eACrB,MAAM,MAAM,QAAQ,EAAE,aAAa,WAAW,OAAO,CAAC,CAAC,MAC/D;EAEA,MAAM,WAAW,eAAe;GAC9B,MAAM,SAAS,OAAO,WAAW,QAAQ,IAAI,MAAM;GACnD,OAAO,cAAc,qBAAqB,MAAM,MAAM,WAAW,MAAM,GAAG;EAC5E,CAAC;EAED,MAAM,qBAAqB,eACzB,MAAM,MAAM,MAAM,EAAE,aAAa,WAAW,aAAa,CAC3D;EAEA,YACQ,MAAM,OAAO,SAClB,UAAU,aAAa;GACtB,IAAI,WAAW,KAAK,CAAC,UACnB,KAAK,OAAO;EAEhB,CACF;;uBAIE,YAkBmB,MAAA,wBAAA,GAAA;gBAjBR,SAAA;0EAAQ,QAAA;IAChB,UAAU,SAAA;IACX,OAAM;IACL,oBAAgB,CAAG,mBAAA;IACnB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;2BAYN,CAVN,mBAUM,OAVN,cAUM,CATJ,YAQe,MAAA,oBAAA,GAAA,MAAA;4BANU,EAAA,UAAA,IAAA,GADvB,mBAME,UAAA,MAAA,WALe,QAAA,QAAR,SAAI;0BADb,YAME,mCAAA;OAJC,KAAK,KAAK;OACJ;OACN,WAAM,WAAE,KAAI,UAAW,IAAI;OAC3B,aAAQ,WAAE,KAAI,YAAa,IAAI;;;;;;;;;;;;;;;;;;;;AErD1C,IAAM,sBAAsB;AAC5B,IAAM,2BAA2B;AAoBjC,IAAM,kBACJ,aACwC;CACxC,OAAO,iBAAiB;AAC1B;AA+BA,IAAM,gBAAgB,UACpB,iBAAiB,gBAAgB,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;AAwBlD,IAAa,uBACX,UAAsC,CAAC,MACpC;CACH,MAAM,UAAU,iBAAiB,QAAQ,wBAAwB;CAEjE,MAAM,gBAAgB,IAAkC,CAAC,CAAC;CAC1D,MAAM,mBAAoD,CAAC;CAG3D,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,kBAAkB,QAAQ,mBAAmB;CAEnD,MAAM,UAAU,eAAuB;EACrC,MAAM,iBAAiB,iBAAiB;EACxC,IAAI,gBAAgB;GAClB,eAAe,MAAM;GACrB,OAAO,iBAAiB;EAC1B;CACF;CAEA,MAAM,cAAc,eAAuB;EACzC,OAAO,UAAU;EACjB,OAAO,cAAc,MAAM;CAC7B;CAEA,MAAM,uBAAuB,EAC3B,YACA,aAII;EACJ,OAAO,QAAgC,cAAc,WAAW,UAAU;GACxE,QAAQ;GACR;EACF,CAAC;CACH;CAEA,MAAM,gBAAgB,gBAAwB;EAC5C,MAAM,IAAI,SAAS,cAAc,GAAG;EACpC,EAAE,SAAS;EACX,EAAE,OAAO;EACT,EAAE,WAAW;EACb,EAAE,MAAM,UAAU;EAClB,SAAS,KAAK,YAAY,CAAC;EAC3B,EAAE,MAAM;EACR,SAAS,KAAK,YAAY,CAAC;CAC7B;;;;;;;;;CAUA,MAAM,sBAAsB,OAAO,EACjC,YACA,aAII;EACJ,IAAI;GACF,MAAM,iBAAiB,MAAM,oBAAoB;IAC/C;IACA;GACF,CAAC;GAED,IAAI,gBAAgB,WAAW,WAAW,gBAAgB,aAAa;IACrE,cAAc,MAAM,WAAW,CAAC,SAAS;IACzC,cAAc,MAAM,WAAW,CAAC,cAC9B,eAAe;IAEjB,aAAa,eAAe,WAAW;IACvC,OAAO;GACT,OAAO,IAAI,gBAAgB,WAAW,UAAU;IAC9C,IAAI,UAAU;IACd,IAAI,gBAAgB,eAClB,WAAW,KAAK,eAAe;IAEjC,MAAM,IAAI,MAAM,OAAO;GACzB;EACF,SAAS,OAAgB;GACvB,QAAQ,MAAM,0BAA0B,KAAK;GAM7C,IAAI,EAHF,aAAa,KAAK,KACjB,iBAAiB,cAAc,aAAa,MAAM,KAAK,IAE1C;IACd,cAAc,MAAM,WAAW,CAAC,SAAS;IACzC,cAAc,MAAM,WAAW,CAAC,iBAC9B,iBAAiB,QAAQ,MAAM,UAAW;GAC9C;GACA,OAAO;EACT;EACA,OAAO;CACT;CAEA,MAAM,mBAAmB,OAAO,EAC9B,QACA,YACA,SACA,WAMI;EACJ,MAAM,aAAa,IAAI,gBAAgB;EACvC,iBAAiB,cAAc;EAE/B,MAAM,SAAS,WAAW;EAE1B,IAAI,WAAW,GAAG;EAClB,IAAI,SACF,YAAY,KAAK,QAAQ;EAG3B,cAAc,MAAM,cAAc;GAChC;GACA,GAAI,WAAW,EAAE,QAAQ;GACzB;GACA,MAAM;GACN,QAAQ;EACV;EAEA,MAAM,sBAAsB;GAC1B,WAAW,OAAO,oBAAoB,SAAS,aAAa;GAC5D,IAAI,CAAC,cAAc,MAAM,aACvB;GAEF,cAAc,MAAM,WAAW,CAAC,SAAS;GACzC,cAAc,MAAM,WAAW,CAAC,iBAC9B;EACJ;EACA,WAAW,OAAO,iBAAiB,SAAS,aAAa;EAEzD,IAAI,QAAQ;EAEZ,OAAO,eAAe,QAAQ,QAAQ,YAAY;GAChD;GACA,MAAM,MAAM,eAAe;GAI3B,IAAI,MAF0B,oBAAoB;IAAE;IAAY;GAAO,CAAC,GAGtE;EAEJ;EAEA,OAAO,iBAAiB;EAExB,IAAI,eAAe,QAAQ,SAAS,YAAY;GAC9C,cAAc,MAAM,WAAW,CAAC,SAAS;GACzC,cAAc,MAAM,WAAW,CAAC,iBAAiB;EACnD;CACF;CAEA,MAAM,QAAQ,OAAO,EACnB,QACA,SACA,WAKI;EACJ,IAAI;GACF,MAAM,WAAW,MAAM,QACrB,eAAe,OAAO,YACtB;IACE,QAAQ;IACR,OAAO,EAAE,QAAQ;GACnB,CACF;GAGA,IAAI,eAAe,QAAQ,GAAG;IAC5B,aAAa,SAAS,WAAW;IACjC;GACF;GAGA,MAAM,iBAAiB;IACrB;IACA;IACA,YAAY,SAAS;IACrB;GACF,CAAC;EACH,SAAS,OAAgB;GAGvB,MAAM,UAAU,SAAS,KAAK,KAAK;EACrC;CACF;CAEA,MAAM,wBAAwB,eAE1B,OAAO,OAAO,cAAc,KAAK,CAAC,CAAC,QAChC,EAAE,aAAa,WAAW,aAC7B,CAAC,CAAC,MACN;CAEA,MAAM,mBAAmB;EACvB,KAAK,MAAM,CAAC,YAAY,eAAe,OAAO,QAAQ,gBAAgB,GAAG;GACvE,YAAY,MAAM;GAClB,OAAO,iBAAiB;EAC1B;EACA,cAAc,QAAQ,CAAC;CACzB;CAIA,OAAO;;;;EAIL;;;;;EAMA;;;;EAKA;;;;EAKA,eAtByB,eAAe,OAAO,OAAO,cAAc,KAAK,CAsB1D;;;;EAKf;;;;EAKA;;;;EAKA;CACF;AACF;;;;;;;;ECnUA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAKb,MAAM,WAAW,eAAe;GAC9B,IAAI,MAAM,KAAK,SAAS,QAMtB,OAAO,GALY,eAChB,MAAM,KAAK,OAAO,OAAQ,MAAM,KAAK,YAAY,EAI1C,EAAW,MADP,eAAe,MAAM,KAAK,IACb;GAG7B,IAAI,MAAM,KAAK,SAAS,aAAa;IACnC,MAAM,QAAQ,MAAM,KAAK;IAGzB,OAAO,GAFY,MAAM,KAAK,gBAET,MAAM,MAAM,OAAO,QAAQ,IAAI,MAAM;GAC5D;GAEA,OAAO;EACT,CAAC;EAED,MAAM,0BAA0B,eAAe;GAC7C,IAAI,MAAM,KAAK,SAAS,QACtB,OAAO,MAAM,KAAK;GAGpB,IAAI,MAAM,KAAK,SAAS,aAMtB,OAFG,MAAM,KAAK,kBAAkB,MAAO,MAAM,KAAK;EAMtD,CAAC;EAED,MAAM,eAAe;GACnB,YAAY;IAAC;IAAa;IAAQ;GAAQ;GAC1C,QAAQ;IAAC;IAAU;IAAS;GAAc;GAC1C,UAAU;IAAC;IAAY;IAAW;GAAgB;GAClD,WAAW;IAAC;IAAa;IAAS;GAAc;GAChD,YAAY;IAAC;IAAc;IAAQ;GAAQ;GAC3C,mBAAmB;IAAC;IAAmB;IAAW;GAAS;EAC7D;EAEA,MAAM,WAAW,eAA4B;GAC3C,IAAI,MAAM,KAAK,SAAS,aACtB,OAAO;GAIT,OADkB,iBAAiB,MAAM,KAAK,IACvC,MAAc,iBAAiB,KAAK,YAAY,aAAa;EACtE,CAAC;EAED,MAAM,QAAQ,eAA8C;GAC1D,IAAI,CAAC,MAAM,KAAK,QACd;GAGF,MAAM,CAAC,MAAM,SAAS,QAAQ,aAAa,MAAM,KAAK;GAEtD,OAAO;IAAE;IAAM;IAAS;GAAK;EAC/B,CAAC;EAED,MAAM,gBAAgB,eAAe;GACnC,IACE,MAAM,KAAK,YAAY,KAAA,KACvB,MAAM,KAAK,QAAQ,gBAAgB,KAAA,GAEnC,OAAO;GAGT,OAAO,MAAM,KAAK,QAAQ;EAC5B,CAAC;EAED,MAAM,yBAAyB,eACvB,MAAM,KAAK,WAAW,gBAAgB,cAAc,KAC5D;EAEA,MAAM,gBAAgB,eAAe;GACnC,IACE,MAAM,KAAK,YAAY,KAAA,KACvB,MAAM,KAAK,QAAQ,gBAAgB,KAAA,GAEnC,OAAO;GAGT,OAAO,MAAM,KAAK,QAAQ;EAC5B,CAAC;EAED,MAAM,8BAA8B,eAC5B,MAAM,KAAK,WAAW,qBAAqB,cAAc,KACjE;EAEA,MAAM,UAAU,eAA0C;GACxD,MAAM,SAA+B,CAAC;GAEtC,IAAI,uBAAuB,OACzB,OAAO,KAAK;IACV,IAAI;IACJ,MAAM;IACN,OAAO;IACP,eAAe,KAAK,QAAQ;GAC9B,CAAC;GAGH,IAAI,4BAA4B,OAC9B,OAAO,KAAK;IACV,IAAI;IACJ,MAAM;IACN,OAAO;IACP,eAAe,KAAK,aAAa;GACnC,CAAC;GAGH,OAAO;EACT,CAAC;;uBAIC,YAQE,MAAA,oBAAA,GAAA;IAPC,IAAI,QAAA,KAAK;IACT,UAAU,QAAA,KAAK;IACf,UAAU,SAAA;IACV,MAAM,SAAA;IACN,UAAU,wBAAA;IACV,OAAO,MAAA;IACP,SAAS,QAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEjJd,MAAM,OAAO;EAMb,MAAM,WAAW,SAAoB,SAAC,UAA6B;EAEnE,MAAM,aAAa,eAAe,QAAA,MAAM,MAAM;EAC9C,MAAM,gBAAgB,eACd,QAAA,MAAM,QAAQ,EAAE,aAAa,WAAW,UAAU,CAAC,CAAC,MAC5D;EAEA,MAAM,WAAW,eAAe;GAC9B,MAAM,SAAS,OAAO,WAAW,QAAQ,IAAI,MAAM;GACnD,OAAO,YAAY,cAAc,MAAM,MAAM,WAAW,MAAM,GAAG;EACnE,CAAC;EAED,MAAM,wBAAwB,eAE1B,QAAA,MAAM,MACH,EAAE,aAAa,WAAW,gBAAgB,WAAW,YACxD,KAAK,QAAA,mBAAmB,CAC5B;;uBAIE,YAgCmB,MAAA,wBAAA,GAAA;gBA/BR,SAAA;0EAAQ,QAAA;IAChB,UAAU,SAAA;IACX,OAAM;IACL,oBAAgB,CAAG,sBAAA;IACnB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;2BA0BN,CAxBN,mBAwBM,OAxBN,YAwBM,CAvBJ,YAQe,MAAA,oBAAA,GAAA,MAAA;4BANU,EAAA,UAAA,IAAA,GADvB,mBAME,UAAA,MAAA,WALe,QAAA,QAAR,SAAI;0BADb,YAME,iCAAA;OAJC,KAAK,KAAK;OACJ;OACN,WAAM,WAAE,KAAI,UAAW,IAAI;OAC3B,gBAAY,WAAE,KAAI,eAAgB,IAAI;;;;;;;;QAI3B,QAAA,mBAAgB,KAAA,UAAA,IAAA,GAC9B,mBAUM,UAAA,EAAA,KAAA,EAAA,GAAA,WATe,QAAA,mBAAZ,aAAQ;yBADjB,mBAUM,OAAA;MARH,KAAK;MACN,OAAM;SAEN,YAA2C,MAAA,kBAAA,GAAA,EAAvB,SAAQ,aAAY,CAAA,GACxC,mBAGM,OAHN,YAGM,CAFJ,YAAsC,MAAA,kBAAA,GAAA,EAAlB,SAAQ,QAAO,CAAA,GACnC,YAAqC,MAAA,kBAAA,GAAA,EAAjB,SAAQ,OAAM,CAAA,CAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;AE3B9C,IAAa,oBAAoB,YAAqC;CACpE,MAAM,EAAE,uBAAuB,GAAG,gBAAgB;CAClD,MAAM,cAAc,IAAgC,CAAC,CAAC;CAEtD,MAAM,EAAE,aAAa,QAAQ,cAAc,oBAAoB;EAC7D,GAAG;EAEH,uBAAuB,WAAW;GAChC,MAAM,EAAE,UAAU,SAAS,YAAY,MAAM,OAAO;GACpD,MAAM,UAAU;IAAE,GAAG;IAAQ;IAAU;GAAK;GAC5C,YAAY,MAAM,OAAO,SAAS,CAAC,SACjC,wBAAwB,OAAO,KAAK;GACtC,QAAQ,uBAAuB,OAAO;EACxC;EAEA,qBAAqB,EAAE,UAAU,YAAY;GAC3C,MAAM,EAAE,aAAa,YAAY,MAAM;GACvC,YAAY,MAAM,YAAY;IAC5B,GAAG,YAAY,MAAM;IACrB,QAAQ;IACR,gBAAgB,MAAM;GACxB;GACA,QAAQ,qBAAqB;IAAE;IAAU;IAAO;GAAS,CAAC;EAC5D;EAEA,aAAa,UAAU,aAAa;GAClC,IAAI,YAAY,MAAM,SAAS,CAAC,WAAW,cACzC,YAAa,MAAM,SAAS,CAAoB,WAAW;EAE/D;CACF,CAAC;CAED,MAAM,QAAQ,OACZ,UACA,kBACG;EACH,IAAI;GACF,cAAc,SAAS,EAAE,UAAU,WAAW;IAC5C,YAAY,MAAM,YAAY;KAC5B,IAAI;KACJ,MAAM,KAAK;KACX,MAAM,KAAK;KACX,UAAU;KACV,MAAM;KACN,QAAQ;KACR;IACF;GACF,CAAC;GAED,OAAO,MAAM,YAAY,aAAa;EACxC,SAAS,OAAO;GACd,QAAQ,MAAM,yBAAyB,KAAK;GAC5C,OAAO,CAAC;EACV;CACF;CAEA,MAAM,aAAa,aACjB,YAAY,MAAM,SAAS,EAAE,WAAW,gBACxC,YAAY,MAAM,SAAS,EAAE,WAAW;CAE1C,MAAM,cAAc,aAAqB;EACvC,IAAI,UAAU,QAAQ,GACpB,OAAO,QAAQ;EAGjB,OAAO,YAAY,MAAM;CAC3B;CAEA,MAAM,mBAAmB;EACvB,YAAY,QAAQ,CAAC;CACvB;CAEA,MAAM,cAAc,eAAe,OAAO,OAAO,YAAY,KAAK,CAAC;CAWnE,OAAO;EACL;EACA,mBAXwB,eACxB,YAAY,MAAM,MAAM,EAAE,aAAa,WAAW,YAAY,CAU9D;EACA,qBAR0B,eAExB,YAAY,MAAM,QAAQ,EAAE,aAAa,WAAW,YAAY,CAAC,CAAC,MAMpE;EACA;EACA,SAAS,aAAqB;GAC5B,OAAO,QAAQ;GACf,YAAY,MAAM,SAAS,CAAC,SAAS;EACvC;EACA;EACA;EACA;EACA,YAAY,UAAkB,UAAiB;GAC7C,YAAY,MAAM,YAAY;IAC5B,GAAG,YAAY,MAAM;IACrB,QAAQ;IACR,gBAAgB,MAAM;GACxB;GACA,UAAU,QAAQ;GAClB,MAAM,EAAE,aAAa,YAAY,MAAM;GACvC,QAAQ,qBAAqB;IAAE;IAAU;IAAU;GAAM,CAAC;EAC5D;CACF;AACF;;;ACtIA,IAAM,gCAAgC;AAEtC,IAAM,kCAAkC,IAAI,OAAO,OAAO;AAE1D,IAAM,yBAAyB;AAK/B,IAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiGtC,IAAa,iBAAiB,UAAgC,CAAC,MAAM;CACnE,MAAM,eAAe,mBAAmB,EACtC,0BAA0B,QAAQ,yBACpC,CAAC;CAED,IAAI,yBAAqE;CAEzE,MAAM,oBAAoB,aAAqB;EAC7C,MAAM,EAAE,gBAAgB;EAExB,IAAI,CAAC,YAAY,MAAM,WAAW;GAChC,QAAQ,KACN,wCAAwC,SAAS,gBACnD;GACA;EACF;EAEA,OAAO,YAAY,MAAM;CAC3B;CAEA,MAAM,qBAAqB,UAAkB,SAA8B;EACzE,MAAM,EAAE,gBAAgB;EAExB,IAAI,CAAC,YAAY,MAAM,WAAW;GAChC,QAAQ,KACN,2CAA2C,SAAS,gBACtD;GACA;EACF;EAEA,YAAY,QAAQ;GAClB,GAAG,YAAY;IACd,WAAW;IACV,GAAG,YAAY,MAAM;IACrB,GAAG;GACL;EACF;CACF;CAEA,MAAM,eAAe,yBAAyB,cAAc;EAC1D,aAAa,aACX,kBAAkB,UAAU,EAAE,QAAQ,WAAW,CAAC;EAEpD,SAAS,UAAU,kBACjB,kBAAkB,UAAU;GAC1B,QAAQ;GACR,gBAAgB;EAClB,CAAC;CACL,CAAC;CAED,yBAAyB,iBAAiB;EACxC,0BAA0B,aAAa;EAEvC,wBAAwB,EAAE,WACxB,QAAQ,uBAAuB,IAAI,IAAI,eAAe;EAExD,uBAAuB,EAAE,UAAU,aAAa,MAAM,eAAe;GACnE,gBACG,aAAa,EACZ,UAAU,aAAa,eAAe,UAAU,WAAW,EAC7D,CAAC,CAAC,CACD,WAAW;IACV,IAAI,QAAQ,uBAAuB,IAAI,GACrC,aAAa,MAAM,QAAQ;IAG7B,QAAQ,uBAAuB;KAC7B;KACA;KACA;KACA;IACF,CAAC;GACH,CAAC,CAAC,CACD,OAAO,UAAU;IAChB,QAAQ,MAAM,uCAAuC,EAAE,MAAM,CAAC;IAC9D,uBAAwB,UAAU,UAAU,KAAK;GACnD,CAAC;EACL;EAEA,qBAAqB,EAAE,UAAU,OAAO,eAAe;GACrD,SAAS,qBAAqB;IAAE;IAAU;IAAO;GAAS,CAAC;EAC7D;CACF,CAAC;CAED,MAAM,uBAAuB,UAA0B;EACrD,MAAM,EAAE,wBAAwB;EAEhC,MAAM,EAAE,qBAAqB,kCAAkC;EAE/D,IAAI,MAAM,SAAS,oBAAoB,QAAQ,oBAC7C,QAAQ,4BAA4B,kBAAkB;EAIxD,MAAM,qBAAqB,KAAK,IAC9B,GACA,qBAAqB,oBAAoB,KAC3C;EACA,OAAO,MAAM,MAAM,GAAG,kBAAkB;CAC1C;CAEA,MAAM,EAAE,aAAa,GAAG,4BAA4B;CAEpD,MAAM,mBAAmB,IAAI,CAAC;CAE9B,MAAM,cAAc,eAClB,OAAO,OAAO,YAAY,KAAK,CACjC;CAEA,OAAO;EACL,GAAG;EAEH;EACA;EACA;EAEA,mBAAmB,eAAe,iBAAiB,QAAQ,CAAC;EAC5D,yBAAyB,eAAe,iBAAiB,KAAK;EAE9D,OAAO,OAAO,UAAkB,UAAkB;GAChD,MAAM,mBAAmB,oBAAoB,KAAK;GAClD,MAAM,uBAAuB,wBAAwB;GACrD,MAAM,iBAAiB,iBAAiB,QACrC,SAAS,KAAK,OAAO,oBACxB;GAEA,IAAI,eAAe,SAAS,GAC1B,MAAM,IAAI,UAAU,SAAS;IAC3B,OAAO;IACP,QAAQ;IACR,SAAS,eAAe,KACrB,SACC,GAAG,KAAK,KAAK,IAAI,eAAe,KAAK,IAAI,EAAE,gCAAgC,eAAe,oBAAoB,EAAE,EACpH;GACF,CAAC;GAGH,IAAI;IACF,IAAI,iBAAiB,WAAW,GAC9B,OAAO,CAAC;IAGV,iBAAiB,SAAS,iBAAiB;IAE3C,MAAM,QAAQ,iBAAiB,KAAK,UAAU;KAI5C,MAAM,iBAAiB,KAAK,iBAAiB,MAAM,KAAK,IAAI;KAC5D;IACF,EAAE;IAEF,MAAM,gBAAgB,MAAM,gBAAQ,aAAa;KAC/C,UAAU,aAAa,cAAc,UAAU,KAAK;KACpD,eAAe,UAEb,QAAQ,MAAM,cAAc,MAAM,aAAa,GAAG;KACpD,cAAc;IAChB,CAAC;IAED,MAAM,YAAY,cAAc,KAAK,WAAW,OAAO,QAAQ;IAG/D,uBAAuB,MAAM,UAAU,aAAa;IAEpD,OAAO;GACT,SAAS,OAAO;IACd,MAAM,UAAU,SAAS,KAAK,KAAK;GACrC,UAAU;IAER,iBAAiB,SAAS,iBAAiB;GAC7C;EACF;EAEA,SAAS,aAAqB;GAC5B,uBAAuB,OAAO,QAAQ;GACtC,aAAa,aAAa,QAAQ,CAAC,CAAC,OAAO,UAAmB;IAC5D,QAAQ,MAAM,6CAA6C,EAAE,MAAM,CAAC;GACtE,CAAC;EACH;CACF;AACF"}
@@ -0,0 +1,2 @@
1
+ export * from './upload/core';
2
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../src/dataTransfer/core.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
@@ -1,7 +1,6 @@
1
- export { default as UploadProgressPanel } from './upload/ui/UploadProgressPanel.vue';
2
1
  export { default as DownloadProgressPanel } from './download/ui/DownloadProgressPanel.vue';
3
2
  export * from './shared/ui';
4
- export * from './upload/useFileUpload';
5
3
  export * from './download/useDownloadArtifact';
4
+ export * from './upload';
6
5
  export * from './shared/httpClient';
7
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dataTransfer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAC3F,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dataTransfer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAC3F,cAAc,aAAa,CAAC;AAC5B,cAAc,gCAAgC,CAAC;AAC/C,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC"}
@@ -8,10 +8,12 @@ declare const __VLS_export: import('vue').DefineComponent<__VLS_PublicProps, {},
8
8
  close: () => any;
9
9
  cancel: (item: UploadItem) => any;
10
10
  "update:expanded": (value: boolean) => any;
11
+ viewDetails: (item: UploadItem) => any;
11
12
  }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
12
13
  onClose?: (() => any) | undefined;
13
14
  onCancel?: ((item: UploadItem) => any) | undefined;
14
15
  "onUpdate:expanded"?: ((value: boolean) => any) | undefined;
16
+ onViewDetails?: ((item: UploadItem) => any) | undefined;
15
17
  }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
16
18
  declare const _default: typeof __VLS_export;
17
19
  export default _default;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UploadProgressPanel.vue.d.ts","sourceRoot":"","sources":["../../../../src/dataTransfer/upload/UploadProgressPanel.vue"],"names":[],"mappings":"AA8GA,OAAO,KAAK,EAAE,UAAU,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAEpE,KAAK,WAAW,GAAG,wBAAwB,CAAC;AAkC5C,KAAK,gBAAgB,GAAG;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAKF,KAAK,iBAAiB,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAqKxD,QAAA,MAAM,YAAY;;;;;;;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -1,12 +1,13 @@
1
1
  import { UploadItem } from './types';
2
2
  type Props = {
3
3
  item: UploadItem;
4
- allowCancel: boolean;
5
4
  };
6
5
  declare const __VLS_export: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
7
6
  cancel: () => any;
7
+ viewDetails: () => any;
8
8
  }, string, import('vue').PublicProps, Readonly<Props> & Readonly<{
9
9
  onCancel?: (() => any) | undefined;
10
+ onViewDetails?: (() => any) | undefined;
10
11
  }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
11
12
  declare const _default: typeof __VLS_export;
12
13
  export default _default;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UploadProgressPanelItem.vue.d.ts","sourceRoot":"","sources":["../../../../src/dataTransfer/upload/UploadProgressPanelItem.vue"],"names":[],"mappings":"AAkLA,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,SAAS,CAAC;AAE5D,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAsLF,QAAA,MAAM,YAAY;;;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -0,0 +1,3 @@
1
+ export * from './uploadManager';
2
+ export * from './uploadClient';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/dataTransfer/upload/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { UploadClient } from './uploadClient';
2
+ type Options = {
3
+ /** Called once a tracked upload's processing reaches a terminal, successful state. */
4
+ onComplete: (uploadId: string) => void;
5
+ /** Called once a tracked upload's processing reaches a terminal, failed/aborted state. */
6
+ onFail: (uploadId: string, statusMessage: string) => void;
7
+ /** Delay between polling requests. Defaults to 1500ms. */
8
+ pollIntervalMs?: number;
9
+ pollTimeout?: number;
10
+ };
11
+ export declare function createUploadStatusPoller(uploadClient: UploadClient, options: Options): {
12
+ /**
13
+ * Tracks an id for polling. Will start the polling requests immediately unless indicated otherwise
14
+ * by the `startPolling` parameter.
15
+ *
16
+ * Adding newer ids to an already-running poller is not a problem, since new ids just
17
+ * increase the poll batch size upon the next execution of the polling request
18
+ *
19
+ * @param uploadId
20
+ * @param startPolling
21
+ */
22
+ track: (uploadId: string, startPolling?: boolean) => Promise<void>;
23
+ stop: () => void;
24
+ };
25
+ export {};
26
+ //# sourceMappingURL=createUploadStatusPoller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createUploadStatusPoller.d.ts","sourceRoot":"","sources":["../../../../../../src/dataTransfer/upload/core/uploadClient/createUploadStatusPoller.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQnD,KAAK,OAAO,GAAG;IACb,sFAAsF;IACtF,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,0FAA0F;IAC1F,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,OAAO;IAqEd;;;;;;;;;OASG;sBACe,MAAM;;EAY3B"}
@@ -0,0 +1,3 @@
1
+ export * from './uploadClient';
2
+ export * from './createUploadStatusPoller';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/dataTransfer/upload/core/uploadClient/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC"}