@uploadista/vue 0.1.4-beta.1 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/UploadistaProvider-DU4YVCy4.mjs +2 -0
  2. package/dist/UploadistaProvider-DU4YVCy4.mjs.map +1 -0
  3. package/dist/{index-CV58aghD.d.mts → UploadistaProvider.vue-_qDn5Gvp.d.mts} +5 -5
  4. package/dist/UploadistaProvider.vue-_qDn5Gvp.d.mts.map +1 -0
  5. package/dist/components/index.d.mts +2 -2
  6. package/dist/components/index.mjs +1 -1
  7. package/dist/composables/index.d.mts +2 -2
  8. package/dist/composables/index.mjs +1 -1
  9. package/dist/{index-CaAIl5mm.d.mts → index-CxdtdMrD.d.mts} +46 -46
  10. package/dist/{index-CaAIl5mm.d.mts.map → index-CxdtdMrD.d.mts.map} +1 -1
  11. package/dist/index.d.mts +6 -3
  12. package/dist/index.mjs +1 -1
  13. package/dist/is-browser-file-CN7ymiO5.mjs +2 -0
  14. package/dist/is-browser-file-CN7ymiO5.mjs.map +1 -0
  15. package/dist/providers/index.d.mts +1 -1
  16. package/dist/providers/index.mjs +1 -1
  17. package/dist/upload-BJHmqKbp.mjs +2 -0
  18. package/dist/upload-BJHmqKbp.mjs.map +1 -0
  19. package/dist/{components-BFhWuoiw.css → upload-y0K2veej.css} +1 -1
  20. package/dist/upload-y0K2veej.css.map +1 -0
  21. package/dist/{composables-DEuPeeIk.mjs → useUploadMetrics-CJesoLV7.mjs} +1 -1
  22. package/dist/useUploadMetrics-CJesoLV7.mjs.map +1 -0
  23. package/dist/{index-ZaF-lpSA.d.mts → useUploadMetrics-DEdjOjLp.d.mts} +13 -13
  24. package/dist/useUploadMetrics-DEdjOjLp.d.mts.map +1 -0
  25. package/dist/utils/index.mjs +2 -1
  26. package/dist/utils/index.mjs.map +1 -0
  27. package/package.json +6 -6
  28. package/dist/components-BFhWuoiw.css.map +0 -1
  29. package/dist/components-RbGygyCI.mjs +0 -2
  30. package/dist/components-RbGygyCI.mjs.map +0 -1
  31. package/dist/composables-DEuPeeIk.mjs.map +0 -1
  32. package/dist/index-CV58aghD.d.mts.map +0 -1
  33. package/dist/index-ZaF-lpSA.d.mts.map +0 -1
  34. package/dist/providers-BZSbSDJz.mjs +0 -2
  35. package/dist/providers-BZSbSDJz.mjs.map +0 -1
  36. package/dist/utils-kqjsMS_C.mjs +0 -2
  37. package/dist/utils-kqjsMS_C.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"components-RbGygyCI.mjs","names":["$attrs","$attrs","$attrs","$attrs","$attrs","$attrs"],"sources":["../src/components/FlowUploadList.vue","../src/components/flow/Flow.vue","../src/components/flow/useFlowContext.ts","../src/components/flow/FlowCancel.vue","../src/components/flow/FlowPause.vue","../src/components/flow/FlowResume.vue","../src/components/flow/FlowDropZone.vue","../src/components/flow/FlowError.vue","../src/components/flow/FlowInput.vue","../src/components/flow/FlowInputDropZone.vue","../src/components/flow/FlowInputPreview.vue","../src/components/flow/FlowInputs.vue","../src/components/flow/FlowInputUrlField.vue","../src/components/flow/FlowProgress.vue","../src/components/flow/FlowReset.vue","../src/components/flow/FlowStatus.vue","../src/components/flow/FlowSubmit.vue","../src/components/UploadList.vue","../src/components/UploadZone.vue","../src/components/upload/useUploadContext.ts","../src/components/upload/Upload.vue","../src/components/upload/UploadCancel.vue","../src/components/upload/UploadClearCompleted.vue","../src/components/upload/UploadDropZone.vue","../src/components/upload/UploadError.vue","../src/components/upload/UploadItem.vue","../src/components/upload/UploadItems.vue","../src/components/upload/UploadProgress.vue","../src/components/upload/UploadReset.vue","../src/components/upload/UploadRetry.vue","../src/components/upload/UploadStartAll.vue","../src/components/upload/UploadStatus.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * FlowUploadList - Display list of flow uploads with processing status\n *\n * Shows the progress and processing status of files being uploaded through flow pipelines.\n * Supports filtering, sorting, and status-based grouping. Provides flexible slot-based\n * customization for rendering each flow upload item.\n *\n * @component\n * @example\n * // Basic flow upload list\n * <FlowUploadList :uploads=\"flowUploads\" />\n *\n * @example\n * // Custom item rendering with flow status\n * <FlowUploadList :uploads=\"flowUploads\">\n * <template #item=\"{ item, isSuccess, isError, isUploading }\">\n * <div class=\"flow-item\">\n * <span>{{ item.filename }}</span>\n * <progress :value=\"item.uploadProgress\" max=\"100\"></progress>\n * <span v-if=\"isUploading\">Processing...</span>\n * <span v-else-if=\"isSuccess\">Complete</span>\n * <span v-else-if=\"isError\">Failed</span>\n * </div>\n * </template>\n * </FlowUploadList>\n *\n * @example\n * // With status grouping\n * <FlowUploadList :uploads=\"flowUploads\">\n * <template #default=\"{ itemsByStatus }\">\n * <div v-if=\"itemsByStatus.uploading.length\">\n * <h3>Uploading...</h3>\n * <div v-for=\"item of itemsByStatus.uploading\" :key=\"item.id\">\n * {{ item.filename }}\n * </div>\n * </div>\n * </template>\n * </FlowUploadList>\n */\nimport type {\n BrowserUploadInput,\n FlowUploadItem,\n} from \"@uploadista/client-browser\";\nimport type { VNodeChild } from \"vue\";\nimport { computed } from \"vue\";\nimport { isBrowserFile } from \"../utils\";\n\n/**\n * Props for the FlowUploadList component\n * @property {FlowUploadItem[]} uploads - Array of flow upload items to display\n * @property {Function} filter - Optional filter for which items to display\n * @property {Function} sortBy - Optional sorting function for items (a, b) => number\n */\nexport interface FlowUploadListProps {\n /**\n * Array of flow upload items to display\n */\n uploads: FlowUploadItem<BrowserUploadInput>[];\n\n /**\n * Optional filter for which items to display\n */\n filter?: (item: FlowUploadItem<BrowserUploadInput>) => boolean;\n\n /**\n * Optional sorting function for items\n */\n sortBy?: (\n a: FlowUploadItem<BrowserUploadInput>,\n b: FlowUploadItem<BrowserUploadInput>,\n ) => number;\n}\n\nconst props = defineProps<FlowUploadListProps>();\n\ninterface FlowUploadListItemProps {\n item: FlowUploadItem<BrowserUploadInput>;\n index: number;\n isPending: boolean;\n isUploading: boolean;\n isSuccess: boolean;\n isError: boolean;\n isAborted: boolean;\n formatFileSize: (bytes: number) => string;\n}\n\ninterface FlowUploadListDefaultProps {\n items: FlowUploadItem<BrowserUploadInput>[];\n itemsByStatus: {\n pending: FlowUploadItem<BrowserUploadInput>[];\n uploading: FlowUploadItem<BrowserUploadInput>[];\n success: FlowUploadItem<BrowserUploadInput>[];\n error: FlowUploadItem<BrowserUploadInput>[];\n aborted: FlowUploadItem<BrowserUploadInput>[];\n };\n}\n\ndefineSlots<{\n item(props: FlowUploadListItemProps): VNodeChild;\n default(props: FlowUploadListDefaultProps): VNodeChild;\n}>();\n\n// Apply filtering and sorting\nconst filteredItems = computed(() => {\n let items = props.uploads;\n\n if (props.filter) {\n items = items.filter(props.filter);\n }\n\n if (props.sortBy) {\n items = [...items].sort(props.sortBy);\n }\n\n return items;\n});\n\n// Group items by status\nconst itemsByStatus = computed(() => ({\n pending: filteredItems.value.filter((item) => item.status === \"pending\"),\n uploading: filteredItems.value.filter((item) => item.status === \"uploading\"),\n success: filteredItems.value.filter((item) => item.status === \"success\"),\n error: filteredItems.value.filter((item) => item.status === \"error\"),\n aborted: filteredItems.value.filter((item) => item.status === \"aborted\"),\n}));\n\n// Helper function to format file sizes\nconst formatFileSize = (bytes: number): string => {\n if (bytes === 0) return \"0 Bytes\";\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;\n};\n\n// Helper function to get status icon\nconst getStatusIcon = (status: string): string => {\n switch (status) {\n case \"pending\":\n return \"⏳\";\n case \"uploading\":\n return \"📤\";\n case \"success\":\n return \"✅\";\n case \"error\":\n return \"❌\";\n case \"aborted\":\n return \"⏹️\";\n default:\n return \"❓\";\n }\n};\n\n// Helper function to get status color\nconst getStatusColor = (status: string): string => {\n switch (status) {\n case \"pending\":\n return \"#6c757d\";\n case \"uploading\":\n return \"#007bff\";\n case \"success\":\n return \"#28a745\";\n case \"error\":\n return \"#dc3545\";\n case \"aborted\":\n return \"#6c757d\";\n default:\n return \"#6c757d\";\n }\n};\n</script>\n\n<template>\n <div class=\"flow-upload-list\">\n <slot :items=\"filteredItems\" :items-by-status=\"itemsByStatus\">\n <!-- Default rendering: simple list of flow upload items -->\n <div\n v-for=\"(item, index) in filteredItems\"\n :key=\"item.id\"\n class=\"flow-upload-list__item\"\n :class=\"`flow-upload-list__item--${item.status}`\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :index=\"index\"\n :is-pending=\"item.status === 'pending'\"\n :is-uploading=\"item.status === 'uploading'\"\n :is-success=\"item.status === 'success'\"\n :is-error=\"item.status === 'error'\"\n :is-aborted=\"item.status === 'aborted'\"\n :format-file-size=\"formatFileSize\"\n >\n <!-- Default item template -->\n <div class=\"flow-upload-list__item-header\">\n <span class=\"flow-upload-list__item-icon\">\n {{ getStatusIcon(item.status) }}\n </span>\n <span class=\"flow-upload-list__item-name\">\n {{ isBrowserFile(item.file) ? item.file.name : 'File' }}\n </span>\n <span\n class=\"flow-upload-list__item-status\"\n :style=\"{ color: getStatusColor(item.status) }\"\n >\n {{ item.status.toUpperCase() }}\n </span>\n </div>\n\n <div class=\"flow-upload-list__item-details\">\n <span class=\"flow-upload-list__item-size\">\n {{ formatFileSize(item.totalBytes) }}\n </span>\n <span v-if=\"item.jobId\" class=\"flow-upload-list__item-job\">\n Job: {{ item.jobId.slice(0, 8) }}...\n </span>\n </div>\n\n <div v-if=\"item.status === 'uploading'\" class=\"flow-upload-list__item-progress\">\n <div class=\"flow-upload-list__progress-bar\">\n <div\n class=\"flow-upload-list__progress-fill\"\n :style=\"{ width: `${item.progress}%` }\"\n />\n </div>\n <span class=\"flow-upload-list__progress-text\">\n {{ item.progress }}% • {{ formatFileSize(item.bytesUploaded) }} / {{ formatFileSize(item.totalBytes) }}\n </span>\n </div>\n\n <div v-if=\"item.status === 'error' && item.error\" class=\"flow-upload-list__item-error\">\n {{ item.error.message }}\n </div>\n\n <div v-if=\"item.status === 'success'\" class=\"flow-upload-list__item-success\">\n Upload complete\n </div>\n </slot>\n </div>\n </slot>\n </div>\n</template>\n\n<style scoped>\n.flow-upload-list {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.flow-upload-list__item {\n padding: 0.75rem;\n border: 1px solid #e0e0e0;\n border-radius: 0.375rem;\n background-color: #fff;\n}\n\n.flow-upload-list__item-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n}\n\n.flow-upload-list__item-icon {\n font-size: 1rem;\n}\n\n.flow-upload-list__item-name {\n flex: 1;\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.flow-upload-list__item-status {\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n}\n\n.flow-upload-list__item-details {\n display: flex;\n gap: 1rem;\n font-size: 0.75rem;\n color: #666;\n margin-bottom: 0.5rem;\n}\n\n.flow-upload-list__item-size {\n font-weight: 500;\n}\n\n.flow-upload-list__item-job {\n color: #999;\n font-family: monospace;\n}\n\n.flow-upload-list__item-progress {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n\n.flow-upload-list__progress-bar {\n width: 100%;\n height: 0.375rem;\n background-color: #e0e0e0;\n border-radius: 0.1875rem;\n overflow: hidden;\n}\n\n.flow-upload-list__progress-fill {\n height: 100%;\n background-color: #007bff;\n transition: width 0.2s ease;\n}\n\n.flow-upload-list__progress-text {\n font-size: 0.75rem;\n color: #666;\n}\n\n.flow-upload-list__item-error {\n margin-top: 0.5rem;\n padding: 0.5rem;\n background-color: #f8d7da;\n color: #721c24;\n font-size: 0.75rem;\n border-radius: 0.25rem;\n}\n\n.flow-upload-list__item-success {\n margin-top: 0.5rem;\n padding: 0.5rem;\n background-color: #d4edda;\n color: #155724;\n font-size: 0.75rem;\n border-radius: 0.25rem;\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { FlowUploadOptions } from \"@uploadista/client-browser\";\nimport type {\n FlowUploadState,\n FlowUploadStatus,\n InputExecutionState,\n} from \"@uploadista/client-core\";\nimport type { TypedOutput } from \"@uploadista/core/flow\";\nimport { provide } from \"vue\";\nimport {\n type FlowInputMetadata,\n type UseFlowReturn,\n useFlow,\n} from \"../../composables/useFlow\";\n\n/**\n * Props for the Flow root component.\n */\nexport interface FlowProps {\n /** Flow ID to execute */\n flowId: string;\n /** Storage ID for file uploads */\n storageId: string;\n /** Optional output node ID to wait for */\n outputNodeId?: string;\n /** Optional metadata to include with the flow execution */\n metadata?: Record<string, string>;\n}\n\nconst props = defineProps<FlowProps>();\n\nconst emit = defineEmits<{\n /** Called when flow completes successfully */\n success: [outputs: TypedOutput[]];\n /** Called when flow fails */\n error: [error: Error];\n /** Called on upload progress */\n progress: [\n uploadId: string,\n bytesUploaded: number,\n totalBytes: number | null,\n ];\n /** Called when flow completes with all outputs */\n flowComplete: [outputs: TypedOutput[]];\n /** Called when upload is aborted */\n abort: [];\n}>();\n\n// Build options from props and emit handlers\nconst options: FlowUploadOptions = {\n flowConfig: {\n flowId: props.flowId,\n storageId: props.storageId,\n outputNodeId: props.outputNodeId,\n metadata: props.metadata,\n },\n onSuccess: (outputs) => emit(\"success\", outputs),\n onError: (error) => emit(\"error\", error),\n onProgress: (uploadId, bytesUploaded, totalBytes) =>\n emit(\"progress\", uploadId, bytesUploaded, totalBytes),\n onFlowComplete: (outputs) => emit(\"flowComplete\", outputs),\n onAbort: () => emit(\"abort\"),\n};\n\nconst flow = useFlow(options);\n\n// Re-export types for convenience\nexport type {\n FlowUploadState,\n FlowUploadStatus,\n InputExecutionState,\n FlowInputMetadata,\n};\n\n/**\n * Context value provided by the Flow component root.\n * Contains all flow state and actions.\n */\nexport interface FlowContextValue {\n /** Current upload state */\n state: UseFlowReturn[\"state\"];\n /** Discovered input nodes metadata (null until discovery completes) */\n inputMetadata: UseFlowReturn[\"inputMetadata\"];\n /** Current input values set via setInput() */\n inputs: UseFlowReturn[\"inputs\"];\n /** Per-input execution state for multi-input flows */\n inputStates: UseFlowReturn[\"inputStates\"];\n\n /** Set an input value for a specific node */\n setInput: UseFlowReturn[\"setInput\"];\n /** Execute the flow with current inputs */\n execute: UseFlowReturn[\"execute\"];\n /** Upload a single file through the flow */\n upload: UseFlowReturn[\"upload\"];\n /** Abort the current upload */\n abort: UseFlowReturn[\"abort\"];\n /** Pause the current upload */\n pause: UseFlowReturn[\"pause\"];\n /** Resume a paused upload */\n resume: UseFlowReturn[\"resume\"];\n /** Reset the upload state and clear all inputs */\n reset: UseFlowReturn[\"reset\"];\n\n /** Whether an upload or flow execution is in progress */\n isUploading: UseFlowReturn[\"isUploading\"];\n /** Whether the file is currently being uploaded */\n isUploadingFile: UseFlowReturn[\"isUploadingFile\"];\n /** Whether the flow is currently processing */\n isProcessing: UseFlowReturn[\"isProcessing\"];\n /** Whether the hook is discovering flow inputs */\n isDiscoveringInputs: UseFlowReturn[\"isDiscoveringInputs\"];\n}\n\n// Create the context value\nconst contextValue: FlowContextValue = {\n state: flow.state,\n inputMetadata: flow.inputMetadata,\n inputs: flow.inputs,\n inputStates: flow.inputStates,\n setInput: flow.setInput,\n execute: flow.execute,\n upload: flow.upload,\n abort: flow.abort,\n pause: flow.pause,\n resume: flow.resume,\n reset: flow.reset,\n isUploading: flow.isUploading,\n isUploadingFile: flow.isUploadingFile,\n isProcessing: flow.isProcessing,\n isDiscoveringInputs: flow.isDiscoveringInputs,\n};\n\n// Provide context for child components\nprovide(\"flowContext\", contextValue);\n\n// Also expose to parent via defineExpose for programmatic access\ndefineExpose(contextValue);\n</script>\n\n<template>\n <slot />\n</template>\n","import { inject } from \"vue\";\nimport type { FlowContextValue } from \"./Flow.vue\";\n\n/**\n * Injection key for the Flow context\n */\nexport const FLOW_CONTEXT_KEY = \"flowContext\";\n\n/**\n * Injection key for the FlowInput context\n */\nexport const FLOW_INPUT_CONTEXT_KEY = \"flowInputContext\";\n\n/**\n * Context value for a specific input node within a Flow.\n */\nexport interface FlowInputContextValue {\n /** Input node ID */\n nodeId: string;\n /** Input metadata from flow discovery */\n metadata: {\n nodeId: string;\n nodeName: string;\n nodeDescription: string;\n inputTypeId?: string;\n required: boolean;\n };\n /** Current value for this input */\n value: unknown;\n /** Set the value for this input */\n setValue: (value: unknown) => void;\n /** Per-input execution state (if available) */\n state:\n | {\n status: string;\n progress: number;\n error: Error | null;\n }\n | undefined;\n}\n\n/**\n * Hook to access flow context from within a Flow component.\n * @throws Error if used outside of a Flow component\n */\nexport function useFlowContext(): FlowContextValue {\n const context = inject<FlowContextValue>(FLOW_CONTEXT_KEY);\n if (!context) {\n throw new Error(\n \"useFlowContext must be used within a <Flow> component. \" +\n 'Wrap your component tree with <Flow flowId=\"...\" storageId=\"...\">',\n );\n }\n return context;\n}\n\n/**\n * Hook to access flow input context from within a FlowInput component.\n * @throws Error if used outside of a FlowInput component\n */\nexport function useFlowInputContext(): FlowInputContextValue {\n const context = inject<FlowInputContextValue>(FLOW_INPUT_CONTEXT_KEY);\n if (!context) {\n throw new Error(\n \"useFlowInputContext must be used within a <FlowInput> component. \" +\n 'Wrap your component with <FlowInput nodeId=\"...\">',\n );\n }\n return context;\n}\n","<script setup lang=\"ts\">\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\nconst handleClick = () => {\n flow.abort();\n};\n</script>\n\n<template>\n <button\n type=\"button\"\n @click=\"handleClick\"\n v-bind=\"$attrs\"\n >\n <slot>Cancel</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\n// Disable attribute inheritance so we can spread them manually\nexport default {\n inheritAttrs: false,\n};\n</script>\n","<script setup lang=\"ts\">\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\nconst handleClick = () => {\n flow.pause();\n};\n</script>\n\n<template>\n <button\n type=\"button\"\n @click=\"handleClick\"\n v-bind=\"$attrs\"\n >\n <slot>Pause</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\n// Disable attribute inheritance so we can spread them manually\nexport default {\n inheritAttrs: false,\n};\n</script>\n","<script setup lang=\"ts\">\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\nconst handleClick = () => {\n flow.resume();\n};\n</script>\n\n<template>\n <button\n type=\"button\"\n @click=\"handleClick\"\n v-bind=\"$attrs\"\n >\n <slot>Resume</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\n// Disable attribute inheritance so we can spread them manually\nexport default {\n inheritAttrs: false,\n};\n</script>\n","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\nimport { type DragDropState, useDragDrop } from \"../../composables/useDragDrop\";\nimport { useFlowContext } from \"./useFlowContext\";\n\n/**\n * Props for FlowDropZone component.\n */\nexport interface FlowDropZoneProps {\n /** Accepted file types (e.g., \"image/*\", \".pdf\") */\n accept?: string;\n /** Maximum file size in bytes */\n maxFileSize?: number;\n}\n\nconst props = withDefaults(defineProps<FlowDropZoneProps>(), {\n accept: undefined,\n maxFileSize: undefined,\n});\n\nconst flow = useFlowContext();\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nconst dragDrop = useDragDrop({\n onFilesReceived: (files) => {\n const file = files[0];\n if (file) {\n flow.upload(file);\n }\n },\n accept: props.accept\n ? props.accept.split(\",\").map((t) => t.trim())\n : undefined,\n maxFileSize: props.maxFileSize,\n multiple: false,\n});\n\nconst openFilePicker = () => {\n inputRef.value?.click();\n};\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowDropZoneSlotProps {\n /** Whether files are being dragged over */\n isDragging: boolean;\n /** Whether drag is over the zone */\n isOver: boolean;\n /** Upload progress (0-100) */\n progress: number;\n /** Current flow status */\n status: string;\n /** Current drag-drop state */\n dragDropState: DragDropState;\n /** Open file picker programmatically */\n openFilePicker: () => void;\n /** Drag event handlers to spread on the container */\n dragHandlers: {\n onDragenter: (e: DragEvent) => void;\n onDragover: (e: DragEvent) => void;\n onDragleave: (e: DragEvent) => void;\n onDrop: (e: DragEvent) => void;\n };\n /** Input props for the hidden file input */\n inputProps: {\n type: \"file\";\n multiple: boolean;\n accept: string | undefined;\n };\n /** Input change handler */\n onInputChange: (e: Event) => void;\n /** Ref for the file input element */\n inputRef: typeof inputRef;\n}\n\nconst slotProps = computed<FlowDropZoneSlotProps>(() => ({\n isDragging: dragDrop.state.value.isDragging,\n isOver: dragDrop.state.value.isOver,\n progress: flow.state.value.progress,\n status: flow.state.value.status,\n dragDropState: dragDrop.state.value,\n openFilePicker,\n dragHandlers: {\n onDragenter: dragDrop.onDragEnter,\n onDragover: dragDrop.onDragOver,\n onDragleave: dragDrop.onDragLeave,\n onDrop: dragDrop.onDrop,\n },\n inputProps: dragDrop.inputProps.value,\n onInputChange: dragDrop.onInputChange,\n inputRef,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default content if no slot provided -->\n <div\n v-bind=\"slotProps.dragHandlers\"\n @click=\"openFilePicker\"\n :style=\"{\n border: slotProps.isDragging ? '2px dashed #3b82f6' : '2px dashed #d1d5db',\n borderRadius: '0.5rem',\n padding: '2rem',\n textAlign: 'center',\n cursor: flow.isUploading.value ? 'not-allowed' : 'pointer',\n opacity: flow.isUploading.value ? 0.5 : 1,\n backgroundColor: slotProps.isOver ? '#eff6ff' : 'transparent',\n transition: 'all 0.2s ease',\n }\"\n >\n <p v-if=\"slotProps.isDragging\">Drop file here...</p>\n <p v-else-if=\"flow.isUploading.value\">Uploading... {{ slotProps.progress }}%</p>\n <p v-else>Drag and drop a file here, or click to select</p>\n </div>\n <input\n ref=\"inputRef\"\n type=\"file\"\n :multiple=\"slotProps.inputProps.multiple\"\n :accept=\"slotProps.inputProps.accept\"\n @change=\"slotProps.onInputChange\"\n style=\"display: none\"\n />\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowErrorSlotProps {\n /** Error object (null if no error) */\n error: Error | null;\n /** Whether there is an error */\n hasError: boolean;\n /** Error message */\n message: string | null;\n /** Reset the flow */\n reset: () => void;\n}\n\nconst slotProps = computed<FlowErrorSlotProps>(() => ({\n error: flow.state.value.error,\n hasError: flow.state.value.status === \"error\",\n message: flow.state.value.error?.message ?? null,\n reset: flow.reset,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default error display -->\n <div\n v-if=\"slotProps.hasError\"\n style=\"padding: 1rem; background: #fef2f2; border: 1px solid #fecaca; border-radius: 0.5rem; color: #dc2626;\"\n >\n <p style=\"margin: 0; font-weight: 600;\">Error</p>\n <p style=\"margin: 0.25rem 0 0; font-size: 0.875rem;\">{{ slotProps.message }}</p>\n <button\n type=\"button\"\n @click=\"slotProps.reset\"\n style=\"margin-top: 0.75rem; padding: 0.5rem 1rem; background: #dc2626; color: white; border: none; border-radius: 0.375rem; cursor: pointer;\"\n >\n Try Again\n </button>\n </div>\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, provide } from \"vue\";\nimport {\n FLOW_INPUT_CONTEXT_KEY,\n type FlowInputContextValue,\n useFlowContext,\n} from \"./useFlowContext\";\n\n/**\n * Props for FlowInput component.\n */\nexport interface FlowInputProps {\n /** Input node ID */\n nodeId: string;\n}\n\nconst props = defineProps<FlowInputProps>();\nconst flow = useFlowContext();\n\n// Find metadata for this input\nconst metadata = computed(() =>\n flow.inputMetadata.value?.find((m) => m.nodeId === props.nodeId),\n);\n\n// Get current value for this input\nconst value = computed(() => flow.inputs.value[props.nodeId]);\n\n// Get execution state for this input\nconst inputState = computed(() => flow.inputStates.value.get(props.nodeId));\n\n// Create setValue function scoped to this input\nconst setValue = (newValue: unknown) => {\n flow.setInput(props.nodeId, newValue);\n};\n\n// Create a context object with getters that access computed refs\n// This ensures reactivity works while also providing stable function references\nconst contextValue: FlowInputContextValue = {\n get nodeId() {\n return props.nodeId;\n },\n get metadata() {\n return (\n metadata.value ?? {\n nodeId: props.nodeId,\n nodeName: \"\",\n nodeDescription: \"\",\n required: false,\n }\n );\n },\n get value() {\n return value.value;\n },\n setValue,\n get state() {\n return inputState.value;\n },\n};\n\n// Provide context for child components (FlowInputDropZone, etc.)\nprovide(FLOW_INPUT_CONTEXT_KEY, contextValue);\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowInputSlotProps {\n /** Input node ID */\n nodeId: string;\n /** Input metadata from flow discovery */\n metadata: FlowInputContextValue[\"metadata\"] | undefined;\n /** Current value for this input */\n value: unknown;\n /** Set the value for this input */\n setValue: (value: unknown) => void;\n /** Per-input execution state (if available) */\n state: FlowInputContextValue[\"state\"];\n}\n\nconst slotProps = computed<FlowInputSlotProps>(() => ({\n nodeId: props.nodeId,\n metadata: metadata.value,\n value: value.value,\n setValue,\n state: inputState.value,\n}));\n</script>\n\n<template>\n <!-- Only render if metadata is found -->\n <slot v-if=\"metadata\" v-bind=\"slotProps\" />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\nimport { type DragDropState, useDragDrop } from \"../../composables/useDragDrop\";\nimport { useFlowInputContext } from \"./useFlowContext\";\n\n// Helper function to check if value is a File (for template use)\nconst isFile = (value: unknown): value is File => value instanceof File;\n\n/**\n * Props for FlowInputDropZone component.\n */\nexport interface FlowInputDropZoneProps {\n /** Accepted file types (e.g., \"image/*\", \".pdf\") */\n accept?: string;\n /** Maximum file size in bytes */\n maxFileSize?: number;\n}\n\nconst props = withDefaults(defineProps<FlowInputDropZoneProps>(), {\n accept: undefined,\n maxFileSize: undefined,\n});\n\nconst input = useFlowInputContext();\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nconst dragDrop = useDragDrop({\n onFilesReceived: (files) => {\n const file = files[0];\n if (file) {\n // Set the input value but don't trigger upload yet\n input.setValue(file);\n }\n },\n accept: props.accept\n ? props.accept.split(\",\").map((t) => t.trim())\n : undefined,\n maxFileSize: props.maxFileSize,\n multiple: false,\n});\n\nconst openFilePicker = () => {\n inputRef.value?.click();\n};\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowInputDropZoneSlotProps {\n /** Whether files are being dragged over */\n isDragging: boolean;\n /** Whether drag is over the zone */\n isOver: boolean;\n /** Current value for this input */\n value: unknown;\n /** Per-input progress (if available) */\n progress: number;\n /** Per-input status (if available) */\n status: string;\n /** Current drag-drop state */\n dragDropState: DragDropState;\n /** Open file picker programmatically */\n openFilePicker: () => void;\n /** Drag event handlers to spread on the container */\n dragHandlers: {\n onDragenter: (e: DragEvent) => void;\n onDragover: (e: DragEvent) => void;\n onDragleave: (e: DragEvent) => void;\n onDrop: (e: DragEvent) => void;\n };\n /** Input props for the hidden file input */\n inputProps: {\n type: \"file\";\n multiple: boolean;\n accept: string | undefined;\n };\n /** Input change handler */\n onInputChange: (e: Event) => void;\n /** Ref for the file input element */\n inputRef: typeof inputRef;\n}\n\nconst slotProps = computed<FlowInputDropZoneSlotProps>(() => ({\n isDragging: dragDrop.state.value.isDragging,\n isOver: dragDrop.state.value.isOver,\n value: input.value,\n progress: input.state?.progress ?? 0,\n status: input.state?.status ?? \"idle\",\n dragDropState: dragDrop.state.value,\n openFilePicker,\n dragHandlers: {\n onDragenter: dragDrop.onDragEnter,\n onDragover: dragDrop.onDragOver,\n onDragleave: dragDrop.onDragLeave,\n onDrop: dragDrop.onDrop,\n },\n inputProps: dragDrop.inputProps.value,\n onInputChange: dragDrop.onInputChange,\n inputRef,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default content if no slot provided -->\n <div\n v-bind=\"slotProps.dragHandlers\"\n @click=\"openFilePicker\"\n :style=\"{\n border: slotProps.isDragging ? '2px dashed #3b82f6' : '2px dashed #d1d5db',\n borderRadius: '0.5rem',\n padding: '2rem',\n textAlign: 'center',\n cursor: 'pointer',\n backgroundColor: slotProps.isOver ? '#eff6ff' : 'transparent',\n transition: 'all 0.2s ease',\n }\"\n >\n <p v-if=\"slotProps.isDragging\">Drop file here...</p>\n <p v-else-if=\"isFile(slotProps.value)\">\n Selected: {{ slotProps.value.name }}\n </p>\n <p v-else>Drag and drop a file here, or click to select</p>\n </div>\n <input\n ref=\"inputRef\"\n type=\"file\"\n :multiple=\"slotProps.inputProps.multiple\"\n :accept=\"slotProps.inputProps.accept\"\n @change=\"slotProps.onInputChange\"\n style=\"display: none\"\n />\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useFlowInputContext } from \"./useFlowContext\";\n\nconst input = useFlowInputContext();\n\nconst isFile = computed(() => input.value instanceof File);\nconst isUrl = computed(\n () => typeof input.value === \"string\" && (input.value as string).length > 0,\n);\n\nconst clear = () => {\n input.setValue(undefined);\n};\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowInputPreviewSlotProps {\n /** Current value */\n value: unknown;\n /** Whether value is a File */\n isFile: boolean;\n /** Whether value is a URL string */\n isUrl: boolean;\n /** File name (if value is File) */\n fileName: string | null;\n /** File size in bytes (if value is File) */\n fileSize: number | null;\n /** Clear the input value */\n clear: () => void;\n}\n\nconst slotProps = computed<FlowInputPreviewSlotProps>(() => ({\n value: input.value,\n isFile: isFile.value,\n isUrl: isUrl.value,\n fileName: isFile.value ? (input.value as File).name : null,\n fileSize: isFile.value ? (input.value as File).size : null,\n clear,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default preview content - only render for File or URL values -->\n <div v-if=\"slotProps.isFile || slotProps.isUrl\" style=\"display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem; background: #f3f4f6; border-radius: 0.375rem;\">\n <div style=\"flex: 1; min-width: 0;\">\n <p v-if=\"slotProps.isFile\" style=\"margin: 0; font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\">\n {{ slotProps.fileName }}\n <span v-if=\"slotProps.fileSize\" style=\"color: #6b7280; margin-left: 0.25rem;\">\n ({{ (slotProps.fileSize / 1024).toFixed(1) }} KB)\n </span>\n </p>\n <p v-else-if=\"slotProps.isUrl\" style=\"margin: 0; font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\">\n {{ slotProps.value }}\n </p>\n </div>\n <button\n type=\"button\"\n @click=\"clear\"\n style=\"padding: 0.25rem 0.5rem; background: transparent; border: none; cursor: pointer; color: #6b7280;\"\n aria-label=\"Clear\"\n >\n &times;\n </button>\n </div>\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport type { FlowInputMetadata } from \"../../composables/useFlow\";\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowInputsSlotProps {\n /** Discovered input metadata */\n inputs: FlowInputMetadata[];\n /** Whether inputs are still being discovered */\n isLoading: boolean;\n}\n\nconst slotProps = computed<FlowInputsSlotProps>(() => ({\n inputs: flow.inputMetadata.value ?? [],\n isLoading: flow.isDiscoveringInputs.value,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default loading state if no slot provided -->\n <div v-if=\"slotProps.isLoading\" style=\"padding: 1rem; text-align: center;\">\n Discovering flow inputs...\n </div>\n <div v-else-if=\"slotProps.inputs.length === 0\" style=\"padding: 1rem; text-align: center; color: #6b7280;\">\n No inputs found for this flow.\n </div>\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useFlowInputContext } from \"./useFlowContext\";\n\n/**\n * Props for FlowInputUrlField component.\n */\nexport interface FlowInputUrlFieldProps {\n /** Placeholder text */\n placeholder?: string;\n}\n\nconst props = withDefaults(defineProps<FlowInputUrlFieldProps>(), {\n placeholder: \"https://example.com/file\",\n});\n\nconst input = useFlowInputContext();\n\n// Check if value is a URL string\nconst isUrl = computed(() => typeof input.value === \"string\");\nconst urlValue = computed(() => (isUrl.value ? (input.value as string) : \"\"));\n\nconst handleInput = (event: Event) => {\n const target = event.target as HTMLInputElement;\n input.setValue(target.value);\n};\n</script>\n\n<template>\n <input\n type=\"url\"\n :value=\"urlValue\"\n @input=\"handleInput\"\n :placeholder=\"placeholder\"\n v-bind=\"$attrs\"\n />\n</template>\n\n<script lang=\"ts\">\n// Disable attribute inheritance so we can spread them manually\nexport default {\n inheritAttrs: false,\n};\n</script>\n","<script setup lang=\"ts\">\nimport type { FlowUploadStatus } from \"@uploadista/client-core\";\nimport { computed } from \"vue\";\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowProgressSlotProps {\n /** Progress percentage (0-100) */\n progress: number;\n /** Bytes uploaded so far */\n bytesUploaded: number;\n /** Total bytes to upload (null if unknown) */\n totalBytes: number | null;\n /** Current status */\n status: FlowUploadStatus;\n}\n\nconst slotProps = computed<FlowProgressSlotProps>(() => ({\n progress: flow.state.value.progress,\n bytesUploaded: flow.state.value.bytesUploaded,\n totalBytes: flow.state.value.totalBytes,\n status: flow.state.value.status,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default progress display -->\n <div v-if=\"slotProps.status === 'uploading' || slotProps.status === 'processing'\" style=\"width: 100%;\">\n <div style=\"display: flex; justify-content: space-between; margin-bottom: 0.25rem; font-size: 0.875rem;\">\n <span>{{ slotProps.status === 'uploading' ? 'Uploading' : 'Processing' }}</span>\n <span>{{ slotProps.progress.toFixed(1) }}%</span>\n </div>\n <div style=\"width: 100%; height: 0.5rem; background: #e5e7eb; border-radius: 0.25rem; overflow: hidden;\">\n <div\n :style=\"{\n width: `${slotProps.progress}%`,\n height: '100%',\n background: '#3b82f6',\n transition: 'width 0.2s ease',\n }\"\n />\n </div>\n <div v-if=\"slotProps.totalBytes\" style=\"margin-top: 0.25rem; font-size: 0.75rem; color: #6b7280;\">\n {{ (slotProps.bytesUploaded / 1024).toFixed(0) }} KB / {{ (slotProps.totalBytes / 1024).toFixed(0) }} KB\n </div>\n </div>\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\nconst handleClick = () => {\n flow.reset();\n};\n</script>\n\n<template>\n <button\n type=\"button\"\n @click=\"handleClick\"\n v-bind=\"$attrs\"\n >\n <slot>Reset</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\n// Disable attribute inheritance so we can spread them manually\nexport default {\n inheritAttrs: false,\n};\n</script>\n","<script setup lang=\"ts\">\nimport type { FlowUploadStatus } from \"@uploadista/client-core\";\nimport type { TypedOutput } from \"@uploadista/core/flow\";\nimport { computed } from \"vue\";\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowStatusSlotProps {\n /** Current status */\n status: FlowUploadStatus;\n /** Current node being processed (if any) */\n currentNodeName: string | null;\n /** Current node type (if any) */\n currentNodeType: string | null;\n /** Error (if status is error) */\n error: Error | null;\n /** Job ID (if started) */\n jobId: string | null;\n /** Whether flow has started */\n flowStarted: boolean;\n /** Flow outputs (if completed) */\n flowOutputs: TypedOutput[] | null;\n}\n\nconst slotProps = computed<FlowStatusSlotProps>(() => ({\n status: flow.state.value.status,\n currentNodeName: flow.state.value.currentNodeName,\n currentNodeType: flow.state.value.currentNodeType,\n error: flow.state.value.error,\n jobId: flow.state.value.jobId,\n flowStarted: flow.state.value.flowStarted,\n flowOutputs: flow.state.value.flowOutputs,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default status display - only show when not idle -->\n <div v-if=\"slotProps.status !== 'idle'\" style=\"padding: 0.5rem;\">\n <p style=\"margin: 0; font-size: 0.875rem; color: #6b7280;\">\n Status: <strong>{{ slotProps.status }}</strong>\n </p>\n <p v-if=\"slotProps.currentNodeName\" style=\"margin: 0.25rem 0 0; font-size: 0.75rem; color: #9ca3af;\">\n Processing: {{ slotProps.currentNodeName }}\n </p>\n <p v-if=\"slotProps.jobId\" style=\"margin: 0.25rem 0 0; font-size: 0.75rem; color: #9ca3af;\">\n Job ID: {{ slotProps.jobId }}\n </p>\n </div>\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\nconst handleClick = () => {\n flow.execute();\n};\n</script>\n\n<template>\n <button\n type=\"button\"\n @click=\"handleClick\"\n :disabled=\"flow.isUploading.value\"\n v-bind=\"$attrs\"\n >\n <slot>Execute</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\n// Disable attribute inheritance so we can spread them manually\nexport default {\n inheritAttrs: false,\n};\n</script>\n","<script setup lang=\"ts\">\n/**\n * UploadList - Display a list of uploads with customizable status grouping and sorting\n *\n * Shows the progress and status of multiple file uploads. Supports filtering, sorting,\n * and status-based grouping. Provides flexible slot-based customization for rendering each item.\n *\n * @component\n * @example\n * // Basic upload list\n * <UploadList :uploads=\"uploads\" />\n *\n * @example\n * // Custom item rendering with status indicators\n * <UploadList :uploads=\"uploads\">\n * <template #item=\"{ item, isSuccess, isError }\">\n * <div class=\"upload-item\">\n * <span>{{ item.filename }}</span>\n * <progress :value=\"item.progress\" max=\"100\"></progress>\n * <span :class=\"{ success: isSuccess, error: isError }\">\n * {{ item.state.status }}\n * </span>\n * </div>\n * </template>\n * </UploadList>\n *\n * @example\n * // With filtering and sorting\n * <UploadList\n * :uploads=\"uploads\"\n * :filter=\"item => item.state.status === 'success'\"\n * :sort-by=\"(a, b) => a.uploadedAt - b.uploadedAt\"\n * />\n */\nimport type { VNodeChild } from \"vue\";\nimport { computed } from \"vue\";\nimport type { UploadItem } from \"../composables\";\nimport { isBrowserFile } from \"../utils\";\n\n/**\n * Props for the UploadList component\n * @property {UploadItem[]} uploads - Array of upload items to display\n * @property {Function} filter - Optional filter for which items to display\n * @property {Function} sortBy - Optional sorting function for items (a, b) => number\n */\nexport interface UploadListProps {\n /**\n * Array of upload items to display\n */\n uploads: UploadItem[];\n\n /**\n * Optional filter for which items to display\n */\n filter?: (item: UploadItem) => boolean;\n\n /**\n * Optional sorting function for items\n */\n sortBy?: (a: UploadItem, b: UploadItem) => number;\n}\n\nconst props = defineProps<UploadListProps>();\n\ndefineSlots<{\n item(props: {\n item: UploadItem;\n index: number;\n isUploading: boolean;\n isSuccess: boolean;\n isError: boolean;\n formatFileSize: (bytes: number) => string;\n }): VNodeChild;\n default?(props: {\n items: UploadItem[];\n itemsByStatus: {\n idle: UploadItem[];\n uploading: UploadItem[];\n success: UploadItem[];\n error: UploadItem[];\n aborted: UploadItem[];\n };\n }): VNodeChild;\n}>();\n\n// Apply filtering and sorting\nconst filteredItems = computed(() => {\n let items = props.uploads;\n\n if (props.filter) {\n items = items.filter(props.filter);\n }\n\n if (props.sortBy) {\n items = [...items].sort(props.sortBy);\n }\n\n return items;\n});\n\n// Group items by status\nconst itemsByStatus = computed(() => ({\n idle: filteredItems.value.filter((item) => item.state.status === \"idle\"),\n uploading: filteredItems.value.filter(\n (item) => item.state.status === \"uploading\",\n ),\n success: filteredItems.value.filter(\n (item) => item.state.status === \"success\",\n ),\n error: filteredItems.value.filter((item) => item.state.status === \"error\"),\n aborted: filteredItems.value.filter(\n (item) => item.state.status === \"aborted\",\n ),\n}));\n\n// Helper function to format file sizes\nconst formatFileSize = (bytes: number): string => {\n if (bytes === 0) return \"0 Bytes\";\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;\n};\n\n// Helper function to get status icon\nconst getStatusIcon = (status: string): string => {\n switch (status) {\n case \"idle\":\n return \"⏳\";\n case \"uploading\":\n return \"📤\";\n case \"success\":\n return \"✅\";\n case \"error\":\n return \"❌\";\n case \"aborted\":\n return \"⏹️\";\n default:\n return \"❓\";\n }\n};\n\n// Helper function to get status color\nconst getStatusColor = (status: string): string => {\n switch (status) {\n case \"idle\":\n return \"#6c757d\";\n case \"uploading\":\n return \"#007bff\";\n case \"success\":\n return \"#28a745\";\n case \"error\":\n return \"#dc3545\";\n case \"aborted\":\n return \"#6c757d\";\n default:\n return \"#6c757d\";\n }\n};\n</script>\n\n<template>\n <div class=\"upload-list\">\n <slot :items=\"filteredItems\" :items-by-status=\"itemsByStatus\">\n <!-- Default rendering: simple list of upload items -->\n <div\n v-for=\"(item, index) in filteredItems\"\n :key=\"item.id\"\n class=\"upload-list__item\"\n :class=\"`upload-list__item--${item.state.status}`\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :index=\"index\"\n :is-uploading=\"item.state.status === 'uploading'\"\n :is-success=\"item.state.status === 'success'\"\n :is-error=\"item.state.status === 'error'\"\n :format-file-size=\"formatFileSize\"\n >\n <!-- Default item template -->\n <div class=\"upload-list__item-header\">\n <span class=\"upload-list__item-icon\">\n {{ getStatusIcon(item.state.status) }}\n </span>\n <span class=\"upload-list__item-name\">\n {{ isBrowserFile(item.file) ? item.file.name : 'File' }}\n </span>\n <span\n class=\"upload-list__item-status\"\n :style=\"{ color: getStatusColor(item.state.status) }\"\n >\n {{ item.state.status.toUpperCase() }}\n </span>\n </div>\n\n <div v-if=\"item.state.totalBytes\" class=\"upload-list__item-size\">\n {{ formatFileSize(item.state.totalBytes) }}\n </div>\n\n <div v-if=\"item.state.status === 'uploading'\" class=\"upload-list__item-progress\">\n <div class=\"upload-list__progress-bar\">\n <div\n class=\"upload-list__progress-fill\"\n :style=\"{ width: `${item.state.progress}%` }\"\n />\n </div>\n <span class=\"upload-list__progress-text\">{{ item.state.progress }}%</span>\n </div>\n\n <div v-if=\"item.state.status === 'error' && item.state.error\" class=\"upload-list__item-error\">\n {{ item.state.error.message }}\n </div>\n </slot>\n </div>\n </slot>\n </div>\n</template>\n\n<style scoped>\n.upload-list {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.upload-list__item {\n padding: 0.75rem;\n border: 1px solid #e0e0e0;\n border-radius: 0.375rem;\n background-color: #fff;\n}\n\n.upload-list__item-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n}\n\n.upload-list__item-icon {\n font-size: 1rem;\n}\n\n.upload-list__item-name {\n flex: 1;\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.upload-list__item-status {\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n}\n\n.upload-list__item-size {\n font-size: 0.75rem;\n color: #666;\n margin-bottom: 0.5rem;\n}\n\n.upload-list__item-progress {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.upload-list__progress-bar {\n flex: 1;\n height: 0.375rem;\n background-color: #e0e0e0;\n border-radius: 0.1875rem;\n overflow: hidden;\n}\n\n.upload-list__progress-fill {\n height: 100%;\n background-color: #007bff;\n transition: width 0.2s ease;\n}\n\n.upload-list__progress-text {\n font-size: 0.75rem;\n color: #666;\n min-width: 3rem;\n text-align: right;\n}\n\n.upload-list__item-error {\n margin-top: 0.5rem;\n padding: 0.5rem;\n background-color: #f8d7da;\n color: #721c24;\n font-size: 0.75rem;\n border-radius: 0.25rem;\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * UploadZone - A flexible file upload component with drag-and-drop support\n *\n * Provides a drag-and-drop zone and file picker for uploading files. Supports both single\n * and multiple file uploads with validation. Emits events for file selection and upload events.\n *\n * @component\n * @example\n * // Basic single file upload\n * <UploadZone @file-select=\"handleFiles\" />\n *\n * @example\n * // Multiple files with validation\n * <UploadZone\n * multiple\n * accept={[\"image/*\"]}\n * :max-file-size=\"10 * 1024 * 1024\"\n * @file-select=\"handleFiles\"\n * @validation-error=\"handleErrors\"\n * >\n * <template #default=\"{ isDragging, errors, openFilePicker }\">\n * <div :class=\"{ dragging: isDragging }\" @click=\"openFilePicker\">\n * <p>{{ isDragging ? 'Drop files here' : 'Click or drag files here' }}</p>\n * <div v-if=\"errors.length\">\n * <p v-for=\"error in errors\" :key=\"error\">{{ error }}</p>\n * </div>\n * </div>\n * </template>\n * </UploadZone>\n *\n * @emits file-select - When files are selected/dropped\n * @emits upload-start - When upload begins\n * @emits validation-error - When validation fails\n */\nimport type { UploadOptions } from \"@uploadista/client-browser\";\nimport type { VNodeChild } from \"vue\";\nimport { computed, ref } from \"vue\";\nimport type { MultiUploadOptions } from \"../composables\";\nimport { useDragDrop, useMultiUpload, useUpload } from \"../composables\";\n\n/**\n * Props for the UploadZone component\n * @property {string[]} accept - Accepted file types (MIME types or file extensions)\n * @property {boolean} multiple - Whether to allow multiple files (default: true)\n * @property {boolean} disabled - Whether the upload zone is disabled (default: false)\n * @property {number} maxFileSize - Maximum file size in bytes\n * @property {Function} validator - Custom validation function for files\n * @property {MultiUploadOptions} multiUploadOptions - Multi-upload options (only used when multiple=true)\n * @property {UploadOptions} uploadOptions - Single upload options (only used when multiple=false)\n */\nexport interface UploadZoneProps {\n /**\n * Accepted file types (MIME types or file extensions)\n */\n accept?: string[];\n\n /**\n * Whether to allow multiple files\n */\n multiple?: boolean;\n\n /**\n * Whether the upload zone is disabled\n */\n disabled?: boolean;\n\n /**\n * Maximum file size in bytes\n */\n maxFileSize?: number;\n\n /**\n * Custom validation function for files\n */\n validator?: (files: File[]) => string[] | null;\n\n /**\n * Multi-upload options (only used when multiple=true)\n */\n multiUploadOptions?: MultiUploadOptions;\n\n /**\n * Single upload options (only used when multiple=false)\n */\n uploadOptions?: UploadOptions;\n}\n\nconst props = withDefaults(defineProps<UploadZoneProps>(), {\n multiple: true,\n disabled: false,\n});\n\nconst emit = defineEmits<{\n \"file-select\": [files: File[]];\n \"upload-start\": [files: File[]];\n \"validation-error\": [errors: string[]];\n}>();\n\ndefineSlots<{\n default(props: {\n isDragging: boolean;\n isOver: boolean;\n isUploading: boolean;\n errors: string[];\n openFilePicker: () => void;\n }): VNodeChild;\n}>();\n\n// Initialize upload composables\nconst singleUpload = props.multiple\n ? null\n : useUpload(props.uploadOptions || {});\nconst multiUpload = props.multiple\n ? useMultiUpload(props.multiUploadOptions || {})\n : null;\n\n// Handle files received from drag-drop or file picker\nconst handleFilesReceived = (files: File[]) => {\n emit(\"file-select\", files);\n emit(\"upload-start\", files);\n\n if (props.multiple && multiUpload) {\n multiUpload.addFiles(files);\n setTimeout(() => multiUpload.startAll(), 0);\n } else if (!props.multiple && singleUpload && files[0]) {\n singleUpload.upload(files[0]);\n }\n};\n\n// Handle validation errors\nconst handleValidationError = (errors: string[]) => {\n emit(\"validation-error\", errors);\n};\n\n// Initialize drag-drop\nconst dragDrop = useDragDrop({\n accept: props.accept,\n multiple: props.multiple,\n maxFileSize: props.maxFileSize,\n validator: props.validator,\n onFilesReceived: handleFilesReceived,\n onValidationError: handleValidationError,\n});\n\n// File input ref\nconst fileInputRef = ref<HTMLInputElement>();\n\n// Open file picker\nconst openFilePicker = () => {\n if (!props.disabled) {\n fileInputRef.value?.click();\n }\n};\n\n// Computed states\nconst isActive = computed(\n () => dragDrop.state.value.isDragging || dragDrop.state.value.isOver,\n);\n\nconst isUploading = computed(() => {\n if (props.multiple && multiUpload) {\n return multiUpload.state.value.isUploading;\n } else if (!props.multiple && singleUpload) {\n return singleUpload.state.value.status === \"uploading\";\n }\n return false;\n});\n</script>\n\n<template>\n <div\n class=\"upload-zone\"\n :class=\"{ 'upload-zone--active': isActive, 'upload-zone--disabled': disabled }\"\n @dragenter=\"!disabled && dragDrop.onDragEnter\"\n @dragover=\"!disabled && dragDrop.onDragOver\"\n @dragleave=\"!disabled && dragDrop.onDragLeave\"\n @drop=\"!disabled && dragDrop.onDrop\"\n @click=\"openFilePicker\"\n role=\"button\"\n :tabindex=\"disabled ? -1 : 0\"\n :aria-disabled=\"disabled\"\n :aria-label=\"multiple ? 'Upload multiple files' : 'Upload a file'\"\n @keydown.enter=\"openFilePicker\"\n @keydown.space.prevent=\"openFilePicker\"\n >\n <slot\n :is-dragging=\"dragDrop.state.value.isDragging\"\n :is-over=\"dragDrop.state.value.isOver\"\n :is-uploading=\"isUploading\"\n :errors=\"[...dragDrop.state.value.errors]\"\n :open-file-picker=\"openFilePicker\"\n >\n <!-- Default slot content -->\n <div class=\"upload-zone__content\">\n <p v-if=\"dragDrop.state.value.isDragging\">\n {{ multiple ? 'Drop files here...' : 'Drop file here...' }}\n </p>\n <p v-else>\n {{ multiple ? 'Drag files here or click to select' : 'Drag a file here or click to select' }}\n </p>\n\n <div v-if=\"dragDrop.state.value.errors.length > 0\" class=\"upload-zone__errors\">\n <p v-for=\"(error, index) in dragDrop.state.value.errors\" :key=\"index\">\n {{ error }}\n </p>\n </div>\n </div>\n </slot>\n\n <input\n ref=\"fileInputRef\"\n type=\"file\"\n :multiple=\"dragDrop.inputProps.value.multiple\"\n :accept=\"dragDrop.inputProps.value.accept\"\n :disabled=\"disabled\"\n @change=\"dragDrop.onInputChange\"\n style=\"display: none\"\n aria-hidden=\"true\"\n />\n </div>\n</template>\n\n<style scoped>\n.upload-zone {\n cursor: pointer;\n user-select: none;\n}\n\n.upload-zone--disabled {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.upload-zone__content {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n}\n\n.upload-zone__errors {\n margin-top: 0.5rem;\n color: #dc3545;\n font-size: 0.875rem;\n}\n\n.upload-zone__errors p {\n margin: 0.25rem 0;\n}\n</style>\n","import { inject } from \"vue\";\nimport type { UploadContextValue } from \"./Upload.vue\";\n\n/**\n * Injection key for the Upload context\n */\nexport const UPLOAD_CONTEXT_KEY = \"uploadContext\";\n\n/**\n * Injection key for the UploadItem context\n */\nexport const UPLOAD_ITEM_CONTEXT_KEY = \"uploadItemContext\";\n\n/**\n * Context value for a specific upload item within an Upload.\n */\nexport interface UploadItemContextValue {\n /** Item ID */\n id: string;\n /** The file being uploaded */\n file: File | Blob;\n /** Current upload state */\n state: {\n status: string;\n progress: number;\n bytesUploaded: number;\n totalBytes: number | null;\n error: Error | null;\n result: unknown;\n };\n /** Abort this upload */\n abort: () => void;\n /** Retry this upload */\n retry: () => void;\n /** Remove this item from the queue */\n remove: () => void;\n}\n\n/**\n * Composable to access upload context from within an Upload component.\n * @throws Error if used outside of an Upload component\n */\nexport function useUploadContext(): UploadContextValue {\n const context = inject<UploadContextValue>(UPLOAD_CONTEXT_KEY);\n if (!context) {\n throw new Error(\n \"useUploadContext must be used within an <Upload> component. \" +\n \"Wrap your component tree with <Upload>\",\n );\n }\n return context;\n}\n\n/**\n * Composable to access upload item context from within an UploadItem component.\n * @throws Error if used outside of an UploadItem component\n */\nexport function useUploadItemContext(): UploadItemContextValue {\n const context = inject<UploadItemContextValue>(UPLOAD_ITEM_CONTEXT_KEY);\n if (!context) {\n throw new Error(\n \"useUploadItemContext must be used within an <UploadItem> component. \" +\n 'Wrap your component with <UploadItem id=\"...\">',\n );\n }\n return context;\n}\n","<script setup lang=\"ts\">\nimport type { UploadFile } from \"@uploadista/core/types\";\nimport { computed, provide } from \"vue\";\nimport {\n type MultiUploadState,\n type UploadItem,\n useMultiUpload,\n} from \"../../composables/useMultiUpload\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\n/**\n * Props for the Upload root component.\n */\nexport interface UploadProps {\n /** Whether to allow multiple file uploads (default: false) */\n multiple?: boolean;\n /** Maximum concurrent uploads (default: 3, only used in multi mode) */\n maxConcurrent?: number;\n /** Whether to auto-start uploads when files are received (default: true) */\n autoStart?: boolean;\n /** Metadata to attach to uploads */\n metadata?: Record<string, string>;\n}\n\nconst props = withDefaults(defineProps<UploadProps>(), {\n multiple: false,\n maxConcurrent: 3,\n autoStart: true,\n});\n\nconst emit = defineEmits<{\n /** Called when a single file upload succeeds (single mode) */\n success: [result: UploadFile];\n /** Called when an upload fails */\n error: [error: Error, item?: UploadItem];\n /** Called when all uploads complete (multi mode) */\n complete: [\n results: { successful: UploadItem[]; failed: UploadItem[]; total: number },\n ];\n /** Called when an individual upload starts */\n uploadStart: [item: UploadItem];\n /** Called on upload progress */\n progress: [\n item: UploadItem,\n progress: number,\n bytesUploaded: number,\n totalBytes: number | null,\n ];\n}>();\n\nconst multiUpload = useMultiUpload({\n maxConcurrent: props.maxConcurrent,\n metadata: props.metadata,\n onUploadStart: (item) => emit(\"uploadStart\", item),\n onUploadProgress: (item, progress, bytesUploaded, totalBytes) =>\n emit(\"progress\", item, progress, bytesUploaded, totalBytes),\n onUploadSuccess: (_item, result) => {\n // In single mode, call success directly\n if (!props.multiple) {\n emit(\"success\", result);\n }\n },\n onUploadError: (item, error) => {\n emit(\"error\", error, item);\n },\n onComplete: (results) => emit(\"complete\", results),\n});\n\nconst handleFilesReceived = (files: File[]) => {\n if (!props.multiple) {\n // Single mode: clear existing and add new file\n multiUpload.clearAll();\n }\n multiUpload.addFiles(files);\n if (props.autoStart) {\n // Use setTimeout to ensure state is updated before starting\n setTimeout(() => multiUpload.startAll(), 0);\n }\n};\n\n/**\n * Context value provided by the Upload component root.\n * Contains all upload state and actions.\n */\nexport interface UploadContextValue {\n /** Whether in multi-file mode */\n mode: \"single\" | \"multi\";\n /** Current multi-upload state (aggregate) */\n state: MultiUploadState;\n /** All upload items */\n items: readonly UploadItem[];\n /** Whether auto-start is enabled */\n autoStart: boolean;\n\n /** Add files to the upload queue */\n addFiles: (files: File[]) => void;\n /** Remove an item from the queue */\n removeItem: (id: string) => void;\n /** Start all pending uploads */\n startAll: () => void;\n /** Abort a specific upload by ID */\n abortUpload: (id: string) => void;\n /** Abort all active uploads */\n abortAll: () => void;\n /** Retry a specific failed upload by ID */\n retryUpload: (id: string) => void;\n /** Retry all failed uploads */\n retryFailed: () => void;\n /** Clear all completed uploads */\n clearCompleted: () => void;\n /** Clear all items and reset state */\n clearAll: () => void;\n\n /** Internal handler for files received from drop zone */\n handleFilesReceived: (files: File[]) => void;\n}\n\n// Create computed context value that updates reactively\n// Cast items to mutable array for context (the readonly is enforced at the composable level)\nconst contextValue = computed<UploadContextValue>(() => ({\n mode: props.multiple ? \"multi\" : \"single\",\n state: multiUpload.state.value,\n items: multiUpload.items.value as UploadItem[],\n autoStart: props.autoStart,\n addFiles: multiUpload.addFiles,\n removeItem: multiUpload.removeItem,\n startAll: multiUpload.startAll,\n abortUpload: multiUpload.abortUpload,\n abortAll: multiUpload.abortAll,\n retryUpload: multiUpload.retryUpload,\n retryFailed: multiUpload.retryFailed,\n clearCompleted: multiUpload.clearCompleted,\n clearAll: multiUpload.clearAll,\n handleFilesReceived,\n}));\n\n// Provide context for child components\nprovide(UPLOAD_CONTEXT_KEY, contextValue);\n\n// Expose to parent via defineExpose for programmatic access\ndefineExpose(contextValue);\n</script>\n\n<template>\n <slot />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\"UploadCancel must be used within an <Upload> component.\");\n}\n\nconst isDisabled = computed(() => !uploadContext.value.state.isUploading);\n\nconst handleClick = () => {\n uploadContext.value.abortAll();\n};\n</script>\n\n<template>\n <button type=\"button\" :disabled=\"isDisabled\" @click=\"handleClick\">\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\n \"UploadClearCompleted must be used within an <Upload> component.\",\n );\n}\n\nconst isDisabled = computed(() => uploadContext.value.state.completed === 0);\n\nconst handleClick = () => {\n uploadContext.value.clearCompleted();\n};\n</script>\n\n<template>\n <button type=\"button\" :disabled=\"isDisabled\" @click=\"handleClick\">\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref } from \"vue\";\nimport { type DragDropState, useDragDrop } from \"../../composables/useDragDrop\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\n/**\n * Props for UploadDropZone component.\n */\nexport interface UploadDropZoneProps {\n /** Accepted file types (e.g., \"image/*\", \".pdf\") */\n accept?: string;\n /** Maximum file size in bytes */\n maxFileSize?: number;\n /** Maximum number of files (only in multi mode) */\n maxFiles?: number;\n}\n\n/**\n * Slot props for UploadDropZone component.\n */\nexport interface UploadDropZoneSlotProps {\n /** Whether files are being dragged over */\n isDragging: boolean;\n /** Whether drag is over the zone */\n isOver: boolean;\n /** Validation errors */\n errors: readonly string[];\n /** Drag event handlers to bind to the drop zone element */\n dragHandlers: {\n onDragenter: (event: DragEvent) => void;\n onDragover: (event: DragEvent) => void;\n onDragleave: (event: DragEvent) => void;\n onDrop: (event: DragEvent) => void;\n };\n /** Input props for the hidden file input */\n inputProps: {\n type: \"file\";\n multiple: boolean;\n accept: string | undefined;\n };\n /** Handler for input change event */\n onInputChange: (event: Event) => void;\n /** Open file picker programmatically */\n openFilePicker: () => void;\n /** Current drag-drop state */\n dragDropState: DragDropState;\n}\n\nconst props = defineProps<UploadDropZoneProps>();\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\"UploadDropZone must be used within an <Upload> component.\");\n}\n\nconst inputRef = ref<HTMLInputElement>();\n\nconst dragDrop = useDragDrop({\n onFilesReceived: (files) => uploadContext.value.handleFilesReceived(files),\n accept: props.accept\n ? props.accept.split(\",\").map((t) => t.trim())\n : undefined,\n maxFileSize: props.maxFileSize,\n maxFiles: uploadContext.value.mode === \"multi\" ? props.maxFiles : 1,\n multiple: uploadContext.value.mode === \"multi\",\n});\n\nconst openFilePicker = () => {\n inputRef.value?.click();\n};\n\nconst slotProps = computed<UploadDropZoneSlotProps>(() => ({\n isDragging: dragDrop.state.value.isDragging,\n isOver: dragDrop.state.value.isOver,\n errors: dragDrop.state.value.errors,\n dragHandlers: {\n onDragenter: dragDrop.onDragEnter,\n onDragover: dragDrop.onDragOver,\n onDragleave: dragDrop.onDragLeave,\n onDrop: dragDrop.onDrop,\n },\n inputProps: dragDrop.inputProps.value,\n onInputChange: dragDrop.onInputChange,\n openFilePicker,\n dragDropState: dragDrop.state.value,\n}));\n\ndefineExpose({ inputRef });\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default slot content if none provided -->\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport type { UploadItem } from \"../../composables/useMultiUpload\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\n/**\n * Slot props for UploadError component.\n */\nexport interface UploadErrorSlotProps {\n /** Whether there are any errors */\n hasError: boolean;\n /** Number of failed uploads */\n failedCount: number;\n /** Failed items */\n failedItems: readonly UploadItem[];\n /** Reset/clear all errors */\n reset: () => void;\n}\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\"UploadError must be used within an <Upload> component.\");\n}\n\nconst slotProps = computed<UploadErrorSlotProps>(() => {\n const failedItems = uploadContext.value.items.filter((item) =>\n [\"error\", \"aborted\"].includes(item.state.status),\n );\n\n return {\n hasError: failedItems.length > 0,\n failedCount: failedItems.length,\n failedItems,\n reset: uploadContext.value.clearCompleted,\n };\n});\n</script>\n\n<template>\n <slot v-bind=\"slotProps\" />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, provide } from \"vue\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport {\n UPLOAD_CONTEXT_KEY,\n UPLOAD_ITEM_CONTEXT_KEY,\n type UploadItemContextValue,\n} from \"./useUploadContext\";\n\n/**\n * Props for UploadItem component.\n */\nexport interface UploadItemProps {\n /** Item ID */\n id: string;\n}\n\n/**\n * Slot props for UploadItem component.\n */\nexport interface UploadItemSlotProps extends UploadItemContextValue {}\n\nconst props = defineProps<UploadItemProps>();\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\"UploadItem must be used within an <Upload> component.\");\n}\n\nconst item = computed(() =>\n uploadContext.value.items.find((i) => i.id === props.id),\n);\n\nconst itemContext = computed<UploadItemContextValue | null>(() => {\n const currentItem = item.value;\n if (!currentItem) return null;\n\n return {\n id: props.id,\n file: currentItem.file,\n state: currentItem.state,\n abort: () => uploadContext.value.abortUpload(props.id),\n retry: () => uploadContext.value.retryUpload(props.id),\n remove: () => uploadContext.value.removeItem(props.id),\n };\n});\n\n// Provide item context for nested components\nprovide(UPLOAD_ITEM_CONTEXT_KEY, itemContext);\n</script>\n\n<template>\n <slot v-if=\"itemContext\" v-bind=\"itemContext\" />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport type { UploadItem } from \"../../composables/useMultiUpload\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\n/**\n * Slot props for UploadItems component.\n */\nexport interface UploadItemsSlotProps {\n /** All upload items */\n items: readonly UploadItem[];\n /** Whether there are any items */\n hasItems: boolean;\n /** Whether items array is empty */\n isEmpty: boolean;\n}\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\"UploadItems must be used within an <Upload> component.\");\n}\n\nconst slotProps = computed<UploadItemsSlotProps>(() => ({\n items: uploadContext.value.items,\n hasItems: uploadContext.value.items.length > 0,\n isEmpty: uploadContext.value.items.length === 0,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\" />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\n/**\n * Slot props for UploadProgress component.\n */\nexport interface UploadProgressSlotProps {\n /** Progress percentage (0-100) */\n progress: number;\n /** Bytes uploaded so far */\n bytesUploaded: number;\n /** Total bytes to upload */\n totalBytes: number;\n /** Whether any uploads are active */\n isUploading: boolean;\n}\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\"UploadProgress must be used within an <Upload> component.\");\n}\n\nconst slotProps = computed<UploadProgressSlotProps>(() => ({\n progress: uploadContext.value.state.progress,\n bytesUploaded: uploadContext.value.state.totalBytesUploaded,\n totalBytes: uploadContext.value.state.totalBytes,\n isUploading: uploadContext.value.state.isUploading,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\" />\n</template>\n","<script setup lang=\"ts\">\nimport { inject } from \"vue\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\"UploadReset must be used within an <Upload> component.\");\n}\n\nconst handleClick = () => {\n uploadContext.value.clearAll();\n};\n</script>\n\n<template>\n <button type=\"button\" @click=\"handleClick\">\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\"UploadRetry must be used within an <Upload> component.\");\n}\n\nconst isDisabled = computed(() => uploadContext.value.state.failed === 0);\n\nconst handleClick = () => {\n uploadContext.value.retryFailed();\n};\n</script>\n\n<template>\n <button type=\"button\" :disabled=\"isDisabled\" @click=\"handleClick\">\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\"UploadStartAll must be used within an <Upload> component.\");\n}\n\nconst idleCount = computed(\n () =>\n uploadContext.value.items.filter((item) => item.state.status === \"idle\")\n .length,\n);\n\nconst isDisabled = computed(\n () => uploadContext.value.state.isUploading || idleCount.value === 0,\n);\n\nconst handleClick = () => {\n uploadContext.value.startAll();\n};\n</script>\n\n<template>\n <button type=\"button\" :disabled=\"isDisabled\" @click=\"handleClick\">\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport type { UploadContextValue } from \"./Upload.vue\";\nimport { UPLOAD_CONTEXT_KEY } from \"./useUploadContext\";\n\n/**\n * Slot props for UploadStatus component.\n */\nexport interface UploadStatusSlotProps {\n /** Overall status */\n status: \"idle\" | \"uploading\" | \"success\" | \"error\";\n /** Whether idle (no uploads active or completed) */\n isIdle: boolean;\n /** Whether uploading */\n isUploading: boolean;\n /** Whether all uploads succeeded */\n isSuccess: boolean;\n /** Whether any upload failed */\n isError: boolean;\n /** Whether all uploads completed (success or failure) */\n isComplete: boolean;\n /** Number of total items */\n total: number;\n /** Number of successful uploads */\n successful: number;\n /** Number of failed uploads */\n failed: number;\n /** Number of currently uploading */\n uploading: number;\n}\n\nconst uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);\nif (!uploadContext) {\n throw new Error(\"UploadStatus must be used within an <Upload> component.\");\n}\n\nconst slotProps = computed<UploadStatusSlotProps>(() => {\n const state = uploadContext.value.state;\n\n // Derive overall status\n let status: \"idle\" | \"uploading\" | \"success\" | \"error\" = \"idle\";\n if (state.isUploading) {\n status = \"uploading\";\n } else if (state.isComplete) {\n status = state.failed > 0 ? \"error\" : \"success\";\n }\n\n return {\n status,\n isIdle: status === \"idle\",\n isUploading: state.isUploading,\n isSuccess: state.isComplete && state.failed === 0,\n isError: state.failed > 0,\n isComplete: state.isComplete,\n total: state.total,\n successful: state.successful,\n failed: state.failed,\n uploading: state.uploading,\n };\n});\n</script>\n\n<template>\n <slot v-bind=\"slotProps\" />\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"composables-DEuPeeIk.mjs","names":["initialState","initialState"],"sources":["../src/composables/useFlowManagerContext.ts","../src/composables/useFlow.ts","../src/composables/useDragDrop.ts","../src/composables/eventUtils.ts","../src/composables/useFlowEvents.ts","../src/composables/useUploadEvents.ts","../src/composables/useUploadistaEvents.ts","../src/composables/useMultiFlowUpload.ts","../src/composables/useMultiUpload.ts","../src/composables/useUpload.ts","../src/composables/useUploadMetrics.ts"],"sourcesContent":["import type {\n FlowManager,\n FlowManagerCallbacks,\n FlowUploadOptions,\n} from \"@uploadista/client-core\";\nimport { inject } from \"vue\";\n\n/**\n * Context value providing access to flow managers\n */\ninterface FlowManagerContextValue {\n getManager: (\n flowId: string,\n callbacks: FlowManagerCallbacks,\n options: FlowUploadOptions,\n ) => FlowManager<unknown>;\n releaseManager: (flowId: string) => void;\n}\n\n/**\n * Composable to access the FlowManager context.\n * Must be used within a FlowManagerProvider.\n *\n * @returns FlowManager context value with getManager and releaseManager functions\n * @throws Error if used outside of FlowManagerProvider\n *\n * @example\n * ```ts\n * function setup() {\n * const { getManager, releaseManager } = useFlowManagerContext();\n * // Use to create managers...\n * }\n * ```\n */\nexport function useFlowManagerContext(): FlowManagerContextValue {\n const context = inject<FlowManagerContextValue>(\"flowManagerContext\");\n\n if (!context) {\n throw new Error(\n \"useFlowManagerContext must be used within a FlowManagerProvider. \" +\n \"Make sure to wrap your component tree with <FlowManagerProvider>.\",\n );\n }\n\n return context;\n}\n","import type { FlowUploadOptions } from \"@uploadista/client-browser\";\nimport type {\n FlowManager,\n FlowUploadState,\n FlowUploadStatus,\n InputExecutionState,\n} from \"@uploadista/client-core\";\nimport type { TypedOutput } from \"@uploadista/core/flow\";\nimport {\n computed,\n onMounted,\n onUnmounted,\n type Ref,\n readonly,\n ref,\n shallowReadonly,\n} from \"vue\";\nimport { useFlowManagerContext } from \"./useFlowManagerContext\";\nimport { useUploadistaClient } from \"./useUploadistaClient\";\n\n// Re-export types from core for convenience\nexport type { FlowUploadState, FlowUploadStatus, InputExecutionState };\n\n/**\n * Input metadata discovered from the flow\n */\nexport interface FlowInputMetadata {\n /** Input node ID */\n nodeId: string;\n /** Human-readable node name */\n nodeName: string;\n /** Node description explaining what input is needed */\n nodeDescription: string;\n /** Input type ID from inputTypeRegistry - describes how clients interact with this node */\n inputTypeId?: string;\n /** Whether this input is required */\n required: boolean;\n}\n\n/**\n * Return value from the useFlow composable with upload control methods and state.\n *\n * @property state - Complete flow upload state with progress and outputs\n * @property inputMetadata - Metadata about discovered input nodes (null until discovered)\n * @property inputStates - Per-input execution state for multi-input flows\n * @property inputs - Current input values set via setInput()\n * @property setInput - Set an input value for a specific node (for progressive provision)\n * @property execute - Execute the flow with current inputs (auto-detects types)\n * @property upload - Convenience method for single-file upload (same as execute with one file input)\n * @property abort - Cancel the current upload and flow execution\n * @property pause - Pause the current upload\n * @property reset - Reset state to idle (clears all data)\n * @property isUploading - True when upload or processing is active\n * @property isUploadingFile - True only during file upload phase\n * @property isProcessing - True only during flow processing phase\n * @property isDiscoveringInputs - True while discovering flow inputs\n */\nexport interface UseFlowReturn {\n /**\n * Current upload state\n */\n state: Readonly<Ref<FlowUploadState>>;\n\n /**\n * Discovered input nodes metadata (null until discovery completes)\n */\n inputMetadata: Readonly<Ref<FlowInputMetadata[] | null>>;\n\n /**\n * Per-input execution state for multi-input flows\n */\n inputStates: Readonly<Ref<ReadonlyMap<string, InputExecutionState>>>;\n\n /**\n * Current inputs set via setInput()\n */\n inputs: Readonly<Ref<Record<string, unknown>>>;\n\n /**\n * Set an input value for a specific node.\n * For progressive input provision before calling execute().\n *\n * @param nodeId - The input node ID\n * @param value - The input value (File, URL string, or structured data)\n */\n setInput: (nodeId: string, value: unknown) => void;\n\n /**\n * Execute the flow with current inputs.\n * Automatically detects input types and routes appropriately.\n * For single input, uses standard upload path.\n * For multiple inputs, requires multiInputUploadFn.\n */\n execute: () => Promise<void>;\n\n /**\n * Upload a single file through the flow (convenience method).\n * Equivalent to setInput(firstNodeId, file) + execute().\n *\n * @param file - File or Blob to upload\n */\n upload: (file: File | Blob) => Promise<void>;\n\n /**\n * Abort the current upload\n */\n abort: () => Promise<void>;\n\n /**\n * Pause the current upload\n */\n pause: () => Promise<void>;\n\n /**\n * Resume a paused upload\n */\n resume: () => Promise<void>;\n\n /**\n * Reset the upload state and clear all inputs\n */\n reset: () => void;\n\n /**\n * Whether an upload or flow execution is in progress (uploading OR processing)\n */\n isUploading: Readonly<Ref<boolean>>;\n\n /**\n * Whether the file is currently being uploaded (chunks being sent)\n */\n isUploadingFile: Readonly<Ref<boolean>>;\n\n /**\n * Whether the flow is currently processing (after upload completes)\n */\n isProcessing: Readonly<Ref<boolean>>;\n\n /**\n * Whether the hook is discovering flow inputs\n */\n isDiscoveringInputs: Readonly<Ref<boolean>>;\n\n /**\n * Whether the flow is currently paused\n */\n isPaused: Readonly<Ref<boolean>>;\n}\n\nconst initialState: FlowUploadState = {\n status: \"idle\",\n progress: 0,\n bytesUploaded: 0,\n totalBytes: null,\n error: null,\n jobId: null,\n flowStarted: false,\n currentNodeName: null,\n currentNodeType: null,\n flowOutputs: null,\n pausedAtNodeId: null,\n};\n\n/**\n * Vue composable for executing flows with single or multiple inputs.\n * Automatically discovers input nodes and detects input types (File, URL, structured data).\n * Supports progressive input provision via setInput() and execute().\n *\n * This is the unified flow composable that replaces useFlowUpload for advanced use cases.\n * It provides:\n * - Auto-discovery of flow input nodes\n * - Automatic input type detection (file -> upload, string -> URL, object -> data)\n * - Progressive input provision via setInput()\n * - Multi-input support with parallel coordination\n * - Per-input state tracking\n *\n * Must be used within FlowManagerProvider (which must be within UploadistaProvider).\n * Flow events are automatically routed by the provider to the appropriate manager.\n *\n * @param options - Flow upload configuration including flow ID and event handlers\n * @returns Flow upload state and control methods\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useFlow } from '@uploadista/vue';\n *\n * // Single file upload (simple case)\n * const flow = useFlow({\n * flowConfig: {\n * flowId: \"image-optimization\",\n * storageId: \"s3-images\",\n * },\n * onSuccess: (outputs) => {\n * console.log(\"Flow outputs:\", outputs);\n * },\n * });\n *\n * const handleFileChange = (event: Event) => {\n * const file = (event.target as HTMLInputElement).files?.[0];\n * if (file) flow.upload(file);\n * };\n * </script>\n *\n * <template>\n * <div>\n * <input type=\"file\" @change=\"handleFileChange\" />\n * <div v-if=\"flow.isUploading.value\">Progress: {{ flow.state.value.progress }}%</div>\n * </div>\n * </template>\n * ```\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useFlow } from '@uploadista/vue';\n *\n * // Multi-input with progressive provision\n * const flow = useFlow({\n * flowConfig: {\n * flowId: \"multi-source-processing\",\n * storageId: \"default\",\n * },\n * });\n *\n * const handleInputChange = (nodeId: string, event: Event) => {\n * const target = event.target as HTMLInputElement;\n * if (target.type === 'file') {\n * const file = target.files?.[0];\n * if (file) flow.setInput(nodeId, file);\n * } else {\n * flow.setInput(nodeId, target.value);\n * }\n * };\n * </script>\n *\n * <template>\n * <div v-if=\"flow.inputMetadata.value\">\n * <div v-for=\"input in flow.inputMetadata.value\" :key=\"input.nodeId\">\n * <label>{{ input.nodeName }}</label>\n * <input\n * v-if=\"input.inputTypeId === 'streaming-input-v1'\"\n * type=\"file\"\n * @change=\"(e) => handleInputChange(input.nodeId, e)\"\n * />\n * <input\n * v-else\n * type=\"url\"\n * @change=\"(e) => handleInputChange(input.nodeId, e)\"\n * />\n * </div>\n * <button @click=\"flow.execute\" :disabled=\"flow.isUploading.value\">\n * Execute Flow\n * </button>\n * </div>\n * </template>\n * ```\n *\n * @see {@link useFlowUpload} for a simpler file-only upload composable\n */\nexport function useFlow(options: FlowUploadOptions): UseFlowReturn {\n const { client } = useUploadistaClient();\n const { getManager, releaseManager } = useFlowManagerContext();\n\n const state = ref<FlowUploadState>(initialState);\n const inputMetadata = ref<FlowInputMetadata[] | null>(null);\n const isDiscoveringInputs = ref(false);\n const inputs = ref<Record<string, unknown>>({});\n const inputStates = ref<ReadonlyMap<string, InputExecutionState>>(new Map());\n\n let manager: FlowManager<unknown> | null = null;\n\n // Store latest options in a ref to access in callbacks\n const optionsRef = ref(options);\n let pollInterval: ReturnType<typeof setInterval> | null = null;\n\n // Auto-discover flow inputs on mount\n onMounted(async () => {\n // Discover inputs\n isDiscoveringInputs.value = true;\n try {\n const { flow } = await client.getFlow(options.flowConfig.flowId);\n\n // Find all input nodes\n const inputNodes = flow.nodes.filter((node) => node.type === \"input\");\n\n const metadata: FlowInputMetadata[] = inputNodes.map((node) => ({\n nodeId: node.id,\n nodeName: node.name,\n nodeDescription: node.description,\n inputTypeId: node.inputTypeId,\n // TODO: Add required field to node schema to determine if input is required\n required: true,\n }));\n\n inputMetadata.value = metadata;\n } catch (error) {\n console.error(\"Failed to discover flow inputs:\", error);\n } finally {\n isDiscoveringInputs.value = false;\n }\n\n // Create stable callback wrappers\n const stableCallbacks = {\n onStateChange: (newState: FlowUploadState) => {\n state.value = newState;\n },\n onProgress: (\n uploadId: string,\n bytesUploaded: number,\n totalBytes: number | null,\n ) => {\n optionsRef.value.onProgress?.(uploadId, bytesUploaded, totalBytes);\n },\n onChunkComplete: (\n chunkSize: number,\n bytesAccepted: number,\n bytesTotal: number | null,\n ) => {\n optionsRef.value.onChunkComplete?.(\n chunkSize,\n bytesAccepted,\n bytesTotal,\n );\n },\n onFlowComplete: (outputs: TypedOutput[]) => {\n optionsRef.value.onFlowComplete?.(outputs);\n },\n onSuccess: (outputs: TypedOutput[]) => {\n optionsRef.value.onSuccess?.(outputs);\n },\n onError: (error: Error) => {\n optionsRef.value.onError?.(error);\n },\n onAbort: () => {\n optionsRef.value.onAbort?.();\n },\n };\n\n // Get manager from context\n manager = getManager(options.flowConfig.flowId, stableCallbacks, options);\n\n // Set up interval to poll input states for multi-input flows\n pollInterval = setInterval(() => {\n if (manager) {\n const states = manager.getInputStates();\n if (states.size > 0) {\n inputStates.value = new Map(states);\n }\n }\n }, 100); // Poll every 100ms\n });\n\n // Cleanup on unmount\n onUnmounted(() => {\n if (pollInterval) {\n clearInterval(pollInterval);\n pollInterval = null;\n }\n if (manager) {\n releaseManager(options.flowConfig.flowId);\n manager = null;\n }\n });\n\n // Set an input value\n const setInput = (nodeId: string, value: unknown) => {\n inputs.value = { ...inputs.value, [nodeId]: value };\n };\n\n // Execute flow with current inputs\n const execute = async () => {\n if (!manager) {\n throw new Error(\"FlowManager not initialized\");\n }\n\n if (Object.keys(inputs.value).length === 0) {\n throw new Error(\n \"No inputs provided. Use setInput() to provide inputs before calling execute()\",\n );\n }\n\n await manager.executeFlow(inputs.value);\n };\n\n // Convenience method for single file upload\n const upload = async (file: File | Blob) => {\n if (!manager) {\n throw new Error(\"FlowManager not initialized\");\n }\n\n // If we have input metadata, use the first input node\n // Otherwise, let the manager discover it\n if (inputMetadata.value && inputMetadata.value.length > 0) {\n const firstInputNode = inputMetadata.value[0];\n if (!firstInputNode) {\n throw new Error(\"No input nodes found\");\n }\n inputs.value = { [firstInputNode.nodeId]: file };\n await manager.executeFlow({ [firstInputNode.nodeId]: file });\n } else {\n // Fall back to direct upload (manager will handle discovery)\n await manager.upload(file);\n }\n };\n\n const abort = async () => {\n await manager?.abort();\n };\n\n const pause = async () => {\n await manager?.pause();\n };\n\n const resume = async () => {\n await manager?.resume();\n };\n\n const reset = () => {\n manager?.reset();\n inputs.value = {};\n inputStates.value = new Map();\n };\n\n // Derive computed values from state\n const isUploading = computed(\n () =>\n state.value.status === \"uploading\" || state.value.status === \"processing\",\n );\n const isUploadingFile = computed(() => state.value.status === \"uploading\");\n const isProcessing = computed(() => state.value.status === \"processing\");\n const isPaused = computed(() => state.value.status === \"paused\");\n\n return {\n state: shallowReadonly(state),\n inputMetadata: shallowReadonly(inputMetadata),\n inputStates: shallowReadonly(inputStates),\n inputs: shallowReadonly(inputs),\n setInput,\n execute,\n upload,\n abort,\n pause,\n resume,\n reset,\n isUploading: readonly(isUploading),\n isUploadingFile: readonly(isUploadingFile),\n isProcessing: readonly(isProcessing),\n isDiscoveringInputs: readonly(isDiscoveringInputs),\n isPaused: readonly(isPaused),\n };\n}\n","import { computed, readonly, ref } from \"vue\";\n\nexport interface DragDropOptions {\n /**\n * Accept specific file types (MIME types or file extensions)\n */\n accept?: string[];\n\n /**\n * Maximum number of files allowed\n */\n maxFiles?: number;\n\n /**\n * Maximum file size in bytes\n */\n maxFileSize?: number;\n\n /**\n * Whether to allow multiple files\n */\n multiple?: boolean;\n\n /**\n * Custom validation function for files\n */\n validator?: (files: File[]) => string[] | null;\n\n /**\n * Called when files are dropped or selected\n */\n onFilesReceived?: (files: File[]) => void;\n\n /**\n * Called when validation fails\n */\n onValidationError?: (errors: string[]) => void;\n\n /**\n * Called when drag state changes\n */\n onDragStateChange?: (isDragging: boolean) => void;\n}\n\nexport interface DragDropState {\n /**\n * Whether files are currently being dragged over the drop zone\n */\n readonly isDragging: boolean;\n\n /**\n * Whether the drag is currently over the drop zone\n */\n readonly isOver: boolean;\n\n /**\n * Whether the dragged items are valid files\n */\n readonly isValid: boolean;\n\n /**\n * Current validation errors\n */\n readonly errors: readonly string[];\n}\n\nconst initialState: DragDropState = {\n isDragging: false,\n isOver: false,\n isValid: true,\n errors: [],\n};\n\n/**\n * Vue composable for handling drag and drop file uploads with validation.\n * Provides drag state management, file validation, and file picker integration.\n *\n * @param options - Configuration and event handlers\n * @returns Drag and drop state and handlers\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useDragDrop } from '@uploadista/vue';\n * import { ref } from 'vue';\n *\n * const inputRef = ref<HTMLInputElement>();\n *\n * const dragDrop = useDragDrop({\n * accept: ['image/*', '.pdf'],\n * maxFiles: 5,\n * maxFileSize: 10 * 1024 * 1024, // 10MB\n * multiple: true,\n * onFilesReceived: (files) => {\n * console.log('Received files:', files);\n * // Process files with upload composables\n * },\n * onValidationError: (errors) => {\n * console.error('Validation errors:', errors);\n * },\n * });\n *\n * const openFilePicker = () => {\n * inputRef.value?.click();\n * };\n * </script>\n *\n * <template>\n * <div>\n * <div\n * @dragenter=\"dragDrop.onDragEnter\"\n * @dragover=\"dragDrop.onDragOver\"\n * @dragleave=\"dragDrop.onDragLeave\"\n * @drop=\"dragDrop.onDrop\"\n * @click=\"openFilePicker\"\n * :style=\"{\n * border: dragDrop.state.isDragging ? '2px dashed #007bff' : '2px dashed #ccc',\n * backgroundColor: dragDrop.state.isOver ? '#f8f9fa' : 'transparent',\n * padding: '2rem',\n * textAlign: 'center',\n * cursor: 'pointer',\n * }\"\n * >\n * <p v-if=\"dragDrop.state.isDragging\">Drop files here...</p>\n * <p v-else>Drag files here or click to select</p>\n *\n * <div v-if=\"dragDrop.state.errors.length > 0\" style=\"color: red; margin-top: 1rem\">\n * <p v-for=\"(error, index) in dragDrop.state.errors\" :key=\"index\">{{ error }}</p>\n * </div>\n * </div>\n *\n * <input\n * ref=\"inputRef\"\n * type=\"file\"\n * :multiple=\"dragDrop.inputProps.multiple\"\n * :accept=\"dragDrop.inputProps.accept\"\n * @change=\"dragDrop.onInputChange\"\n * style=\"display: none\"\n * />\n * </div>\n * </template>\n * ```\n */\nexport function useDragDrop(options: DragDropOptions = {}) {\n const {\n accept,\n maxFiles,\n maxFileSize,\n multiple = true,\n validator,\n onFilesReceived,\n onValidationError,\n onDragStateChange,\n } = options;\n\n const state = ref<DragDropState>({ ...initialState });\n const dragCounter = ref(0);\n\n const updateState = (update: Partial<DragDropState>) => {\n state.value = { ...state.value, ...update };\n };\n\n const validateFiles = (files: File[]): string[] => {\n const errors: string[] = [];\n\n // Check file count\n if (maxFiles && files.length > maxFiles) {\n errors.push(\n `Maximum ${maxFiles} files allowed. You selected ${files.length} files.`,\n );\n }\n\n // Check individual files\n for (const file of files) {\n // Check file size\n if (maxFileSize && file.size > maxFileSize) {\n const maxSizeMB = (maxFileSize / (1024 * 1024)).toFixed(1);\n const fileSizeMB = (file.size / (1024 * 1024)).toFixed(1);\n errors.push(\n `File \"${file.name}\" (${fileSizeMB}MB) exceeds maximum size of ${maxSizeMB}MB.`,\n );\n }\n\n // Check file type\n if (accept && accept.length > 0) {\n const isAccepted = accept.some((acceptType) => {\n if (acceptType.startsWith(\".\")) {\n // File extension check\n return file.name.toLowerCase().endsWith(acceptType.toLowerCase());\n } else {\n // MIME type check (supports wildcards like image/*)\n if (acceptType.endsWith(\"/*\")) {\n const baseType = acceptType.slice(0, -2);\n return file.type.startsWith(baseType);\n } else {\n return file.type === acceptType;\n }\n }\n });\n\n if (!isAccepted) {\n errors.push(\n `File \"${file.name}\" type \"${file.type}\" is not accepted. Accepted types: ${accept.join(\", \")}.`,\n );\n }\n }\n }\n\n // Run custom validator\n if (validator) {\n const customErrors = validator(files);\n if (customErrors) {\n errors.push(...customErrors);\n }\n }\n\n return errors;\n };\n\n const processFiles = (files: File[]) => {\n const fileArray = Array.from(files);\n const errors = validateFiles(fileArray);\n\n if (errors.length > 0) {\n updateState({ errors, isValid: false });\n onValidationError?.(errors);\n } else {\n updateState({ errors: [], isValid: true });\n onFilesReceived?.(fileArray);\n }\n };\n\n const getFilesFromDataTransfer = (dataTransfer: DataTransfer): File[] => {\n const files: File[] = [];\n\n if (dataTransfer.items) {\n // Use DataTransferItemList interface\n for (let i = 0; i < dataTransfer.items.length; i++) {\n const item = dataTransfer.items[i];\n if (item && item.kind === \"file\") {\n const file = item.getAsFile();\n if (file) {\n files.push(file);\n }\n }\n }\n } else {\n // Fallback to DataTransfer.files\n for (let i = 0; i < dataTransfer.files.length; i++) {\n const file = dataTransfer.files[i];\n if (file) {\n files.push(file);\n }\n }\n }\n\n return files;\n };\n\n const onDragEnter = (event: DragEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n dragCounter.value++;\n\n if (dragCounter.value === 1) {\n updateState({ isDragging: true, isOver: true });\n onDragStateChange?.(true);\n }\n };\n\n const onDragOver = (event: DragEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n // Set dropEffect to indicate what operation is allowed\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = \"copy\";\n }\n };\n\n const onDragLeave = (event: DragEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n dragCounter.value--;\n\n if (dragCounter.value === 0) {\n updateState({ isDragging: false, isOver: false, errors: [] });\n onDragStateChange?.(false);\n }\n };\n\n const onDrop = (event: DragEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n dragCounter.value = 0;\n updateState({ isDragging: false, isOver: false });\n onDragStateChange?.(false);\n\n if (event.dataTransfer) {\n const files = getFilesFromDataTransfer(event.dataTransfer);\n if (files.length > 0) {\n processFiles(files);\n }\n }\n };\n\n const onInputChange = (event: Event) => {\n const input = event.target as HTMLInputElement;\n if (input.files && input.files.length > 0) {\n const files = Array.from(input.files);\n processFiles(files);\n }\n\n // Reset input value to allow selecting the same files again\n input.value = \"\";\n };\n\n const reset = () => {\n state.value = { ...initialState };\n dragCounter.value = 0;\n };\n\n const inputProps = computed(() => ({\n type: \"file\" as const,\n multiple,\n accept: accept?.join(\", \"),\n }));\n\n return {\n state: readonly(state),\n onDragEnter,\n onDragOver,\n onDragLeave,\n onDrop,\n onInputChange,\n inputProps,\n processFiles,\n reset,\n };\n}\n","import { EventType, type FlowEvent } from \"@uploadista/core/flow\";\nimport { type UploadEvent, UploadEventType } from \"@uploadista/core/types\";\n\n/**\n * Type guard to check if an event is a flow event\n */\nexport function isFlowEvent(event: unknown): event is FlowEvent {\n if (typeof event !== \"object\" || event === null) return false;\n if (!(\"eventType\" in event)) return false;\n const e = event as { eventType: unknown };\n return (\n e.eventType === EventType.JobStart ||\n e.eventType === EventType.JobEnd ||\n e.eventType === EventType.FlowStart ||\n e.eventType === EventType.FlowEnd ||\n e.eventType === EventType.FlowError ||\n e.eventType === EventType.FlowPause ||\n e.eventType === EventType.FlowCancel ||\n e.eventType === EventType.NodeStart ||\n e.eventType === EventType.NodeEnd ||\n e.eventType === EventType.NodePause ||\n e.eventType === EventType.NodeResume ||\n e.eventType === EventType.NodeError ||\n e.eventType === EventType.NodeStream ||\n e.eventType === EventType.NodeResponse\n );\n}\n\n/**\n * Type guard to check if an event is an upload event\n */\nexport function isUploadEvent(event: unknown): event is UploadEvent {\n if (typeof event !== \"object\" || event === null) return false;\n if (!(\"type\" in event)) return false;\n const e = event as { type: unknown };\n return (\n e.type === UploadEventType.UPLOAD_STARTED ||\n e.type === UploadEventType.UPLOAD_PROGRESS ||\n e.type === UploadEventType.UPLOAD_COMPLETE ||\n e.type === UploadEventType.UPLOAD_FAILED ||\n e.type === UploadEventType.UPLOAD_VALIDATION_SUCCESS ||\n e.type === UploadEventType.UPLOAD_VALIDATION_FAILED ||\n e.type === UploadEventType.UPLOAD_VALIDATION_WARNING\n );\n}\n","import type { UploadistaEvent } from \"@uploadista/client-browser\";\nimport type {\n FlowEventFlowCancel,\n FlowEventFlowEnd,\n FlowEventFlowError,\n FlowEventFlowPause,\n FlowEventFlowStart,\n FlowEventJobEnd,\n FlowEventJobStart,\n FlowEventNodeEnd,\n FlowEventNodeError,\n FlowEventNodePause,\n FlowEventNodeResume,\n FlowEventNodeStart,\n} from \"@uploadista/core/flow\";\nimport { EventType } from \"@uploadista/core/flow\";\nimport { onBeforeUnmount, onMounted } from \"vue\";\nimport { isFlowEvent } from \"./eventUtils\";\nimport { useUploadistaClient } from \"./useUploadistaClient\";\n\n/**\n * Options for handling flow execution events.\n *\n * All callbacks are optional - only provide handlers for events you care about.\n */\nexport interface UseFlowEventsOptions {\n /** Called when a job starts execution */\n onJobStart?: (event: FlowEventJobStart) => void;\n /** Called when a job completes (success or failure) */\n onJobEnd?: (event: FlowEventJobEnd) => void;\n /** Called when a flow begins execution */\n onFlowStart?: (event: FlowEventFlowStart) => void;\n /** Called when a flow completes successfully */\n onFlowEnd?: (event: FlowEventFlowEnd) => void;\n /** Called when a flow encounters an error */\n onFlowError?: (event: FlowEventFlowError) => void;\n /** Called when a flow is paused by user request */\n onFlowPause?: (event: FlowEventFlowPause) => void;\n /** Called when a flow is cancelled by user request */\n onFlowCancel?: (event: FlowEventFlowCancel) => void;\n /** Called when a node starts processing */\n onNodeStart?: (event: FlowEventNodeStart) => void;\n /** Called when a node completes successfully */\n onNodeEnd?: (event: FlowEventNodeEnd) => void;\n /** Called when a node pauses (waiting for additional data) */\n onNodePause?: (event: FlowEventNodePause) => void;\n /** Called when a paused node resumes execution */\n onNodeResume?: (event: FlowEventNodeResume) => void;\n /** Called when a node encounters an error */\n onNodeError?: (event: FlowEventNodeError) => void;\n}\n\n/**\n * Structured composable for handling flow execution events with type-safe callbacks.\n *\n * This composable provides a clean API for listening to specific flow events without\n * needing to manually filter events or use type guards.\n *\n * Must be used within UploadistaProvider.\n *\n * @param options - Object with optional callbacks for each flow event type\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useFlowEvents } from '@uploadista/vue';\n *\n * useFlowEvents({\n * onFlowStart: (event) => {\n * console.log('Flow started:', event.flowId);\n * },\n * onNodeStart: (event) => {\n * console.log('Node started:', event.nodeName);\n * },\n * onNodeEnd: (event) => {\n * console.log('Node completed:', event.nodeName, event.result);\n * },\n * onFlowEnd: (event) => {\n * console.log('Flow completed with outputs:', event.outputs);\n * },\n * onFlowError: (event) => {\n * console.error('Flow failed:', event.error);\n * },\n * });\n * </script>\n *\n * <template>\n * <div>Monitoring flow execution...</div>\n * </template>\n * ```\n */\nexport function useFlowEvents(options: UseFlowEventsOptions): void {\n const { subscribeToEvents } = useUploadistaClient();\n let unsubscribe: (() => void) | null = null;\n\n onMounted(() => {\n unsubscribe = subscribeToEvents((event: UploadistaEvent) => {\n // Only handle flow events\n if (!isFlowEvent(event)) return;\n\n // Route to appropriate callback based on event type\n switch (event.eventType) {\n case EventType.JobStart:\n options.onJobStart?.(event);\n break;\n case EventType.JobEnd:\n options.onJobEnd?.(event);\n break;\n case EventType.FlowStart:\n options.onFlowStart?.(event);\n break;\n case EventType.FlowEnd:\n options.onFlowEnd?.(event);\n break;\n case EventType.FlowError:\n options.onFlowError?.(event);\n break;\n case EventType.FlowPause:\n options.onFlowPause?.(event);\n break;\n case EventType.FlowCancel:\n options.onFlowCancel?.(event);\n break;\n case EventType.NodeStart:\n options.onNodeStart?.(event);\n break;\n case EventType.NodeEnd:\n options.onNodeEnd?.(event);\n break;\n case EventType.NodePause:\n options.onNodePause?.(event);\n break;\n case EventType.NodeResume:\n options.onNodeResume?.(event);\n break;\n case EventType.NodeError:\n options.onNodeError?.(event);\n break;\n }\n });\n });\n\n onBeforeUnmount(() => {\n unsubscribe?.();\n });\n}\n","import type { UploadistaEvent } from \"@uploadista/client-browser\";\nimport { UploadEventType } from \"@uploadista/core/types\";\nimport { onBeforeUnmount, onMounted } from \"vue\";\nimport { isUploadEvent } from \"./eventUtils\";\nimport { useUploadistaClient } from \"./useUploadistaClient\";\n\n/**\n * Upload progress event data\n */\nexport interface UploadProgressEventData {\n id: string;\n progress: number;\n total: number;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload started/complete event data (contains full UploadFile)\n */\nexport interface UploadFileEventData {\n // This will contain the full UploadFile schema\n [key: string]: unknown;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload failed event data\n */\nexport interface UploadFailedEventData {\n id: string;\n error: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation success event data\n */\nexport interface UploadValidationSuccessEventData {\n id: string;\n validationType: \"checksum\" | \"mimetype\";\n algorithm?: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation failed event data\n */\nexport interface UploadValidationFailedEventData {\n id: string;\n reason: string;\n expected: string;\n actual: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Upload validation warning event data\n */\nexport interface UploadValidationWarningEventData {\n id: string;\n message: string;\n flow?: {\n flowId: string;\n nodeId: string;\n jobId: string;\n };\n}\n\n/**\n * Options for handling upload events.\n *\n * All callbacks are optional - only provide handlers for events you care about.\n */\nexport interface UseUploadEventsOptions {\n /** Called when an upload starts */\n onUploadStarted?: (data: UploadFileEventData) => void;\n /** Called with upload progress updates */\n onUploadProgress?: (data: UploadProgressEventData) => void;\n /** Called when an upload completes successfully */\n onUploadComplete?: (data: UploadFileEventData) => void;\n /** Called when an upload fails */\n onUploadFailed?: (data: UploadFailedEventData) => void;\n /** Called when upload validation succeeds */\n onUploadValidationSuccess?: (data: UploadValidationSuccessEventData) => void;\n /** Called when upload validation fails */\n onUploadValidationFailed?: (data: UploadValidationFailedEventData) => void;\n /** Called when upload validation produces a warning */\n onUploadValidationWarning?: (data: UploadValidationWarningEventData) => void;\n}\n\n/**\n * Structured composable for handling upload events with type-safe callbacks.\n *\n * This composable provides a clean API for listening to specific upload events without\n * needing to manually filter events or use type guards.\n *\n * Must be used within UploadistaProvider.\n *\n * @param options - Object with optional callbacks for each upload event type\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useUploadEvents } from '@uploadista/vue';\n *\n * useUploadEvents({\n * onUploadStarted: (data) => {\n * console.log('Upload started:', data.id);\n * },\n * onUploadProgress: (data) => {\n * const percent = (data.progress / data.total) * 100;\n * console.log(`Upload progress: ${percent}%`);\n * },\n * onUploadComplete: (data) => {\n * console.log('Upload completed:', data);\n * },\n * onUploadFailed: (data) => {\n * console.error('Upload failed:', data.error);\n * },\n * });\n * </script>\n *\n * <template>\n * <div>Monitoring uploads...</div>\n * </template>\n * ```\n */\nexport function useUploadEvents(options: UseUploadEventsOptions): void {\n const { subscribeToEvents } = useUploadistaClient();\n let unsubscribe: (() => void) | null = null;\n\n onMounted(() => {\n unsubscribe = subscribeToEvents((event: UploadistaEvent) => {\n // Only handle upload events\n if (!isUploadEvent(event)) return;\n\n // Route to appropriate callback based on event type\n // Note: flow context is at the top level of the event, not inside data\n const flowContext = \"flow\" in event ? event.flow : undefined;\n\n switch (event.type) {\n case UploadEventType.UPLOAD_STARTED:\n options.onUploadStarted?.({\n ...(event.data as unknown as Omit<UploadFileEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_PROGRESS:\n options.onUploadProgress?.({\n ...(event.data as unknown as Omit<UploadProgressEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_COMPLETE:\n options.onUploadComplete?.({\n ...(event.data as unknown as Omit<UploadFileEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_FAILED:\n options.onUploadFailed?.({\n ...(event.data as unknown as Omit<UploadFailedEventData, \"flow\">),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_SUCCESS:\n options.onUploadValidationSuccess?.({\n ...(event.data as unknown as Omit<\n UploadValidationSuccessEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_FAILED:\n options.onUploadValidationFailed?.({\n ...(event.data as unknown as Omit<\n UploadValidationFailedEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n case UploadEventType.UPLOAD_VALIDATION_WARNING:\n options.onUploadValidationWarning?.({\n ...(event.data as unknown as Omit<\n UploadValidationWarningEventData,\n \"flow\"\n >),\n flow: flowContext,\n });\n break;\n }\n });\n });\n\n onBeforeUnmount(() => {\n unsubscribe?.();\n });\n}\n","import type { UploadistaEvent } from \"@uploadista/client-browser\";\nimport { onBeforeUnmount, onMounted } from \"vue\";\nimport { useUploadistaClient } from \"./useUploadistaClient\";\n\n/**\n * Simple composable that subscribes to all Uploadista events (both flow and upload events).\n *\n * This is a low-level composable that provides access to all events. For more structured\n * event handling, consider using `useFlowEvents` or `useUploadEvents` instead.\n *\n * Must be used within UploadistaProvider.\n *\n * @param callback - Function called for every event emitted by the Uploadista client\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useUploadistaEvents, isFlowEvent, isUploadEvent } from '@uploadista/vue';\n *\n * useUploadistaEvents((event) => {\n * if (isFlowEvent(event)) {\n * console.log('Flow event:', event.eventType);\n * } else if (isUploadEvent(event)) {\n * console.log('Upload event:', event.type);\n * }\n * });\n * </script>\n *\n * <template>\n * <div>Listening to all events...</div>\n * </template>\n * ```\n */\nexport function useUploadistaEvents(\n callback: (event: UploadistaEvent) => void,\n): void {\n const { subscribeToEvents } = useUploadistaClient();\n let unsubscribe: (() => void) | null = null;\n\n onMounted(() => {\n unsubscribe = subscribeToEvents(callback);\n });\n\n onBeforeUnmount(() => {\n unsubscribe?.();\n });\n}\n","import type {\n BrowserUploadInput,\n FlowUploadItem,\n MultiFlowUploadOptions,\n MultiFlowUploadState,\n} from \"@uploadista/client-browser\";\nimport { computed, readonly, ref } from \"vue\";\nimport { useUploadistaClient } from \"./useUploadistaClient\";\n\n/**\n * Vue composable for uploading multiple files through a flow.\n *\n * Must be used within a component tree that has the Uploadista plugin installed.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useMultiFlowUpload } from '@uploadista/vue';\n *\n * const multiFlowUpload = useMultiFlowUpload({\n * flowConfig: {\n * flowId: \"batch-upload-flow\",\n * inputNodeId: \"upload-node\",\n * storageId: \"my-storage\",\n * },\n * maxConcurrent: 3,\n * onComplete: (items) => {\n * console.log(\"All uploads complete:\", items);\n * },\n * });\n *\n * const handleFileChange = (event: Event) => {\n * const files = (event.target as HTMLInputElement).files;\n * if (files) {\n * multiFlowUpload.addFiles(files);\n * multiFlowUpload.startUpload();\n * }\n * };\n * </script>\n *\n * <template>\n * <div>\n * <input type=\"file\" multiple @change=\"handleFileChange\" />\n *\n * <div v-for=\"item in multiFlowUpload.state.items\" :key=\"item.id\">\n * <span>{{ item.file.name }}</span>\n * <progress :value=\"item.progress\" :max=\"100\" />\n * <button\n * v-if=\"item.status === 'uploading'\"\n * @click=\"multiFlowUpload.abortUpload(item.id)\"\n * >\n * Cancel\n * </button>\n * </div>\n * </div>\n * </template>\n * ```\n */\nexport function useMultiFlowUpload(\n options: MultiFlowUploadOptions<BrowserUploadInput>,\n) {\n const client = useUploadistaClient();\n const items = ref<FlowUploadItem<BrowserUploadInput>[]>([]);\n const abortFns = ref<Map<string, () => void>>(new Map());\n const queue = ref<string[]>([]);\n const activeCount = ref(0);\n\n const maxConcurrent = options.maxConcurrent ?? 3;\n\n const calculateTotalProgress = (\n items: FlowUploadItem<BrowserUploadInput>[],\n ) => {\n if (items.length === 0) return 0;\n const totalProgress = items.reduce((sum, item) => sum + item.progress, 0);\n return Math.round(totalProgress / items.length);\n };\n\n const processQueue = async () => {\n if (activeCount.value >= maxConcurrent || queue.value.length === 0) {\n return;\n }\n\n const itemId = queue.value.shift();\n if (!itemId) return;\n\n const item = items.value.find((i) => i.id === itemId);\n if (!item || item.status !== \"pending\") {\n processQueue();\n return;\n }\n\n activeCount.value++;\n\n items.value = items.value.map((i) =>\n i.id === itemId ? { ...i, status: \"uploading\" as const } : i,\n );\n\n try {\n const { abort, jobId } = await client.client.uploadWithFlow(\n item.file,\n options.flowConfig,\n {\n onJobStart: (jobId: string) => {\n items.value = items.value.map((i) =>\n i.id === itemId ? { ...i, jobId } : i,\n );\n },\n onProgress: (\n _uploadId: string,\n bytesUploaded: number,\n totalBytes: number | null,\n ) => {\n const progress = totalBytes\n ? Math.round((bytesUploaded / totalBytes) * 100)\n : 0;\n\n items.value = items.value.map((i) => {\n if (i.id === itemId) {\n const updated = {\n ...i,\n progress,\n bytesUploaded,\n totalBytes: totalBytes || 0,\n };\n options.onItemProgress?.(updated);\n return updated;\n }\n return i;\n });\n },\n onSuccess: (outputs) => {\n items.value = items.value.map((i) => {\n if (i.id === itemId) {\n const updated = {\n ...i,\n status: \"success\" as const,\n result: outputs,\n progress: 100,\n };\n options.onItemSuccess?.(updated);\n return updated;\n }\n return i;\n });\n\n // Check if all uploads are complete\n const allComplete = items.value.every(\n (i) =>\n i.status === \"success\" ||\n i.status === \"error\" ||\n i.status === \"aborted\",\n );\n if (allComplete) {\n options.onComplete?.(items.value);\n }\n\n abortFns.value.delete(itemId);\n activeCount.value--;\n processQueue();\n },\n onError: (error: Error) => {\n items.value = items.value.map((i) => {\n if (i.id === itemId) {\n const updated = { ...i, status: \"error\" as const, error };\n options.onItemError?.(updated, error);\n return updated;\n }\n return i;\n });\n\n // Check if all uploads are complete\n const allComplete = items.value.every(\n (i) =>\n i.status === \"success\" ||\n i.status === \"error\" ||\n i.status === \"aborted\",\n );\n if (allComplete) {\n options.onComplete?.(items.value);\n }\n\n abortFns.value.delete(itemId);\n activeCount.value--;\n processQueue();\n },\n onShouldRetry: options.onShouldRetry,\n },\n );\n\n abortFns.value.set(itemId, abort);\n\n items.value = items.value.map((i) =>\n i.id === itemId ? { ...i, jobId } : i,\n );\n } catch (error) {\n items.value = items.value.map((i) =>\n i.id === itemId\n ? { ...i, status: \"error\" as const, error: error as Error }\n : i,\n );\n\n activeCount.value--;\n processQueue();\n }\n };\n\n const addFiles = (files: File[] | FileList) => {\n const fileArray = Array.from(files);\n const newItems: FlowUploadItem<BrowserUploadInput>[] = fileArray.map(\n (file) => ({\n id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,\n file,\n status: \"pending\",\n progress: 0,\n bytesUploaded: 0,\n totalBytes: file.size,\n error: null,\n result: null,\n jobId: null,\n }),\n );\n\n items.value = [...items.value, ...newItems];\n };\n\n const removeFile = (id: string) => {\n const abortFn = abortFns.value.get(id);\n if (abortFn) {\n abortFn();\n abortFns.value.delete(id);\n }\n\n items.value = items.value.filter((item) => item.id !== id);\n queue.value = queue.value.filter((queueId) => queueId !== id);\n };\n\n const startUpload = () => {\n const pendingItems = items.value.filter(\n (item) => item.status === \"pending\",\n );\n queue.value.push(...pendingItems.map((item) => item.id));\n\n for (let i = 0; i < maxConcurrent; i++) {\n processQueue();\n }\n };\n\n const abortUpload = (id: string) => {\n const abortFn = abortFns.value.get(id);\n if (abortFn) {\n abortFn();\n abortFns.value.delete(id);\n\n items.value = items.value.map((item) =>\n item.id === id ? { ...item, status: \"aborted\" as const } : item,\n );\n\n activeCount.value--;\n processQueue();\n }\n };\n\n const abortAll = () => {\n for (const abortFn of abortFns.value.values()) {\n abortFn();\n }\n abortFns.value.clear();\n queue.value = [];\n activeCount.value = 0;\n\n items.value = items.value.map((item) =>\n item.status === \"uploading\"\n ? { ...item, status: \"aborted\" as const }\n : item,\n );\n };\n\n const clear = () => {\n abortAll();\n items.value = [];\n };\n\n const retryUpload = (id: string) => {\n items.value = items.value.map((item) =>\n item.id === id\n ? {\n ...item,\n status: \"pending\" as const,\n progress: 0,\n bytesUploaded: 0,\n error: null,\n }\n : item,\n );\n\n queue.value.push(id);\n processQueue();\n };\n\n const state = computed<MultiFlowUploadState<BrowserUploadInput>>(() => ({\n items: items.value,\n totalProgress: calculateTotalProgress(items.value),\n activeUploads: items.value.filter((item) => item.status === \"uploading\")\n .length,\n completedUploads: items.value.filter((item) => item.status === \"success\")\n .length,\n failedUploads: items.value.filter((item) => item.status === \"error\").length,\n }));\n\n return {\n state: readonly(state),\n addFiles,\n removeFile,\n startUpload,\n abortUpload,\n abortAll,\n clear,\n retryUpload,\n isUploading: computed(() => state.value.activeUploads > 0),\n };\n}\n","import type { UploadOptions } from \"@uploadista/client-browser\";\nimport type { UploadMetrics } from \"@uploadista/client-core\";\nimport type { UploadFile } from \"@uploadista/core/types\";\nimport { computed, readonly, ref } from \"vue\";\nimport type { UploadInput, UploadState, UploadStatus } from \"./useUpload\";\nimport { useUploadistaClient } from \"./useUploadistaClient\";\n\nexport interface UploadItem {\n id: string;\n file: UploadInput;\n state: UploadState;\n}\n\nexport interface MultiUploadOptions\n extends Omit<UploadOptions, \"onSuccess\" | \"onError\" | \"onProgress\"> {\n /**\n * Maximum number of concurrent uploads\n */\n maxConcurrent?: number;\n\n /**\n * Called when an individual file upload starts\n */\n onUploadStart?: (item: UploadItem) => void;\n\n /**\n * Called when an individual file upload progresses\n */\n onUploadProgress?: (\n item: UploadItem,\n progress: number,\n bytesUploaded: number,\n totalBytes: number | null,\n ) => void;\n\n /**\n * Called when an individual file upload succeeds\n */\n onUploadSuccess?: (item: UploadItem, result: UploadFile) => void;\n\n /**\n * Called when an individual file upload fails\n */\n onUploadError?: (item: UploadItem, error: Error) => void;\n\n /**\n * Called when all uploads complete (successfully or with errors)\n */\n onComplete?: (results: {\n successful: UploadItem[];\n failed: UploadItem[];\n total: number;\n }) => void;\n}\n\nexport interface MultiUploadState {\n /**\n * Total number of uploads\n */\n total: number;\n\n /**\n * Number of completed uploads (successful + failed)\n */\n completed: number;\n\n /**\n * Number of successful uploads\n */\n successful: number;\n\n /**\n * Number of failed uploads\n */\n failed: number;\n\n /**\n * Number of currently uploading files\n */\n uploading: number;\n\n /**\n * Overall progress as a percentage (0-100)\n */\n progress: number;\n\n /**\n * Total bytes uploaded across all files\n */\n totalBytesUploaded: number;\n\n /**\n * Total bytes to upload across all files\n */\n totalBytes: number;\n\n /**\n * Whether any uploads are currently active\n */\n isUploading: boolean;\n\n /**\n * Whether all uploads have completed\n */\n isComplete: boolean;\n}\n\n/**\n * Vue composable for managing multiple file uploads with queue management,\n * concurrent upload limits, and batch operations.\n *\n * Must be used within a component tree that has the Uploadista plugin installed.\n *\n * @param options - Multi-upload configuration and event handlers\n * @returns Multi-upload state and control methods\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useMultiUpload } from '@uploadista/vue';\n *\n * const multiUpload = useMultiUpload({\n * maxConcurrent: 3,\n * onUploadSuccess: (item, result) => {\n * console.log(`${item.file.name} uploaded successfully`);\n * },\n * onComplete: (results) => {\n * console.log(`Upload batch complete: ${results.successful.length}/${results.total} successful`);\n * },\n * });\n *\n * const handleFileChange = (event: Event) => {\n * const files = (event.target as HTMLInputElement).files;\n * if (files) {\n * multiUpload.addFiles(Array.from(files));\n * }\n * };\n * </script>\n *\n * <template>\n * <div>\n * <input type=\"file\" multiple @change=\"handleFileChange\" />\n *\n * <div>Progress: {{ multiUpload.state.progress }}%</div>\n * <div>\n * {{ multiUpload.state.uploading }} uploading,\n * {{ multiUpload.state.successful }} successful,\n * {{ multiUpload.state.failed }} failed\n * </div>\n *\n * <button @click=\"multiUpload.startAll\" :disabled=\"multiUpload.state.isUploading\">\n * Start All\n * </button>\n * <button @click=\"multiUpload.abortAll\" :disabled=\"!multiUpload.state.isUploading\">\n * Abort All\n * </button>\n * <button @click=\"multiUpload.retryFailed\" :disabled=\"multiUpload.state.failed === 0\">\n * Retry Failed\n * </button>\n *\n * <div v-for=\"item in multiUpload.items\" :key=\"item.id\">\n * {{ item.file.name }}: {{ item.state.status }} ({{ item.state.progress }}%)\n * </div>\n * </div>\n * </template>\n * ```\n */\nexport function useMultiUpload(options: MultiUploadOptions = {}) {\n const uploadClient = useUploadistaClient();\n const { maxConcurrent = 3 } = options;\n const items = ref<UploadItem[]>([]);\n const nextId = ref(0);\n const activeUploads = ref(new Set<string>());\n\n // Store abort controllers for each upload\n const abortControllers = ref<Map<string, { abort: () => void }>>(new Map());\n\n // Generate a unique ID for each upload item\n const generateId = () => {\n return `upload-${Date.now()}-${nextId.value++}`;\n };\n\n // State update callback for individual uploads\n const onStateUpdate = (id: string, state: Partial<UploadState>) => {\n items.value = items.value.map((item) =>\n item.id === id ? { ...item, state: { ...item.state, ...state } } : item,\n );\n };\n\n // Check if all uploads are complete and trigger completion callback\n const checkForCompletion = () => {\n const allComplete = items.value.every((item) =>\n [\"success\", \"error\", \"aborted\"].includes(item.state.status),\n );\n\n if (allComplete && items.value.length > 0) {\n const successful = items.value.filter(\n (item) => item.state.status === \"success\",\n );\n const failed = items.value.filter((item) =>\n [\"error\", \"aborted\"].includes(item.state.status),\n );\n\n options.onComplete?.({\n successful,\n failed,\n total: items.value.length,\n });\n }\n };\n\n // Start the next available upload if we have capacity\n const startNextUpload = async () => {\n if (activeUploads.value.size >= maxConcurrent) {\n return;\n }\n\n const nextItem = items.value.find(\n (item) =>\n item.state.status === \"idle\" && !activeUploads.value.has(item.id),\n );\n\n if (!nextItem) {\n return;\n }\n\n activeUploads.value.add(nextItem.id);\n options.onUploadStart?.(nextItem);\n\n // Update state to uploading\n onStateUpdate(nextItem.id, { status: \"uploading\" });\n\n try {\n const controller = await uploadClient.client.upload(nextItem.file, {\n metadata: options.metadata,\n uploadLengthDeferred: options.uploadLengthDeferred,\n uploadSize: options.uploadSize,\n\n onProgress: (\n _uploadId: string,\n bytesUploaded: number,\n totalBytes: number | null,\n ) => {\n const progress = totalBytes\n ? Math.round((bytesUploaded / totalBytes) * 100)\n : 0;\n\n onStateUpdate(nextItem.id, {\n progress,\n bytesUploaded,\n totalBytes,\n });\n\n options.onUploadProgress?.(\n nextItem,\n progress,\n bytesUploaded,\n totalBytes,\n );\n },\n\n onChunkComplete: () => {\n // Optional: could expose this as an option\n },\n\n onSuccess: (result: UploadFile) => {\n onStateUpdate(nextItem.id, {\n status: \"success\",\n result,\n progress: 100,\n });\n\n const updatedItem = {\n ...nextItem,\n state: { ...nextItem.state, status: \"success\" as const, result },\n };\n options.onUploadSuccess?.(updatedItem, result);\n\n // Mark complete and start next\n activeUploads.value.delete(nextItem.id);\n abortControllers.value.delete(nextItem.id);\n startNextUpload();\n checkForCompletion();\n },\n\n onError: (error: Error) => {\n onStateUpdate(nextItem.id, {\n status: \"error\",\n error,\n });\n\n const updatedItem = {\n ...nextItem,\n state: { ...nextItem.state, status: \"error\" as const, error },\n };\n options.onUploadError?.(updatedItem, error);\n\n // Mark complete and start next\n activeUploads.value.delete(nextItem.id);\n abortControllers.value.delete(nextItem.id);\n startNextUpload();\n checkForCompletion();\n },\n\n onShouldRetry: options.onShouldRetry,\n });\n\n // Store abort controller\n abortControllers.value.set(nextItem.id, controller);\n } catch (error) {\n onStateUpdate(nextItem.id, {\n status: \"error\",\n error: error as Error,\n });\n\n const updatedItem = {\n ...nextItem,\n state: {\n ...nextItem.state,\n status: \"error\" as const,\n error: error as Error,\n },\n };\n options.onUploadError?.(updatedItem, error as Error);\n\n // Mark complete and start next\n activeUploads.value.delete(nextItem.id);\n abortControllers.value.delete(nextItem.id);\n startNextUpload();\n checkForCompletion();\n }\n };\n\n // Calculate overall state\n const state = computed<MultiUploadState>(() => {\n const itemsList = items.value;\n return {\n total: itemsList.length,\n completed: itemsList.filter((item) =>\n [\"success\", \"error\", \"aborted\"].includes(item.state.status),\n ).length,\n successful: itemsList.filter((item) => item.state.status === \"success\")\n .length,\n failed: itemsList.filter((item) =>\n [\"error\", \"aborted\"].includes(item.state.status),\n ).length,\n uploading: itemsList.filter((item) => item.state.status === \"uploading\")\n .length,\n progress:\n itemsList.length > 0\n ? Math.round(\n itemsList.reduce((sum, item) => sum + item.state.progress, 0) /\n itemsList.length,\n )\n : 0,\n totalBytesUploaded: itemsList.reduce(\n (sum, item) => sum + item.state.bytesUploaded,\n 0,\n ),\n totalBytes: itemsList.reduce(\n (sum, item) => sum + (item.state.totalBytes || 0),\n 0,\n ),\n isUploading: itemsList.some((item) => item.state.status === \"uploading\"),\n isComplete:\n itemsList.length > 0 &&\n itemsList.every((item) =>\n [\"success\", \"error\", \"aborted\"].includes(item.state.status),\n ),\n };\n });\n\n const addFiles = (files: UploadInput[]) => {\n const newItems: UploadItem[] = files.map((file) => {\n const id = generateId();\n return {\n id,\n file,\n state: {\n status: \"idle\",\n progress: 0,\n bytesUploaded: 0,\n totalBytes: file instanceof File ? file.size : null,\n error: null,\n result: null,\n },\n };\n });\n\n items.value = [...items.value, ...newItems];\n };\n\n const removeItem = (id: string) => {\n const item = items.value.find((i) => i.id === id);\n if (item && item.state.status === \"uploading\") {\n // Abort before removing\n const controller = abortControllers.value.get(id);\n if (controller) {\n controller.abort();\n abortControllers.value.delete(id);\n }\n }\n\n items.value = items.value.filter((item) => item.id !== id);\n activeUploads.value.delete(id);\n };\n\n const abortUpload = (id: string) => {\n const item = items.value.find((i) => i.id === id);\n if (item && item.state.status === \"uploading\") {\n const controller = abortControllers.value.get(id);\n if (controller) {\n controller.abort();\n abortControllers.value.delete(id);\n }\n\n activeUploads.value.delete(id);\n\n items.value = items.value.map((i) =>\n i.id === id\n ? { ...i, state: { ...i.state, status: \"aborted\" as const } }\n : i,\n );\n\n // Try to start next upload in queue\n startNextUpload();\n }\n };\n\n const retryUpload = (id: string) => {\n const item = items.value.find((i) => i.id === id);\n if (item && [\"error\", \"aborted\"].includes(item.state.status)) {\n items.value = items.value.map((i) =>\n i.id === id\n ? {\n ...i,\n state: { ...i.state, status: \"idle\" as const, error: null },\n }\n : i,\n );\n\n // Auto-start the upload\n setTimeout(() => startNextUpload(), 0);\n }\n };\n\n const startAll = () => {\n // Start as many uploads as we can up to the concurrent limit\n const idleItems = items.value.filter(\n (item) => item.state.status === \"idle\",\n );\n const slotsAvailable = maxConcurrent - activeUploads.value.size;\n const itemsToStart = idleItems.slice(0, slotsAvailable);\n\n for (const _item of itemsToStart) {\n startNextUpload();\n }\n };\n\n const abortAll = () => {\n items.value\n .filter((item) => item.state.status === \"uploading\")\n .forEach((item) => {\n const controller = abortControllers.value.get(item.id);\n if (controller) {\n controller.abort();\n abortControllers.value.delete(item.id);\n }\n });\n\n activeUploads.value.clear();\n\n // Update all uploading items to aborted status\n items.value = items.value.map((item) =>\n item.state.status === \"uploading\"\n ? { ...item, state: { ...item.state, status: \"aborted\" as const } }\n : item,\n );\n };\n\n const retryFailed = () => {\n const failedItems = items.value.filter((item) =>\n [\"error\", \"aborted\"].includes(item.state.status),\n );\n\n if (failedItems.length > 0) {\n items.value = items.value.map((item) =>\n failedItems.some((f) => f.id === item.id)\n ? {\n ...item,\n state: { ...item.state, status: \"idle\" as const, error: null },\n }\n : item,\n );\n\n // Auto-start uploads if we have capacity\n setTimeout(startAll, 0);\n }\n };\n\n const clearCompleted = () => {\n items.value = items.value.filter(\n (item) => ![\"success\", \"error\", \"aborted\"].includes(item.state.status),\n );\n };\n\n const clearAll = () => {\n abortAll();\n items.value = [];\n activeUploads.value.clear();\n };\n\n const getItemsByStatus = (status: UploadStatus) => {\n return items.value.filter((item) => item.state.status === status);\n };\n\n // Create aggregated metrics object that delegates to the upload client\n const metrics: UploadMetrics = {\n getInsights: () => uploadClient.client.getChunkingInsights(),\n exportMetrics: () => uploadClient.client.exportMetrics(),\n getNetworkMetrics: () => uploadClient.client.getNetworkMetrics(),\n getNetworkCondition: () => uploadClient.client.getNetworkCondition(),\n resetMetrics: () => uploadClient.client.resetMetrics(),\n };\n\n return {\n state: readonly(state),\n items: readonly(items),\n addFiles,\n removeItem,\n removeFile: removeItem, // Alias for consistency\n startAll,\n abortUpload,\n abortAll,\n retryUpload,\n retryFailed,\n clearCompleted,\n clearAll,\n getItemsByStatus,\n metrics,\n };\n}\n","import type {\n ChunkMetrics,\n PerformanceInsights,\n UploadSessionMetrics,\n} from \"@uploadista/client-browser\";\nimport type {\n UploadFunction,\n UploadMetrics,\n UploadOptions,\n} from \"@uploadista/client-core\";\nimport {\n UploadManager,\n type UploadState,\n type UploadStatus,\n} from \"@uploadista/client-core\";\nimport { computed, onUnmounted, ref } from \"vue\";\nimport { useUploadistaClient } from \"./useUploadistaClient\";\n\n// Re-export types from core for convenience\nexport type { UploadState, UploadStatus };\nexport type UploadInput = File | Blob;\nexport type { ChunkMetrics, PerformanceInsights, UploadSessionMetrics };\n\nconst initialState: UploadState = {\n status: \"idle\",\n progress: 0,\n bytesUploaded: 0,\n totalBytes: null,\n error: null,\n result: null,\n};\n\n/**\n * Vue composable for managing individual file uploads with full state management.\n * Provides upload progress tracking, error handling, abort functionality, and retry logic.\n *\n * Must be used within a component tree that has the Uploadista plugin installed.\n *\n * @param options - Upload configuration and event handlers\n * @returns Upload state and control methods\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useUpload } from '@uploadista/vue';\n *\n * const upload = useUpload({\n * onSuccess: (result) => console.log('Upload complete:', result),\n * onError: (error) => console.error('Upload failed:', error),\n * onProgress: (progress) => console.log('Progress:', progress + '%'),\n * });\n *\n * const handleFileChange = (event: Event) => {\n * const file = (event.target as HTMLInputElement).files?.[0];\n * if (file) upload.upload(file);\n * };\n * </script>\n *\n * <template>\n * <div>\n * <input type=\"file\" @change=\"handleFileChange\" />\n * <div v-if=\"upload.isUploading\">Progress: {{ upload.state.progress }}%</div>\n * <div v-if=\"upload.state.error\">Error: {{ upload.state.error.message }}</div>\n * <button v-if=\"upload.canRetry\" @click=\"upload.retry\">Retry</button>\n * <button @click=\"upload.abort\" :disabled=\"!upload.isUploading\">Abort</button>\n * </div>\n * </template>\n * ```\n */\nexport function useUpload(options: UploadOptions = {}) {\n const uploadistaClient = useUploadistaClient();\n const state = ref<UploadState>({ ...initialState });\n let manager: UploadManager | null = null;\n\n // Wrap the client's upload method to match UploadFunction signature\n const uploadFn: UploadFunction<UploadInput> = (\n input: UploadInput,\n opts: UploadOptions,\n ) => uploadistaClient.client.upload(input, opts);\n\n // Create UploadManager instance\n manager = new UploadManager(\n uploadFn,\n {\n onStateChange: (newState: UploadState) => {\n state.value = newState;\n },\n onProgress: options.onProgress,\n onChunkComplete: options.onChunkComplete,\n onSuccess: options.onSuccess,\n onError: options.onError,\n onAbort: options.onAbort,\n },\n options,\n );\n\n // Clean up manager when component unmounts\n onUnmounted(() => {\n manager?.cleanup();\n });\n\n // Upload function\n const upload = (file: UploadInput) => {\n manager?.upload(file);\n };\n\n // Abort function\n const abort = () => {\n manager?.abort();\n };\n\n // Reset function\n const reset = () => {\n manager?.reset();\n };\n\n // Retry function\n const retry = () => {\n manager?.retry();\n };\n\n // Computed properties\n const isUploading = computed(() => state.value.status === \"uploading\");\n const canRetry = computed(() => manager?.canRetry() ?? false);\n\n // Create metrics object that delegates to the upload client\n const metrics: UploadMetrics = {\n getInsights: () => uploadistaClient.client.getChunkingInsights(),\n exportMetrics: () => uploadistaClient.client.exportMetrics(),\n getNetworkMetrics: () => uploadistaClient.client.getNetworkMetrics(),\n getNetworkCondition: () => uploadistaClient.client.getNetworkCondition(),\n resetMetrics: () => uploadistaClient.client.resetMetrics(),\n };\n\n return {\n state,\n upload,\n abort,\n reset,\n retry,\n isUploading,\n canRetry,\n metrics,\n };\n}\n","import type {\n ChunkMetrics,\n PerformanceInsights,\n UploadSessionMetrics,\n} from \"@uploadista/client-browser\";\nimport { onUnmounted, readonly, ref } from \"vue\";\nimport { useUploadistaClient } from \"./useUploadistaClient\";\n\nexport interface UploadMetrics {\n /**\n * Total bytes uploaded across all files\n */\n totalBytesUploaded: number;\n\n /**\n * Total bytes to upload across all files\n */\n totalBytes: number;\n\n /**\n * Overall upload speed in bytes per second\n */\n averageSpeed: number;\n\n /**\n * Current upload speed in bytes per second\n */\n currentSpeed: number;\n\n /**\n * Estimated time remaining in milliseconds\n */\n estimatedTimeRemaining: number | null;\n\n /**\n * Total number of files being tracked\n */\n totalFiles: number;\n\n /**\n * Number of files completed\n */\n completedFiles: number;\n\n /**\n * Number of files currently uploading\n */\n activeUploads: number;\n\n /**\n * Overall progress as percentage (0-100)\n */\n progress: number;\n\n /**\n * Peak upload speed achieved\n */\n peakSpeed: number;\n\n /**\n * Start time of the first upload\n */\n startTime: number | null;\n\n /**\n * End time of the last completed upload\n */\n endTime: number | null;\n\n /**\n * Total duration of all uploads\n */\n totalDuration: number | null;\n\n /**\n * Detailed performance insights from the upload client\n */\n insights: PerformanceInsights;\n\n /**\n * Session metrics for completed uploads\n */\n sessionMetrics: Partial<UploadSessionMetrics>[];\n\n /**\n * Detailed chunk metrics from recent uploads\n */\n chunkMetrics: ChunkMetrics[];\n}\n\nexport interface FileUploadMetrics {\n id: string;\n filename: string;\n size: number;\n bytesUploaded: number;\n progress: number;\n speed: number;\n startTime: number;\n endTime: number | null;\n duration: number | null;\n isComplete: boolean;\n}\n\nexport interface UseUploadMetricsOptions {\n /**\n * Interval for calculating current speed (in milliseconds)\n */\n speedCalculationInterval?: number;\n\n /**\n * Number of speed samples to keep for average calculation\n */\n speedSampleSize?: number;\n\n /**\n * Called when metrics are updated\n */\n onMetricsUpdate?: (metrics: UploadMetrics) => void;\n\n /**\n * Called when a file upload starts\n */\n onFileStart?: (fileMetrics: FileUploadMetrics) => void;\n\n /**\n * Called when a file upload progresses\n */\n onFileProgress?: (fileMetrics: FileUploadMetrics) => void;\n\n /**\n * Called when a file upload completes\n */\n onFileComplete?: (fileMetrics: FileUploadMetrics) => void;\n}\n\nconst initialMetrics: UploadMetrics = {\n totalBytesUploaded: 0,\n totalBytes: 0,\n averageSpeed: 0,\n currentSpeed: 0,\n estimatedTimeRemaining: null,\n totalFiles: 0,\n completedFiles: 0,\n activeUploads: 0,\n progress: 0,\n peakSpeed: 0,\n startTime: null,\n endTime: null,\n totalDuration: null,\n insights: {\n overallEfficiency: 0,\n chunkingEffectiveness: 0,\n networkStability: 0,\n recommendations: [],\n optimalChunkSizeRange: { min: 256 * 1024, max: 2 * 1024 * 1024 },\n },\n sessionMetrics: [],\n chunkMetrics: [],\n};\n\n/**\n * Vue composable for tracking detailed upload metrics and performance statistics.\n * Provides comprehensive monitoring of upload progress, speed, and timing data.\n *\n * @param options - Configuration and event handlers\n * @returns Upload metrics state and control methods\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useUploadMetrics } from '@uploadista/vue';\n *\n * const uploadMetrics = useUploadMetrics({\n * speedCalculationInterval: 1000, // Update speed every second\n * speedSampleSize: 10, // Keep last 10 speed samples for average\n * onMetricsUpdate: (metrics) => {\n * console.log(`Overall progress: ${metrics.progress}%`);\n * console.log(`Speed: ${(metrics.currentSpeed / 1024).toFixed(1)} KB/s`);\n * console.log(`ETA: ${metrics.estimatedTimeRemaining}ms`);\n * },\n * onFileComplete: (fileMetrics) => {\n * console.log(`${fileMetrics.filename} completed in ${fileMetrics.duration}ms`);\n * },\n * });\n *\n * // Start tracking a file\n * const handleFileStart = (file: File) => {\n * uploadMetrics.startFileUpload(file.name, file.name, file.size);\n * };\n *\n * // Update progress during upload\n * const handleProgress = (fileId: string, bytesUploaded: number) => {\n * uploadMetrics.updateFileProgress(fileId, bytesUploaded);\n * };\n * </script>\n *\n * <template>\n * <div>\n * <div>Overall Progress: {{ uploadMetrics.metrics.progress }}%</div>\n * <div>Speed: {{ (uploadMetrics.metrics.currentSpeed / 1024).toFixed(1) }} KB/s</div>\n * <div>Files: {{ uploadMetrics.metrics.completedFiles }}/{{ uploadMetrics.metrics.totalFiles }}</div>\n *\n * <div v-if=\"uploadMetrics.metrics.estimatedTimeRemaining\">\n * ETA: {{ Math.round(uploadMetrics.metrics.estimatedTimeRemaining / 1000) }}s\n * </div>\n *\n * <div v-for=\"file in uploadMetrics.fileMetrics\" :key=\"file.id\">\n * {{ file.filename }}: {{ file.progress }}% ({{ (file.speed / 1024).toFixed(1) }} KB/s)\n * </div>\n * </div>\n * </template>\n * ```\n */\nexport function useUploadMetrics(options: UseUploadMetricsOptions = {}) {\n const {\n speedCalculationInterval = 1000,\n speedSampleSize = 10,\n onMetricsUpdate,\n onFileStart,\n onFileProgress,\n onFileComplete,\n } = options;\n\n const uploadClient = useUploadistaClient();\n\n const metrics = ref<UploadMetrics>({ ...initialMetrics });\n const fileMetrics = ref<FileUploadMetrics[]>([]);\n\n const speedSamples = ref<Array<{ time: number; bytes: number }>>([]);\n const lastUpdate = ref<number>(0);\n const interval = ref<ReturnType<typeof setInterval> | null>(null);\n\n const calculateSpeed = (currentTime: number, totalBytesUploaded: number) => {\n const sample = { time: currentTime, bytes: totalBytesUploaded };\n speedSamples.value.push(sample);\n\n // Keep only recent samples\n if (speedSamples.value.length > speedSampleSize) {\n speedSamples.value = speedSamples.value.slice(-speedSampleSize);\n }\n\n // Calculate current speed (bytes per second)\n let currentSpeed = 0;\n if (speedSamples.value.length >= 2) {\n const recent = speedSamples.value[speedSamples.value.length - 1];\n const previous = speedSamples.value[speedSamples.value.length - 2];\n if (recent && previous) {\n const timeDiff = (recent.time - previous.time) / 1000; // Convert to seconds\n const bytesDiff = recent.bytes - previous.bytes;\n currentSpeed = timeDiff > 0 ? bytesDiff / timeDiff : 0;\n }\n }\n\n // Calculate average speed\n let averageSpeed = 0;\n if (speedSamples.value.length >= 2) {\n const first = speedSamples.value[0];\n const last = speedSamples.value[speedSamples.value.length - 1];\n if (first && last) {\n const totalTime = (last.time - first.time) / 1000; // Convert to seconds\n const totalBytes = last.bytes - first.bytes;\n averageSpeed = totalTime > 0 ? totalBytes / totalTime : 0;\n }\n }\n\n return { currentSpeed, averageSpeed };\n };\n\n const updateMetrics = () => {\n const now = Date.now();\n\n // Calculate totals from file metrics\n const totalBytes = fileMetrics.value.reduce(\n (sum, file) => sum + file.size,\n 0,\n );\n const totalBytesUploaded = fileMetrics.value.reduce(\n (sum, file) => sum + file.bytesUploaded,\n 0,\n );\n const completedFiles = fileMetrics.value.filter(\n (file) => file.isComplete,\n ).length;\n const activeUploads = fileMetrics.value.filter(\n (file) => !file.isComplete && file.bytesUploaded > 0,\n ).length;\n\n // Calculate speeds\n const { currentSpeed, averageSpeed } = calculateSpeed(\n now,\n totalBytesUploaded,\n );\n\n // Calculate progress\n const progress =\n totalBytes > 0 ? Math.round((totalBytesUploaded / totalBytes) * 100) : 0;\n\n // Calculate estimated time remaining\n let estimatedTimeRemaining: number | null = null;\n if (currentSpeed > 0) {\n const remainingBytes = totalBytes - totalBytesUploaded;\n estimatedTimeRemaining = (remainingBytes / currentSpeed) * 1000; // Convert to milliseconds\n }\n\n // Find start and end times\n const activeTimes = fileMetrics.value.filter((file) => file.startTime > 0);\n const startTime =\n activeTimes.length > 0\n ? Math.min(...activeTimes.map((file) => file.startTime))\n : null;\n\n const completedTimes = fileMetrics.value.filter(\n (file) => file.endTime !== null,\n );\n const endTime =\n completedTimes.length > 0 && completedFiles === fileMetrics.value.length\n ? Math.max(\n ...(completedTimes\n .map((file) => file.endTime)\n .filter((time) => time !== null) as number[]),\n )\n : null;\n\n const totalDuration = startTime && endTime ? endTime - startTime : null;\n\n const newMetrics: UploadMetrics = {\n totalBytesUploaded,\n totalBytes,\n averageSpeed,\n currentSpeed,\n estimatedTimeRemaining,\n totalFiles: fileMetrics.value.length,\n completedFiles,\n activeUploads,\n progress,\n peakSpeed: Math.max(metrics.value.peakSpeed, currentSpeed),\n startTime,\n endTime,\n totalDuration,\n insights: uploadClient.client.getChunkingInsights(),\n sessionMetrics: [uploadClient.client.exportMetrics().session],\n chunkMetrics: uploadClient.client.exportMetrics().chunks,\n };\n\n metrics.value = newMetrics;\n onMetricsUpdate?.(newMetrics);\n };\n\n // Set up periodic speed calculations\n const setupSpeedCalculation = () => {\n if (interval.value) {\n clearInterval(interval.value);\n }\n\n interval.value = setInterval(() => {\n if (\n fileMetrics.value.some(\n (file) => !file.isComplete && file.bytesUploaded > 0,\n )\n ) {\n updateMetrics();\n }\n }, speedCalculationInterval);\n };\n\n const startFileUpload = (id: string, filename: string, size: number) => {\n const now = Date.now();\n\n const fileMetric: FileUploadMetrics = {\n id,\n filename,\n size,\n bytesUploaded: 0,\n progress: 0,\n speed: 0,\n startTime: now,\n endTime: null,\n duration: null,\n isComplete: false,\n };\n\n const existing = fileMetrics.value.find((file) => file.id === id);\n if (existing) {\n fileMetrics.value = fileMetrics.value.map((file) =>\n file.id === id ? fileMetric : file,\n );\n } else {\n fileMetrics.value = [...fileMetrics.value, fileMetric];\n }\n\n onFileStart?.(fileMetric);\n\n // Start speed calculation if this is the first active upload\n if (fileMetrics.value.filter((file) => !file.isComplete).length === 1) {\n setupSpeedCalculation();\n }\n };\n\n const updateFileProgress = (id: string, bytesUploaded: number) => {\n const now = Date.now();\n\n fileMetrics.value = fileMetrics.value.map((file) => {\n if (file.id !== id) return file;\n\n const timeDiff = (now - file.startTime) / 1000; // seconds\n const speed = timeDiff > 0 ? bytesUploaded / timeDiff : 0;\n const progress =\n file.size > 0 ? Math.round((bytesUploaded / file.size) * 100) : 0;\n\n const updatedFile = {\n ...file,\n bytesUploaded,\n progress,\n speed,\n };\n\n onFileProgress?.(updatedFile);\n return updatedFile;\n });\n\n // Trigger metrics update\n setTimeout(updateMetrics, 0);\n };\n\n const completeFileUpload = (id: string) => {\n const now = Date.now();\n\n fileMetrics.value = fileMetrics.value.map((file) => {\n if (file.id !== id) return file;\n\n const duration = now - file.startTime;\n const speed = duration > 0 ? (file.size / duration) * 1000 : 0; // bytes per second\n\n const completedFile = {\n ...file,\n bytesUploaded: file.size,\n progress: 100,\n speed,\n endTime: now,\n duration,\n isComplete: true,\n };\n\n onFileComplete?.(completedFile);\n return completedFile;\n });\n\n // Trigger metrics update\n setTimeout(updateMetrics, 0);\n };\n\n const removeFile = (id: string) => {\n fileMetrics.value = fileMetrics.value.filter((file) => file.id !== id);\n setTimeout(updateMetrics, 0);\n };\n\n const reset = () => {\n if (interval.value) {\n clearInterval(interval.value);\n interval.value = null;\n }\n\n metrics.value = { ...initialMetrics };\n fileMetrics.value = [];\n speedSamples.value = [];\n lastUpdate.value = 0;\n };\n\n const getFileMetrics = (id: string) => {\n return fileMetrics.value.find((file) => file.id === id);\n };\n\n const exportMetrics = () => {\n return {\n overall: metrics.value,\n files: fileMetrics.value,\n exportTime: Date.now(),\n };\n };\n\n // Cleanup on unmount\n onUnmounted(() => {\n if (interval.value) {\n clearInterval(interval.value);\n }\n });\n\n return {\n metrics: readonly(metrics),\n fileMetrics: readonly(fileMetrics),\n startFileUpload,\n updateFileProgress,\n completeFileUpload,\n removeFile,\n reset,\n getFileMetrics,\n exportMetrics,\n };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-CV58aghD.d.mts","names":[],"sources":["../src/providers/FlowManagerProvider.vue","../src/providers/UploadistaProvider.vue"],"mappings":";;;YAuTI,OAAA;AAAA,KACC,aAAA;EACD,OAAA,IAAW,KAAA,SAAc,OAAA;AAAA;AAAA,cACvB,YAAA,EAAU,KAAA,CAAA,eAAA,qBACd,KAAA,CADc,qBAAA,EAAA,KAAA,CAAA,qBAAA,cAAA,KAAA,CAAA,WAAA,EAAA,QAAA,OAAA,QAAA,8BAAA,KAAA,CAAA,uBAAA;AAAA,cAEV,cAAA,EAAqB,iBAAA,QAAuB,YAAA,EAAY,aAAA;AAAA,cAAW,UAAA,SAC7C,cAAA;AAAA,KACvB,iBAAA,SAAwB,CAAA;EAAA;IAE3B,MAAA,EAAQ,CAAA;EAAA;AAAA;;;KCjPL,WAAA;EACD,SAAA;EACA,SAAA;EACA,kBAAA;EACA,SAAA;EACA,eAAA;EACA,2BAAA;AAAA;AAAA,YA0EA,OAAA;AAAA,KACC,WAAA;EACD,OAAA,IAAW,KAAA,SAAc,OAAA;AAAA;AAAA,cAGvB,UAAA,QAAU,eAAA,CAAA,WAAA,kBAAA,KAAA,CAAA,qBAAA,EAAA,KAAA,CAAA,qBAAA,cAAA,KAAA,CAAA,WAAA,EAAA,QAAA,CAAA,WAAA,IAAA,QAAA;;;;;;uBAAA,KAAA,CAAA,uBAAA;AAAA,cAIV,YAAA,EAAqB,eAAA,QAAuB,UAAA,EAAY,WAAA;AAAA,cAAW,QAAA,SAC7C,YAAA;AAAA,KAWvB,eAAA,SAAwB,CAAA;EAAA;IAE3B,MAAA,EAAQ,CAAA;EAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-ZaF-lpSA.d.mts","names":[],"sources":["../src/composables/useFlow.ts","../src/composables/useDragDrop.ts","../src/composables/eventUtils.ts","../src/composables/useFlowEvents.ts","../src/composables/useUploadEvents.ts","../src/composables/useUploadistaEvents.ts","../src/composables/plugin.ts","../src/composables/useMultiFlowUpload.ts","../src/composables/useUpload.ts","../src/composables/useMultiUpload.ts","../src/composables/useUploadistaClient.ts","../src/composables/useUploadMetrics.ts"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{n as e,r as t,t as n}from"./useUploadistaClient-C3qbTB18.mjs";import{createBlock as r,defineComponent as i,onBeforeUnmount as a,onMounted as o,openBlock as s,provide as c,ref as l,renderSlot as u,withCtx as d}from"vue";import{createUploadistaClient as f}from"@uploadista/client-browser";import{EventType as p}from"@uploadista/core/flow";import{UploadEventType as m}from"@uploadista/core/types";import{FlowManager as h}from"@uploadista/client-core";var g=i({__name:`FlowManagerProvider`,setup(e){function t(e){let t=e;return t.eventType===p.FlowStart||t.eventType===p.FlowEnd||t.eventType===p.FlowError||t.eventType===p.NodeStart||t.eventType===p.NodeEnd||t.eventType===p.NodePause||t.eventType===p.NodeResume||t.eventType===p.NodeError}let{client:r,subscribeToEvents:i}=n(),s=new Map,l=null;return o(()=>{l=i(e=>{if(t(e)){for(let t of s.values())t.manager.handleFlowEvent(e);return}if(`type`in e&&e.type===m.UPLOAD_PROGRESS&&`data`in e){let t=e;for(let e of s.values())e.manager.handleUploadProgress(t.data.id,t.data.progress,t.data.total)}})}),a(()=>{l?.();for(let e of s.values())e.manager.cleanup();s.clear()}),c(`flowManagerContext`,{getManager:(e,t,n)=>{let i=s.get(e);if(i)return i.refCount++,i.manager;let a=new h(r.uploadWithFlow,t,n,r.multiInputFlowUpload);return s.set(e,{manager:a,refCount:1,flowId:e}),a},releaseManager:e=>{let t=s.get(e);t&&(t.refCount--,t.refCount<=0&&(t.manager.cleanup(),s.delete(e)))}}),(e,t)=>u(e.$slots,`default`)}}),_=i({__name:`UploadistaProvider`,props:{serverUrl:{},storageId:{default:`local`},uploadistaBasePath:{default:`uploadista`},chunkSize:{default:1024*1024},parallelUploads:{default:1},storeFingerprintForResuming:{type:Boolean,default:!0}},setup(n){let i=n,o=l(new Set),p=f({baseUrl:i.serverUrl,storageId:i.storageId,uploadistaBasePath:i.uploadistaBasePath,chunkSize:i.chunkSize,parallelUploads:i.parallelUploads,storeFingerprintForResuming:i.storeFingerprintForResuming,onEvent:e=>{o.value.forEach(t=>{try{t(e)}catch(e){console.error(`Error in event subscriber:`,e)}})}});return c(e,p),c(t,o),a(()=>{p.closeAllWebSockets()}),(e,t)=>(s(),r(g,null,{default:d(()=>[u(e.$slots,`default`)]),_:3}))}});export{g as n,_ as t};
2
- //# sourceMappingURL=providers-BZSbSDJz.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"providers-BZSbSDJz.mjs","names":[],"sources":["../src/providers/FlowManagerProvider.vue","../src/providers/UploadistaProvider.vue"],"sourcesContent":["<template>\n <slot />\n</template>\n\n<script setup lang=\"ts\">\nimport type {\n BrowserUploadInput,\n UploadistaEvent,\n} from \"@uploadista/client-browser\";\nimport {\n FlowManager,\n type FlowManagerCallbacks,\n type FlowUploadOptions,\n} from \"@uploadista/client-core\";\nimport { EventType, type FlowEvent } from \"@uploadista/core/flow\";\nimport { UploadEventType } from \"@uploadista/core/types\";\nimport { onBeforeUnmount, onMounted, provide } from \"vue\";\nimport { useUploadistaClient } from \"../composables/useUploadistaClient\";\n\n/**\n * Type guard to check if an event is a flow event\n */\nfunction isFlowEvent(event: UploadistaEvent): event is FlowEvent {\n const flowEvent = event as FlowEvent;\n return (\n flowEvent.eventType === EventType.FlowStart ||\n flowEvent.eventType === EventType.FlowEnd ||\n flowEvent.eventType === EventType.FlowError ||\n flowEvent.eventType === EventType.NodeStart ||\n flowEvent.eventType === EventType.NodeEnd ||\n flowEvent.eventType === EventType.NodePause ||\n flowEvent.eventType === EventType.NodeResume ||\n flowEvent.eventType === EventType.NodeError\n );\n}\n\n/**\n * Internal manager registry entry with ref counting\n */\ninterface ManagerEntry {\n manager: FlowManager<unknown>;\n refCount: number;\n flowId: string;\n}\n\n/**\n * Context value providing access to flow managers\n */\ninterface FlowManagerContextValue {\n getManager: (\n flowId: string,\n callbacks: FlowManagerCallbacks,\n options: FlowUploadOptions,\n ) => FlowManager<unknown>;\n releaseManager: (flowId: string) => void;\n}\n\nconst { client, subscribeToEvents } = useUploadistaClient();\nconst managers = new Map<string, ManagerEntry>();\nlet unsubscribe: (() => void) | null = null;\n\n// Subscribe to events and route to managers\nonMounted(() => {\n unsubscribe = subscribeToEvents((event: UploadistaEvent) => {\n // Route flow events to all managers (they filter by jobId internally)\n if (isFlowEvent(event)) {\n for (const entry of managers.values()) {\n entry.manager.handleFlowEvent(event);\n }\n return;\n }\n\n // Route upload progress events to all managers\n if (\n \"type\" in event &&\n event.type === UploadEventType.UPLOAD_PROGRESS &&\n \"data\" in event\n ) {\n const uploadEvent = event;\n\n for (const entry of managers.values()) {\n entry.manager.handleUploadProgress(\n uploadEvent.data.id,\n uploadEvent.data.progress,\n uploadEvent.data.total,\n );\n }\n }\n });\n});\n\n// Cleanup on unmount\nonBeforeUnmount(() => {\n unsubscribe?.();\n for (const entry of managers.values()) {\n entry.manager.cleanup();\n }\n managers.clear();\n});\n\nconst getManager = (\n flowId: string,\n callbacks: FlowManagerCallbacks,\n options: FlowUploadOptions,\n): FlowManager<unknown> => {\n const existing = managers.get(flowId);\n\n if (existing) {\n // Increment ref count for existing manager\n existing.refCount++;\n return existing.manager;\n }\n\n // Create new manager\n const manager = new FlowManager<BrowserUploadInput>(\n client.uploadWithFlow,\n callbacks,\n options,\n client.multiInputFlowUpload,\n );\n\n managers.set(flowId, {\n manager,\n refCount: 1,\n flowId,\n });\n\n return manager;\n};\n\nconst releaseManager = (flowId: string) => {\n const existing = managers.get(flowId);\n if (!existing) return;\n\n existing.refCount--;\n\n // Clean up when no more refs\n if (existing.refCount <= 0) {\n existing.manager.cleanup();\n managers.delete(flowId);\n }\n};\n\n// Provide the context\nconst flowManagerContext: FlowManagerContextValue = {\n getManager,\n releaseManager,\n};\n\nprovide(\"flowManagerContext\", flowManagerContext);\n</script>\n","<script setup lang=\"ts\">\nimport {\n createUploadistaClient,\n type UploadistaEvent,\n} from \"@uploadista/client-browser\";\nimport { onBeforeUnmount, provide, ref } from \"vue\";\nimport {\n UPLOADISTA_CLIENT_KEY,\n UPLOADISTA_EVENT_SUBSCRIBERS_KEY,\n} from \"../composables/plugin\";\nimport FlowManagerProvider from \"./FlowManagerProvider.vue\";\n\nconst props = withDefaults(\n defineProps<{\n serverUrl: string;\n storageId?: string;\n uploadistaBasePath?: string;\n chunkSize?: number;\n parallelUploads?: number;\n storeFingerprintForResuming?: boolean;\n }>(),\n {\n storageId: \"local\",\n uploadistaBasePath: \"uploadista\",\n chunkSize: 1024 * 1024,\n parallelUploads: 1,\n storeFingerprintForResuming: true,\n },\n);\n\n// Create a shared set of event subscribers\nconst eventSubscribers = ref(new Set<(event: UploadistaEvent) => void>());\n\nconst client = createUploadistaClient({\n baseUrl: props.serverUrl,\n storageId: props.storageId,\n uploadistaBasePath: props.uploadistaBasePath,\n chunkSize: props.chunkSize,\n parallelUploads: props.parallelUploads,\n storeFingerprintForResuming: props.storeFingerprintForResuming,\n onEvent: (event) => {\n // Dispatch to all subscribers registered via subscribeToEvents\n eventSubscribers.value.forEach((subscriber) => {\n try {\n subscriber(event);\n } catch (err) {\n console.error(\"Error in event subscriber:\", err);\n }\n });\n },\n});\n\nprovide(UPLOADISTA_CLIENT_KEY, client);\nprovide(UPLOADISTA_EVENT_SUBSCRIBERS_KEY, eventSubscribers);\n\nonBeforeUnmount(() => {\n client.closeAllWebSockets();\n});\n</script>\n\n<template>\n <FlowManagerProvider>\n <slot />\n </FlowManagerProvider>\n</template>\n"],"mappings":"sfAsBA,SAAS,EAAY,EAA4C,CAC/D,IAAM,EAAY,EAClB,OACE,EAAU,YAAc,EAAU,WAClC,EAAU,YAAc,EAAU,SAClC,EAAU,YAAc,EAAU,WAClC,EAAU,YAAc,EAAU,WAClC,EAAU,YAAc,EAAU,SAClC,EAAU,YAAc,EAAU,WAClC,EAAU,YAAc,EAAU,YAClC,EAAU,YAAc,EAAU,UAyBtC,GAAM,CAAE,SAAQ,qBAAsB,GAAqB,CACrD,EAAW,IAAI,IACjB,EAAmC,YAGvC,MAAgB,CACd,EAAc,EAAmB,GAA2B,CAE1D,GAAI,EAAY,EAAM,CAAE,CACtB,IAAK,IAAM,KAAS,EAAS,QAAQ,CACnC,EAAM,QAAQ,gBAAgB,EAAM,CAEtC,OAIF,GACE,SAAU,GACV,EAAM,OAAS,EAAgB,iBAC/B,SAAU,EACV,CACA,IAAM,EAAc,EAEpB,IAAK,IAAM,KAAS,EAAS,QAAQ,CACnC,EAAM,QAAQ,qBACZ,EAAY,KAAK,GACjB,EAAY,KAAK,SACjB,EAAY,KAAK,MAClB,GAGL,EACF,CAGF,MAAsB,CACpB,KAAe,CACf,IAAK,IAAM,KAAS,EAAS,QAAQ,CACnC,EAAM,QAAQ,SAAS,CAEzB,EAAS,OAAO,EAChB,CAmDF,EAAQ,qBAL4C,CAClD,YA5CA,EACA,EACA,IACyB,CACzB,IAAM,EAAW,EAAS,IAAI,EAAO,CAErC,GAAI,EAGF,MADA,GAAS,WACF,EAAS,QAIlB,IAAM,EAAU,IAAI,EAClB,EAAO,eACP,EACA,EACA,EAAO,qBACR,CAQD,OANA,EAAS,IAAI,EAAQ,CACnB,UACA,SAAU,EACV,SACD,CAAC,CAEK,GAmBP,eAhBsB,GAAmB,CACzC,IAAM,EAAW,EAAS,IAAI,EAAO,CAChC,IAEL,EAAS,WAGL,EAAS,UAAY,IACvB,EAAS,QAAQ,SAAS,CAC1B,EAAS,OAAO,EAAO,IAQ1B,CAEgD,QApJ/C,EAAQ,EAAA,OAAA,UAAA,yPCWV,IAAM,EAAQ,EAmBR,EAAmB,EAAI,IAAI,IAAwC,CAEnE,EAAS,EAAuB,CACpC,QAAS,EAAM,UACf,UAAW,EAAM,UACjB,mBAAoB,EAAM,mBAC1B,UAAW,EAAM,UACjB,gBAAiB,EAAM,gBACvB,4BAA6B,EAAM,4BACnC,QAAU,GAAU,CAElB,EAAiB,MAAM,QAAS,GAAe,CAC7C,GAAI,CACF,EAAW,EAAM,OACV,EAAK,CACZ,QAAQ,MAAM,6BAA8B,EAAI,GAElD,EAEL,CAAC,QAEF,EAAQ,EAAuB,EAAO,CACtC,EAAQ,EAAkC,EAAiB,CAE3D,MAAsB,CACpB,EAAO,oBAAoB,EAC3B,aAIA,EAEsB,EAAA,KAAA,eADZ,CAAR,EAAQ,EAAA,OAAA,UAAA,CAAA,CAAA"}
@@ -1,2 +0,0 @@
1
- const e=e=>typeof File<`u`&&e instanceof File;function t(e){if(e===0)return`0 Bytes`;let t=1024,n=[`Bytes`,`KB`,`MB`,`GB`,`TB`],r=Math.floor(Math.log(e)/Math.log(t));return`${Number.parseFloat((e/t**r).toFixed(2))} ${n[r]}`}function n(e){if(e===0)return`0 B/s`;let t=1024,n=[`B/s`,`KB/s`,`MB/s`,`GB/s`],r=Math.floor(Math.log(e)/Math.log(t));return`${Number.parseFloat((e/t**r).toFixed(1))} ${n[r]}`}function r(e){if(e<1e3)return`${Math.round(e)}ms`;if(e<6e4)return`${Math.round(e/1e3)}s`;if(e<36e5){let t=Math.floor(e/6e4),n=Math.round(e%6e4/1e3);return n>0?`${t}m ${n}s`:`${t}m`}let t=Math.floor(e/36e5),n=Math.round(e%36e5/6e4);return n>0?`${t}h ${n}m`:`${t}h`}function i(e,t){return!t||t.length===0?!0:t.some(t=>{if(t.startsWith(`.`))return e.name.toLowerCase().endsWith(t.toLowerCase());if(t.endsWith(`/*`)){let n=t.slice(0,-2);return e.type.startsWith(n)}return e.type===t})}function a(){return`upload-${Date.now()}-${Math.random().toString(36).substr(2,9)}`}function o(e){let t=e.lastIndexOf(`.`);return t===-1?``:e.substring(t+1).toLowerCase()}function s(e){return e.type.startsWith(`image/`)}function c(e){return e.type.startsWith(`video/`)}function l(e){return e.type.startsWith(`audio/`)}function u(e){return[`application/pdf`,`application/msword`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`,`application/vnd.ms-excel`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,`application/vnd.ms-powerpoint`,`application/vnd.openxmlformats-officedocument.presentationml.presentation`,`text/plain`,`text/csv`,`application/rtf`].includes(e.type)}function d(e){return s(e)||c(e)||l(e)?URL.createObjectURL(e):null}function f(e){URL.revokeObjectURL(e)}function p(e,t){return t===0?0:Math.min(100,Math.max(0,Math.round(e/t*100)))}export{n as a,l as c,c as d,f,t as i,u as l,e as m,d as n,a as o,i as p,r,o as s,p as t,s as u};
2
- //# sourceMappingURL=utils-kqjsMS_C.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils-kqjsMS_C.mjs","names":[],"sources":["../src/utils/is-browser-file.ts","../src/utils/index.ts"],"sourcesContent":["export const isBrowserFile = (value: unknown): value is File =>\n typeof File !== \"undefined\" && value instanceof File;\n","/**\n * Utility functions for the Vue upload client\n */\n\n/**\n * Format file size in human-readable format\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 Bytes\";\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;\n}\n\n/**\n * Format upload speed in human-readable format\n */\nexport function formatSpeed(bytesPerSecond: number): string {\n if (bytesPerSecond === 0) return \"0 B/s\";\n const k = 1024;\n const sizes = [\"B/s\", \"KB/s\", \"MB/s\", \"GB/s\"];\n const i = Math.floor(Math.log(bytesPerSecond) / Math.log(k));\n return `${Number.parseFloat((bytesPerSecond / k ** i).toFixed(1))} ${sizes[i]}`;\n}\n\n/**\n * Format duration in human-readable format\n */\nexport function formatDuration(milliseconds: number): string {\n if (milliseconds < 1000) {\n return `${Math.round(milliseconds)}ms`;\n }\n\n if (milliseconds < 60000) {\n return `${Math.round(milliseconds / 1000)}s`;\n }\n\n if (milliseconds < 3600000) {\n const minutes = Math.floor(milliseconds / 60000);\n const seconds = Math.round((milliseconds % 60000) / 1000);\n return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n }\n\n const hours = Math.floor(milliseconds / 3600000);\n const minutes = Math.round((milliseconds % 3600000) / 60000);\n return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;\n}\n\n/**\n * Validate file type against accepted types\n */\nexport function validateFileType(file: File, accept: string[]): boolean {\n if (!accept || accept.length === 0) return true;\n\n return accept.some((acceptType) => {\n if (acceptType.startsWith(\".\")) {\n // File extension check\n return file.name.toLowerCase().endsWith(acceptType.toLowerCase());\n }\n\n // MIME type check (supports wildcards like image/*)\n if (acceptType.endsWith(\"/*\")) {\n const baseType = acceptType.slice(0, -2);\n return file.type.startsWith(baseType);\n }\n\n return file.type === acceptType;\n });\n}\n\n/**\n * Generate a unique ID for upload items\n */\nexport function generateUploadId(): string {\n return `upload-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n}\n\n/**\n * Get file extension from filename\n */\nexport function getFileExtension(filename: string): string {\n const lastDot = filename.lastIndexOf(\".\");\n return lastDot !== -1 ? filename.substring(lastDot + 1).toLowerCase() : \"\";\n}\n\n/**\n * Check if a file is an image\n */\nexport function isImageFile(file: File): boolean {\n return file.type.startsWith(\"image/\");\n}\n\n/**\n * Check if a file is a video\n */\nexport function isVideoFile(file: File): boolean {\n return file.type.startsWith(\"video/\");\n}\n\n/**\n * Check if a file is an audio file\n */\nexport function isAudioFile(file: File): boolean {\n return file.type.startsWith(\"audio/\");\n}\n\n/**\n * Check if a file is a document\n */\nexport function isDocumentFile(file: File): boolean {\n const documentTypes = [\n \"application/pdf\",\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"text/plain\",\n \"text/csv\",\n \"application/rtf\",\n ];\n\n return documentTypes.includes(file.type);\n}\n\n/**\n * Create a preview URL for a file (if supported)\n */\nexport function createFilePreview(file: File): string | null {\n if (isImageFile(file) || isVideoFile(file) || isAudioFile(file)) {\n return URL.createObjectURL(file);\n }\n return null;\n}\n\n/**\n * Clean up a preview URL created with createFilePreview\n */\nexport function revokeFilePreview(previewUrl: string): void {\n URL.revokeObjectURL(previewUrl);\n}\n\n/**\n * Calculate progress percentage\n */\nexport function calculateProgress(current: number, total: number): number {\n if (total === 0) return 0;\n return Math.min(100, Math.max(0, Math.round((current / total) * 100)));\n}\n\n/**\n * Check if a value is a browser file\n */\nexport * from \"./is-browser-file\";\n"],"mappings":"AAAA,MAAa,EAAiB,GAC5B,OAAO,KAAS,KAAe,aAAiB,KCMlD,SAAgB,EAAe,EAAuB,CACpD,GAAI,IAAU,EAAG,MAAO,UACxB,IAAM,EAAI,KACJ,EAAQ,CAAC,QAAS,KAAM,KAAM,KAAM,KAAK,CACzC,EAAI,KAAK,MAAM,KAAK,IAAI,EAAM,CAAG,KAAK,IAAI,EAAE,CAAC,CACnD,MAAO,GAAG,OAAO,YAAY,EAAQ,GAAK,GAAG,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAM,KAMpE,SAAgB,EAAY,EAAgC,CAC1D,GAAI,IAAmB,EAAG,MAAO,QACjC,IAAM,EAAI,KACJ,EAAQ,CAAC,MAAO,OAAQ,OAAQ,OAAO,CACvC,EAAI,KAAK,MAAM,KAAK,IAAI,EAAe,CAAG,KAAK,IAAI,EAAE,CAAC,CAC5D,MAAO,GAAG,OAAO,YAAY,EAAiB,GAAK,GAAG,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAM,KAM7E,SAAgB,EAAe,EAA8B,CAC3D,GAAI,EAAe,IACjB,MAAO,GAAG,KAAK,MAAM,EAAa,CAAC,IAGrC,GAAI,EAAe,IACjB,MAAO,GAAG,KAAK,MAAM,EAAe,IAAK,CAAC,GAG5C,GAAI,EAAe,KAAS,CAC1B,IAAM,EAAU,KAAK,MAAM,EAAe,IAAM,CAC1C,EAAU,KAAK,MAAO,EAAe,IAAS,IAAK,CACzD,OAAO,EAAU,EAAI,GAAG,EAAQ,IAAI,EAAQ,GAAK,GAAG,EAAQ,GAG9D,IAAM,EAAQ,KAAK,MAAM,EAAe,KAAQ,CAC1C,EAAU,KAAK,MAAO,EAAe,KAAW,IAAM,CAC5D,OAAO,EAAU,EAAI,GAAG,EAAM,IAAI,EAAQ,GAAK,GAAG,EAAM,GAM1D,SAAgB,EAAiB,EAAY,EAA2B,CAGtE,MAFI,CAAC,GAAU,EAAO,SAAW,EAAU,GAEpC,EAAO,KAAM,GAAe,CACjC,GAAI,EAAW,WAAW,IAAI,CAE5B,OAAO,EAAK,KAAK,aAAa,CAAC,SAAS,EAAW,aAAa,CAAC,CAInE,GAAI,EAAW,SAAS,KAAK,CAAE,CAC7B,IAAM,EAAW,EAAW,MAAM,EAAG,GAAG,CACxC,OAAO,EAAK,KAAK,WAAW,EAAS,CAGvC,OAAO,EAAK,OAAS,GACrB,CAMJ,SAAgB,GAA2B,CACzC,MAAO,UAAU,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,OAAO,EAAG,EAAE,GAMxE,SAAgB,EAAiB,EAA0B,CACzD,IAAM,EAAU,EAAS,YAAY,IAAI,CACzC,OAAO,IAAY,GAAqD,GAAhD,EAAS,UAAU,EAAU,EAAE,CAAC,aAAa,CAMvE,SAAgB,EAAY,EAAqB,CAC/C,OAAO,EAAK,KAAK,WAAW,SAAS,CAMvC,SAAgB,EAAY,EAAqB,CAC/C,OAAO,EAAK,KAAK,WAAW,SAAS,CAMvC,SAAgB,EAAY,EAAqB,CAC/C,OAAO,EAAK,KAAK,WAAW,SAAS,CAMvC,SAAgB,EAAe,EAAqB,CAclD,MAbsB,CACpB,kBACA,qBACA,0EACA,2BACA,oEACA,gCACA,4EACA,aACA,WACA,kBACD,CAEoB,SAAS,EAAK,KAAK,CAM1C,SAAgB,EAAkB,EAA2B,CAI3D,OAHI,EAAY,EAAK,EAAI,EAAY,EAAK,EAAI,EAAY,EAAK,CACtD,IAAI,gBAAgB,EAAK,CAE3B,KAMT,SAAgB,EAAkB,EAA0B,CAC1D,IAAI,gBAAgB,EAAW,CAMjC,SAAgB,EAAkB,EAAiB,EAAuB,CAExE,OADI,IAAU,EAAU,EACjB,KAAK,IAAI,IAAK,KAAK,IAAI,EAAG,KAAK,MAAO,EAAU,EAAS,IAAI,CAAC,CAAC"}