@chunkflowjs/upload-client-vue 0.0.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +230 -0
- package/dist/index.d.mts +61 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +419 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# @chunkflowjs/upload-client-vue
|
|
2
|
+
|
|
3
|
+
Vue adapter for ChunkFlow Upload SDK with composables and plugin.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @chunkflowjs/upload-client-vue
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @chunkflowjs/upload-client-vue
|
|
11
|
+
# or
|
|
12
|
+
yarn add @chunkflowjs/upload-client-vue
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### 1. Install the Plugin
|
|
18
|
+
|
|
19
|
+
First, install the Upload Plugin in your Vue application:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// main.ts
|
|
23
|
+
import { createApp } from "vue";
|
|
24
|
+
import { createUploadPlugin } from "@chunkflowjs/upload-client-vue";
|
|
25
|
+
import App from "./App.vue";
|
|
26
|
+
|
|
27
|
+
// Create your request adapter
|
|
28
|
+
const requestAdapter = {
|
|
29
|
+
createFile: async (request) => {
|
|
30
|
+
// Implement your API call
|
|
31
|
+
},
|
|
32
|
+
verifyHash: async (request) => {
|
|
33
|
+
// Implement your API call
|
|
34
|
+
},
|
|
35
|
+
uploadChunk: async (request) => {
|
|
36
|
+
// Implement your API call
|
|
37
|
+
},
|
|
38
|
+
mergeFile: async (request) => {
|
|
39
|
+
// Implement your API call
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const app = createApp(App);
|
|
44
|
+
|
|
45
|
+
// Install the upload plugin
|
|
46
|
+
const uploadPlugin = createUploadPlugin({
|
|
47
|
+
requestAdapter,
|
|
48
|
+
managerOptions: {
|
|
49
|
+
maxConcurrentTasks: 5,
|
|
50
|
+
defaultChunkSize: 2 * 1024 * 1024, // 2MB
|
|
51
|
+
defaultConcurrency: 3,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
app.use(uploadPlugin);
|
|
56
|
+
app.mount("#app");
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 2. Use the Composables
|
|
60
|
+
|
|
61
|
+
#### Single File Upload with `useUpload`
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<script setup lang="ts">
|
|
65
|
+
import { useUpload } from "@chunkflowjs/upload-client-vue";
|
|
66
|
+
|
|
67
|
+
const { upload, pause, resume, cancel, status, progress, error } = useUpload({
|
|
68
|
+
onSuccess: (fileUrl) => {
|
|
69
|
+
console.log("Upload complete:", fileUrl);
|
|
70
|
+
},
|
|
71
|
+
onError: (error) => {
|
|
72
|
+
console.error("Upload failed:", error);
|
|
73
|
+
},
|
|
74
|
+
onProgress: (progress) => {
|
|
75
|
+
console.log(`Progress: ${progress.percentage}%`);
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const handleFileSelect = (event: Event) => {
|
|
80
|
+
const input = event.target as HTMLInputElement;
|
|
81
|
+
const file = input.files?.[0];
|
|
82
|
+
if (file) {
|
|
83
|
+
upload(file);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<div>
|
|
90
|
+
<input type="file" @change="handleFileSelect" />
|
|
91
|
+
|
|
92
|
+
<div v-if="status === 'uploading'">
|
|
93
|
+
<p>Progress: {{ progress.percentage.toFixed(1) }}%</p>
|
|
94
|
+
<p>Speed: {{ (progress.speed / 1024 / 1024).toFixed(2) }} MB/s</p>
|
|
95
|
+
<p>Remaining: {{ Math.ceil(progress.remainingTime) }}s</p>
|
|
96
|
+
<button @click="pause">Pause</button>
|
|
97
|
+
<button @click="cancel">Cancel</button>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<button v-if="status === 'paused'" @click="resume">Resume</button>
|
|
101
|
+
<p v-if="status === 'success'">Upload complete!</p>
|
|
102
|
+
<p v-if="status === 'error'">Error: {{ error?.message }}</p>
|
|
103
|
+
</div>
|
|
104
|
+
</template>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### Multiple File Upload with `useUploadList`
|
|
108
|
+
|
|
109
|
+
```vue
|
|
110
|
+
<script setup lang="ts">
|
|
111
|
+
import { useUploadList } from "@chunkflowjs/upload-client-vue";
|
|
112
|
+
|
|
113
|
+
const {
|
|
114
|
+
tasks,
|
|
115
|
+
uploadFiles,
|
|
116
|
+
pauseAll,
|
|
117
|
+
resumeAll,
|
|
118
|
+
cancelAll,
|
|
119
|
+
removeTask,
|
|
120
|
+
clearCompleted,
|
|
121
|
+
getStatistics,
|
|
122
|
+
} = useUploadList();
|
|
123
|
+
|
|
124
|
+
const handleFilesSelect = (event: Event) => {
|
|
125
|
+
const input = event.target as HTMLInputElement;
|
|
126
|
+
const files = Array.from(input.files || []);
|
|
127
|
+
uploadFiles(files);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const stats = getStatistics();
|
|
131
|
+
</script>
|
|
132
|
+
|
|
133
|
+
<template>
|
|
134
|
+
<div>
|
|
135
|
+
<input type="file" multiple @change="handleFilesSelect" />
|
|
136
|
+
|
|
137
|
+
<div>
|
|
138
|
+
<button @click="pauseAll">Pause All</button>
|
|
139
|
+
<button @click="resumeAll">Resume All</button>
|
|
140
|
+
<button @click="cancelAll">Cancel All</button>
|
|
141
|
+
<button @click="clearCompleted">Clear Completed</button>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<p>
|
|
145
|
+
Total: {{ stats.total }} | Uploading: {{ stats.uploading }} | Success: {{ stats.success }} |
|
|
146
|
+
Error: {{ stats.error }}
|
|
147
|
+
</p>
|
|
148
|
+
|
|
149
|
+
<ul>
|
|
150
|
+
<li v-for="task in tasks" :key="task.id">
|
|
151
|
+
<span>{{ task.file.name }}</span>
|
|
152
|
+
<span>{{ task.getStatus() }}</span>
|
|
153
|
+
<span>{{ task.getProgress().percentage.toFixed(1) }}%</span>
|
|
154
|
+
<button @click="removeTask(task.id)">Remove</button>
|
|
155
|
+
</li>
|
|
156
|
+
</ul>
|
|
157
|
+
</div>
|
|
158
|
+
</template>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## API
|
|
162
|
+
|
|
163
|
+
### `createUploadPlugin(options)`
|
|
164
|
+
|
|
165
|
+
Creates a Vue plugin for the Upload SDK.
|
|
166
|
+
|
|
167
|
+
**Options:**
|
|
168
|
+
|
|
169
|
+
- `requestAdapter` (required): Implementation of the RequestAdapter interface
|
|
170
|
+
- `managerOptions` (optional): Configuration for the UploadManager
|
|
171
|
+
- `maxConcurrentTasks`: Maximum number of concurrent upload tasks (default: 3)
|
|
172
|
+
- `defaultChunkSize`: Default chunk size in bytes (default: 1MB)
|
|
173
|
+
- `defaultConcurrency`: Default number of concurrent chunk uploads per task (default: 3)
|
|
174
|
+
- `autoResumeUnfinished`: Automatically resume unfinished uploads on init (default: true)
|
|
175
|
+
|
|
176
|
+
### `useUpload(options)`
|
|
177
|
+
|
|
178
|
+
Composable for uploading a single file.
|
|
179
|
+
|
|
180
|
+
**Options:**
|
|
181
|
+
|
|
182
|
+
- `chunkSize`: Chunk size in bytes
|
|
183
|
+
- `concurrency`: Number of concurrent chunk uploads
|
|
184
|
+
- `retryCount`: Number of retry attempts for failed chunks
|
|
185
|
+
- `retryDelay`: Delay between retries in milliseconds
|
|
186
|
+
- `onSuccess`: Callback when upload succeeds
|
|
187
|
+
- `onError`: Callback when upload fails
|
|
188
|
+
- `onProgress`: Callback on progress updates
|
|
189
|
+
- `onStart`: Callback when upload starts
|
|
190
|
+
- `onPause`: Callback when upload is paused
|
|
191
|
+
- `onResume`: Callback when upload is resumed
|
|
192
|
+
- `onCancel`: Callback when upload is cancelled
|
|
193
|
+
|
|
194
|
+
**Returns:**
|
|
195
|
+
|
|
196
|
+
- `upload(file)`: Function to start uploading a file
|
|
197
|
+
- `pause()`: Function to pause the upload
|
|
198
|
+
- `resume()`: Function to resume the upload
|
|
199
|
+
- `cancel()`: Function to cancel the upload
|
|
200
|
+
- `status`: Reactive ref with current upload status
|
|
201
|
+
- `progress`: Reactive ref with current upload progress
|
|
202
|
+
- `error`: Reactive ref with error if upload failed
|
|
203
|
+
- `task`: Reactive ref with the current upload task
|
|
204
|
+
|
|
205
|
+
### `useUploadList()`
|
|
206
|
+
|
|
207
|
+
Composable for managing multiple file uploads.
|
|
208
|
+
|
|
209
|
+
**Returns:**
|
|
210
|
+
|
|
211
|
+
- `tasks`: Reactive ref with array of all upload tasks
|
|
212
|
+
- `uploadFiles(files)`: Function to upload multiple files
|
|
213
|
+
- `pauseAll()`: Function to pause all uploads
|
|
214
|
+
- `resumeAll()`: Function to resume all uploads
|
|
215
|
+
- `cancelAll()`: Function to cancel all uploads
|
|
216
|
+
- `removeTask(taskId)`: Function to remove a specific task
|
|
217
|
+
- `clearCompleted()`: Function to clear all completed tasks
|
|
218
|
+
- `getStatistics()`: Function to get task statistics
|
|
219
|
+
|
|
220
|
+
### `useUploadManager()`
|
|
221
|
+
|
|
222
|
+
Composable for accessing the UploadManager instance directly.
|
|
223
|
+
|
|
224
|
+
**Returns:**
|
|
225
|
+
|
|
226
|
+
- `manager`: The UploadManager instance
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { UploadManager, UploadManager as UploadManager$1, UploadManagerOptions, UploadManagerOptions as UploadManagerOptions$1, UploadTask, UploadTask as UploadTask$1, UploadTaskOptions, UploadTaskOptions as UploadTaskOptions$1 } from "@chunkflowjs/core";
|
|
2
|
+
import { Plugin, Ref } from "vue";
|
|
3
|
+
import { ChunkInfo, FileInfo, RequestAdapter, RequestAdapter as RequestAdapter$1, UploadProgress, UploadProgress as UploadProgress$1, UploadStatus, UploadStatus as UploadStatus$1, UploadToken } from "@chunkflowjs/protocol";
|
|
4
|
+
|
|
5
|
+
//#region src/plugin.d.ts
|
|
6
|
+
declare const UPLOAD_MANAGER_KEY: unique symbol;
|
|
7
|
+
interface UploadPluginOptions {
|
|
8
|
+
requestAdapter: RequestAdapter$1;
|
|
9
|
+
managerOptions?: Partial<UploadManagerOptions$1>;
|
|
10
|
+
}
|
|
11
|
+
declare function createUploadPlugin(options: UploadPluginOptions): Plugin;
|
|
12
|
+
declare const _default: Plugin<UploadPluginOptions>;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/useUploadManager.d.ts
|
|
15
|
+
declare function useUploadManager(): UploadManager$1;
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/useUpload.d.ts
|
|
18
|
+
interface UseUploadOptions extends Partial<UploadTaskOptions$1> {
|
|
19
|
+
onSuccess?: (fileUrl: string) => void;
|
|
20
|
+
onError?: (error: Error) => void;
|
|
21
|
+
onProgress?: (progress: UploadProgress$1) => void;
|
|
22
|
+
onStart?: () => void;
|
|
23
|
+
onPause?: () => void;
|
|
24
|
+
onResume?: () => void;
|
|
25
|
+
onCancel?: () => void;
|
|
26
|
+
}
|
|
27
|
+
interface UseUploadReturn {
|
|
28
|
+
upload: (file: File) => void;
|
|
29
|
+
pause: () => void;
|
|
30
|
+
resume: () => void;
|
|
31
|
+
cancel: () => void;
|
|
32
|
+
status: Ref<UploadStatus$1>;
|
|
33
|
+
progress: Ref<UploadProgress$1>;
|
|
34
|
+
error: Ref<Error | null>;
|
|
35
|
+
task: Ref<UploadTask$1 | null>;
|
|
36
|
+
}
|
|
37
|
+
declare function useUpload(options?: UseUploadOptions): UseUploadReturn;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/useUploadList.d.ts
|
|
40
|
+
interface UseUploadListReturn {
|
|
41
|
+
tasks: Ref<UploadTask$1[]>;
|
|
42
|
+
uploadFiles: (files: File[]) => void;
|
|
43
|
+
pauseAll: () => void;
|
|
44
|
+
resumeAll: () => void;
|
|
45
|
+
cancelAll: () => void;
|
|
46
|
+
removeTask: (taskId: string) => void;
|
|
47
|
+
clearCompleted: () => void;
|
|
48
|
+
getStatistics: () => {
|
|
49
|
+
total: number;
|
|
50
|
+
idle: number;
|
|
51
|
+
uploading: number;
|
|
52
|
+
paused: number;
|
|
53
|
+
success: number;
|
|
54
|
+
error: number;
|
|
55
|
+
cancelled: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
declare function useUploadList(): UseUploadListReturn;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { type ChunkInfo, type FileInfo, type RequestAdapter, UPLOAD_MANAGER_KEY, type UploadManager, type UploadManagerOptions, _default as UploadPlugin, type UploadPluginOptions, type UploadProgress, type UploadStatus, type UploadTask, type UploadTaskOptions, type UploadToken, type UseUploadListReturn, type UseUploadOptions, type UseUploadReturn, createUploadPlugin, useUpload, useUploadList, useUploadManager };
|
|
61
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/plugin.ts","../src/useUploadManager.ts","../src/useUpload.ts","../src/useUploadList.ts"],"mappings":";;;;;cAsBa,kBAAA;AAAA,UAKI,mBAAA;EAEf,cAAA,EAAgB,gBAAA;EAEhB,cAAA,GAAiB,OAAA,CAAQ,sBAAA;AAAA;AAAA,iBAuCX,kBAAA,CAAmB,OAAA,EAAS,mBAAA,GAAsB,MAAA;AAAA,cAmCjE,QAAA,EAgBI,MAAA,CAAO,mBAAA;;;iBCjFI,gBAAA,CAAA,GAAoB,eAAA;;;UCrBnB,gBAAA,SAAyB,OAAA,CAAQ,mBAAA;EAEhD,SAAA,IAAa,OAAA;EAEb,OAAA,IAAW,KAAA,EAAO,KAAA;EAElB,UAAA,IAAc,QAAA,EAAU,gBAAA;EAExB,OAAA;EAEA,OAAA;EAEA,QAAA;EAEA,QAAA;AAAA;AAAA,UAMe,eAAA;EAEf,MAAA,GAAS,IAAA,EAAM,IAAA;EAEf,KAAA;EAEA,MAAA;EAEA,MAAA;EAEA,MAAA,EAAQ,GAAA,CAAI,cAAA;EAEZ,QAAA,EAAU,GAAA,CAAI,gBAAA;EAEd,KAAA,EAAO,GAAA,CAAI,KAAA;EAEX,IAAA,EAAM,GAAA,CAAI,YAAA;AAAA;AAAA,iBA8DI,SAAA,CAAU,OAAA,GAAS,gBAAA,GAAwB,eAAA;;;UCnG1C,mBAAA;EAEf,KAAA,EAAO,GAAA,CAAI,YAAA;EAEX,WAAA,GAAc,KAAA,EAAO,IAAA;EAErB,QAAA;EAEA,SAAA;EAEA,SAAA;EAEA,UAAA,GAAa,MAAA;EAEb,cAAA;EAEA,aAAA;IACE,KAAA;IACA,IAAA;IACA,SAAA;IACA,MAAA;IACA,OAAA;IACA,KAAA;IACA,SAAA;EAAA;AAAA;AAAA,iBAqEY,aAAA,CAAA,GAAiB,mBAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { UploadManager } from "@chunkflowjs/core";
|
|
2
|
+
import { inject, onMounted, onUnmounted, ref } from "vue";
|
|
3
|
+
|
|
4
|
+
//#region src/plugin.ts
|
|
5
|
+
/**
|
|
6
|
+
* Injection key for UploadManager
|
|
7
|
+
*
|
|
8
|
+
* Use this key with inject() to access the UploadManager instance.
|
|
9
|
+
* Prefer using the useUploadManager() composable instead.
|
|
10
|
+
*/
|
|
11
|
+
const UPLOAD_MANAGER_KEY = Symbol("uploadManager");
|
|
12
|
+
/**
|
|
13
|
+
* Vue Plugin for ChunkFlow Upload SDK
|
|
14
|
+
*
|
|
15
|
+
* Install this plugin in your Vue application to enable upload functionality.
|
|
16
|
+
* Creates and manages a single UploadManager instance for the entire app.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* - Validates: Requirement 10.2 (Vue Composables)
|
|
20
|
+
* - Validates: Requirement 10.3 (auto-initialize on install)
|
|
21
|
+
* - Validates: Requirement 10.4 (auto-cleanup on unmount)
|
|
22
|
+
* - Creates UploadManager instance on install
|
|
23
|
+
* - Provides manager via Vue's provide/inject
|
|
24
|
+
* - Initializes manager automatically
|
|
25
|
+
* - Cleans up manager on app unmount
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { createApp } from 'vue';
|
|
30
|
+
* import { createUploadPlugin } from '@chunkflowjs/upload-client-vue';
|
|
31
|
+
* import { myRequestAdapter } from './api';
|
|
32
|
+
* import App from './App.vue';
|
|
33
|
+
*
|
|
34
|
+
* const app = createApp(App);
|
|
35
|
+
*
|
|
36
|
+
* const uploadPlugin = createUploadPlugin({
|
|
37
|
+
* requestAdapter: myRequestAdapter,
|
|
38
|
+
* managerOptions: {
|
|
39
|
+
* maxConcurrentTasks: 5,
|
|
40
|
+
* defaultChunkSize: 2 * 1024 * 1024, // 2MB
|
|
41
|
+
* },
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* app.use(uploadPlugin);
|
|
45
|
+
* app.mount('#app');
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
function createUploadPlugin(options) {
|
|
49
|
+
const { requestAdapter, managerOptions } = options;
|
|
50
|
+
return { install(app) {
|
|
51
|
+
const manager = new UploadManager({
|
|
52
|
+
requestAdapter,
|
|
53
|
+
...managerOptions
|
|
54
|
+
});
|
|
55
|
+
app.provide(UPLOAD_MANAGER_KEY, manager);
|
|
56
|
+
manager.init().catch((error) => {
|
|
57
|
+
console.error("Failed to initialize UploadManager:", error);
|
|
58
|
+
});
|
|
59
|
+
const originalUnmount = app.unmount.bind(app);
|
|
60
|
+
app.unmount = () => {
|
|
61
|
+
manager.close();
|
|
62
|
+
originalUnmount();
|
|
63
|
+
};
|
|
64
|
+
} };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Default export for convenience
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* import UploadPlugin from '@chunkflowjs/upload-client-vue';
|
|
72
|
+
* app.use(UploadPlugin, { requestAdapter: myAdapter });
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
var plugin_default = { install(app, options) {
|
|
76
|
+
createUploadPlugin(options).install(app);
|
|
77
|
+
} };
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/useUploadManager.ts
|
|
81
|
+
/**
|
|
82
|
+
* useUploadManager - Vue Composable for accessing UploadManager
|
|
83
|
+
*
|
|
84
|
+
* Provides access to the UploadManager instance via Vue's inject API.
|
|
85
|
+
* Must be used within a component that has the Upload Plugin installed.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* - Validates: Requirement 10.2 (Vue Composables)
|
|
89
|
+
*/
|
|
90
|
+
/**
|
|
91
|
+
* Composable for accessing the UploadManager instance
|
|
92
|
+
*
|
|
93
|
+
* Retrieves the UploadManager instance provided by the Upload Plugin.
|
|
94
|
+
* Throws an error if used outside of a component tree with the plugin installed.
|
|
95
|
+
*
|
|
96
|
+
* @returns The UploadManager instance
|
|
97
|
+
* @throws Error if Upload Plugin is not installed
|
|
98
|
+
*
|
|
99
|
+
* @remarks
|
|
100
|
+
* - Validates: Requirement 10.2 (Vue Composables)
|
|
101
|
+
* - Uses Vue's inject API
|
|
102
|
+
* - Validates plugin installation
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```vue
|
|
106
|
+
* <script setup lang="ts">
|
|
107
|
+
* import { useUploadManager } from '@chunkflowjs/upload-client-vue';
|
|
108
|
+
*
|
|
109
|
+
* const manager = useUploadManager();
|
|
110
|
+
*
|
|
111
|
+
* // Use manager to create tasks, etc.
|
|
112
|
+
* const task = manager.createTask(file);
|
|
113
|
+
* <\/script>
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
function useUploadManager() {
|
|
117
|
+
const manager = inject(UPLOAD_MANAGER_KEY);
|
|
118
|
+
if (!manager) throw new Error("useUploadManager must be used within a component tree that has the Upload Plugin installed. Make sure you have called app.use(createUploadPlugin({ ... })) in your main.ts file.");
|
|
119
|
+
return manager;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/useUpload.ts
|
|
124
|
+
/**
|
|
125
|
+
* useUpload - Vue Composable for single file upload
|
|
126
|
+
*
|
|
127
|
+
* Provides a simple interface for uploading a single file with reactive state.
|
|
128
|
+
* Handles task creation, lifecycle events, and state management.
|
|
129
|
+
*
|
|
130
|
+
* @remarks
|
|
131
|
+
* - Validates: Requirement 10.2 (Vue Composables)
|
|
132
|
+
* - Validates: Requirement 10.5 (reactive upload state)
|
|
133
|
+
*/
|
|
134
|
+
/**
|
|
135
|
+
* Composable for uploading a single file
|
|
136
|
+
*
|
|
137
|
+
* Provides reactive state and control functions for file upload.
|
|
138
|
+
* Automatically manages task lifecycle and updates reactive state.
|
|
139
|
+
*
|
|
140
|
+
* @param options - Configuration options and callbacks
|
|
141
|
+
* @returns Upload control functions and reactive state
|
|
142
|
+
*
|
|
143
|
+
* @remarks
|
|
144
|
+
* - Validates: Requirement 10.2 (Vue Composables)
|
|
145
|
+
* - Validates: Requirement 10.5 (reactive state)
|
|
146
|
+
* - Creates new task for each upload
|
|
147
|
+
* - Automatically subscribes to task events
|
|
148
|
+
* - Updates reactive state on events
|
|
149
|
+
* - Cleans up event listeners on unmount
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```vue
|
|
153
|
+
* <script setup lang="ts">
|
|
154
|
+
* import { useUpload } from '@chunkflowjs/upload-client-vue';
|
|
155
|
+
*
|
|
156
|
+
* const { upload, pause, resume, cancel, status, progress, error } = useUpload({
|
|
157
|
+
* onSuccess: (fileUrl) => {
|
|
158
|
+
* console.log('Upload complete:', fileUrl);
|
|
159
|
+
* },
|
|
160
|
+
* onError: (error) => {
|
|
161
|
+
* console.error('Upload failed:', error);
|
|
162
|
+
* },
|
|
163
|
+
* onProgress: (progress) => {
|
|
164
|
+
* console.log(`Progress: ${progress.percentage}%`);
|
|
165
|
+
* },
|
|
166
|
+
* });
|
|
167
|
+
*
|
|
168
|
+
* const handleFileSelect = (event: Event) => {
|
|
169
|
+
* const input = event.target as HTMLInputElement;
|
|
170
|
+
* const file = input.files?.[0];
|
|
171
|
+
* if (file) {
|
|
172
|
+
* upload(file);
|
|
173
|
+
* }
|
|
174
|
+
* };
|
|
175
|
+
* <\/script>
|
|
176
|
+
*
|
|
177
|
+
* <template>
|
|
178
|
+
* <div>
|
|
179
|
+
* <input type="file" @change="handleFileSelect" />
|
|
180
|
+
* <div v-if="status === 'uploading'">
|
|
181
|
+
* <p>Progress: {{ progress.percentage.toFixed(1) }}%</p>
|
|
182
|
+
* <p>Speed: {{ (progress.speed / 1024 / 1024).toFixed(2) }} MB/s</p>
|
|
183
|
+
* <button @click="pause">Pause</button>
|
|
184
|
+
* <button @click="cancel">Cancel</button>
|
|
185
|
+
* </div>
|
|
186
|
+
* <button v-if="status === 'paused'" @click="resume">Resume</button>
|
|
187
|
+
* <p v-if="status === 'success'">Upload complete!</p>
|
|
188
|
+
* <p v-if="status === 'error'">Error: {{ error?.message }}</p>
|
|
189
|
+
* </div>
|
|
190
|
+
* </template>
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
function useUpload(options = {}) {
|
|
194
|
+
const manager = useUploadManager();
|
|
195
|
+
const task = ref(null);
|
|
196
|
+
const status = ref("idle");
|
|
197
|
+
const progress = ref({
|
|
198
|
+
uploadedBytes: 0,
|
|
199
|
+
totalBytes: 0,
|
|
200
|
+
percentage: 0,
|
|
201
|
+
speed: 0,
|
|
202
|
+
remainingTime: 0,
|
|
203
|
+
uploadedChunks: 0,
|
|
204
|
+
totalChunks: 0
|
|
205
|
+
});
|
|
206
|
+
const error = ref(null);
|
|
207
|
+
const upload = (file) => {
|
|
208
|
+
error.value = null;
|
|
209
|
+
status.value = "idle";
|
|
210
|
+
const newTask = manager.createTask(file, {
|
|
211
|
+
chunkSize: options.chunkSize,
|
|
212
|
+
concurrency: options.concurrency,
|
|
213
|
+
retryCount: options.retryCount,
|
|
214
|
+
retryDelay: options.retryDelay,
|
|
215
|
+
autoStart: false
|
|
216
|
+
});
|
|
217
|
+
newTask.on("start", () => {
|
|
218
|
+
status.value = "uploading";
|
|
219
|
+
options.onStart?.();
|
|
220
|
+
});
|
|
221
|
+
newTask.on("progress", () => {
|
|
222
|
+
const progressData = newTask.getProgress();
|
|
223
|
+
progress.value = progressData;
|
|
224
|
+
options.onProgress?.(progressData);
|
|
225
|
+
});
|
|
226
|
+
newTask.on("success", ({ fileUrl }) => {
|
|
227
|
+
status.value = "success";
|
|
228
|
+
options.onSuccess?.(fileUrl);
|
|
229
|
+
});
|
|
230
|
+
newTask.on("error", ({ error: err }) => {
|
|
231
|
+
status.value = "error";
|
|
232
|
+
error.value = err;
|
|
233
|
+
options.onError?.(err);
|
|
234
|
+
});
|
|
235
|
+
newTask.on("pause", () => {
|
|
236
|
+
status.value = "paused";
|
|
237
|
+
options.onPause?.();
|
|
238
|
+
});
|
|
239
|
+
newTask.on("resume", () => {
|
|
240
|
+
status.value = "uploading";
|
|
241
|
+
options.onResume?.();
|
|
242
|
+
});
|
|
243
|
+
newTask.on("cancel", () => {
|
|
244
|
+
status.value = "cancelled";
|
|
245
|
+
options.onCancel?.();
|
|
246
|
+
});
|
|
247
|
+
task.value = newTask;
|
|
248
|
+
newTask.start().catch((err) => {
|
|
249
|
+
console.error("Upload failed:", err);
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
const pause = () => {
|
|
253
|
+
task.value?.pause();
|
|
254
|
+
};
|
|
255
|
+
const resume = () => {
|
|
256
|
+
task.value?.resume().catch((err) => {
|
|
257
|
+
console.error("Resume failed:", err);
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
const cancel = () => {
|
|
261
|
+
task.value?.cancel();
|
|
262
|
+
};
|
|
263
|
+
onUnmounted(() => {
|
|
264
|
+
if (task.value) {
|
|
265
|
+
const currentStatus = task.value.getStatus();
|
|
266
|
+
if (currentStatus === "uploading" || currentStatus === "paused") task.value.cancel();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
return {
|
|
270
|
+
upload,
|
|
271
|
+
pause,
|
|
272
|
+
resume,
|
|
273
|
+
cancel,
|
|
274
|
+
status,
|
|
275
|
+
progress,
|
|
276
|
+
error,
|
|
277
|
+
task
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/useUploadList.ts
|
|
283
|
+
/**
|
|
284
|
+
* useUploadList - Vue Composable for managing multiple file uploads
|
|
285
|
+
*
|
|
286
|
+
* Provides reactive state and control functions for managing a list of upload tasks.
|
|
287
|
+
* Automatically syncs with the UploadManager's task list.
|
|
288
|
+
*
|
|
289
|
+
* @remarks
|
|
290
|
+
* - Validates: Requirement 10.2 (Vue Composables)
|
|
291
|
+
* - Validates: Requirement 10.5 (reactive state)
|
|
292
|
+
*/
|
|
293
|
+
/**
|
|
294
|
+
* Composable for managing multiple file uploads
|
|
295
|
+
*
|
|
296
|
+
* Provides reactive state for all upload tasks and batch control functions.
|
|
297
|
+
* Automatically updates when tasks are added, removed, or change state.
|
|
298
|
+
*
|
|
299
|
+
* @returns Upload list control functions and reactive state
|
|
300
|
+
*
|
|
301
|
+
* @remarks
|
|
302
|
+
* - Validates: Requirement 10.2 (Vue Composables)
|
|
303
|
+
* - Validates: Requirement 10.5 (reactive state)
|
|
304
|
+
* - Polls manager for task updates (100ms interval)
|
|
305
|
+
* - Provides batch operations for all tasks
|
|
306
|
+
* - Automatically cleans up on unmount
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```vue
|
|
310
|
+
* <script setup lang="ts">
|
|
311
|
+
* import { useUploadList } from '@chunkflowjs/upload-client-vue';
|
|
312
|
+
*
|
|
313
|
+
* const {
|
|
314
|
+
* tasks,
|
|
315
|
+
* uploadFiles,
|
|
316
|
+
* pauseAll,
|
|
317
|
+
* resumeAll,
|
|
318
|
+
* cancelAll,
|
|
319
|
+
* removeTask,
|
|
320
|
+
* clearCompleted,
|
|
321
|
+
* getStatistics,
|
|
322
|
+
* } = useUploadList();
|
|
323
|
+
*
|
|
324
|
+
* const handleFilesSelect = (event: Event) => {
|
|
325
|
+
* const input = event.target as HTMLInputElement;
|
|
326
|
+
* const files = Array.from(input.files || []);
|
|
327
|
+
* uploadFiles(files);
|
|
328
|
+
* };
|
|
329
|
+
*
|
|
330
|
+
* const stats = getStatistics();
|
|
331
|
+
* <\/script>
|
|
332
|
+
*
|
|
333
|
+
* <template>
|
|
334
|
+
* <div>
|
|
335
|
+
* <input type="file" multiple @change="handleFilesSelect" />
|
|
336
|
+
* <div>
|
|
337
|
+
* <button @click="pauseAll">Pause All</button>
|
|
338
|
+
* <button @click="resumeAll">Resume All</button>
|
|
339
|
+
* <button @click="cancelAll">Cancel All</button>
|
|
340
|
+
* <button @click="clearCompleted">Clear Completed</button>
|
|
341
|
+
* </div>
|
|
342
|
+
* <p>
|
|
343
|
+
* Total: {{ stats.total }} | Uploading: {{ stats.uploading }} |
|
|
344
|
+
* Success: {{ stats.success }} | Error: {{ stats.error }}
|
|
345
|
+
* </p>
|
|
346
|
+
* <ul>
|
|
347
|
+
* <li v-for="task in tasks" :key="task.id">
|
|
348
|
+
* <span>{{ task.file.name }}</span>
|
|
349
|
+
* <span>{{ task.getStatus() }}</span>
|
|
350
|
+
* <span>{{ task.getProgress().percentage.toFixed(1) }}%</span>
|
|
351
|
+
* <button @click="removeTask(task.id)">Remove</button>
|
|
352
|
+
* </li>
|
|
353
|
+
* </ul>
|
|
354
|
+
* </div>
|
|
355
|
+
* </template>
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
function useUploadList() {
|
|
359
|
+
const manager = useUploadManager();
|
|
360
|
+
const tasks = ref([]);
|
|
361
|
+
let intervalId = null;
|
|
362
|
+
onMounted(() => {
|
|
363
|
+
tasks.value = manager.getAllTasks();
|
|
364
|
+
intervalId = setInterval(() => {
|
|
365
|
+
tasks.value = manager.getAllTasks();
|
|
366
|
+
}, 100);
|
|
367
|
+
});
|
|
368
|
+
onUnmounted(() => {
|
|
369
|
+
if (intervalId !== null) {
|
|
370
|
+
clearInterval(intervalId);
|
|
371
|
+
intervalId = null;
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
const uploadFiles = (files) => {
|
|
375
|
+
files.forEach((file) => {
|
|
376
|
+
manager.createTask(file).start().catch((error) => {
|
|
377
|
+
console.error(`Failed to upload ${file.name}:`, error);
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
const pauseAll = () => {
|
|
382
|
+
manager.pauseAll();
|
|
383
|
+
};
|
|
384
|
+
const resumeAll = () => {
|
|
385
|
+
manager.resumeAll().catch((error) => {
|
|
386
|
+
console.error("Failed to resume all uploads:", error);
|
|
387
|
+
});
|
|
388
|
+
};
|
|
389
|
+
const cancelAll = () => {
|
|
390
|
+
manager.cancelAll();
|
|
391
|
+
};
|
|
392
|
+
const removeTask = (taskId) => {
|
|
393
|
+
manager.deleteTask(taskId).catch((error) => {
|
|
394
|
+
console.error(`Failed to remove task ${taskId}:`, error);
|
|
395
|
+
});
|
|
396
|
+
};
|
|
397
|
+
const clearCompleted = () => {
|
|
398
|
+
manager.clearCompletedTasks().catch((error) => {
|
|
399
|
+
console.error("Failed to clear completed tasks:", error);
|
|
400
|
+
});
|
|
401
|
+
};
|
|
402
|
+
const getStatistics = () => {
|
|
403
|
+
return manager.getStatistics();
|
|
404
|
+
};
|
|
405
|
+
return {
|
|
406
|
+
tasks,
|
|
407
|
+
uploadFiles,
|
|
408
|
+
pauseAll,
|
|
409
|
+
resumeAll,
|
|
410
|
+
cancelAll,
|
|
411
|
+
removeTask,
|
|
412
|
+
clearCompleted,
|
|
413
|
+
getStatistics
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
//#endregion
|
|
418
|
+
export { UPLOAD_MANAGER_KEY, plugin_default as UploadPlugin, createUploadPlugin, useUpload, useUploadList, useUploadManager };
|
|
419
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/plugin.ts","../src/useUploadManager.ts","../src/useUpload.ts","../src/useUploadList.ts"],"sourcesContent":["/**\n * Vue Plugin for ChunkFlow Upload SDK\n *\n * Provides UploadManager instance to Vue application via provide/inject.\n * Handles initialization and cleanup of the manager.\n *\n * @remarks\n * - Validates: Requirement 10.2 (Vue Composables integration)\n * - Validates: Requirement 10.3 (auto-initialize on mount)\n * - Validates: Requirement 10.4 (auto-cleanup on unmount)\n */\n\nimport type { App, Plugin } from \"vue\";\nimport { UploadManager, type UploadManagerOptions } from \"@chunkflowjs/core\";\nimport type { RequestAdapter } from \"@chunkflowjs/protocol\";\n\n/**\n * Injection key for UploadManager\n *\n * Use this key with inject() to access the UploadManager instance.\n * Prefer using the useUploadManager() composable instead.\n */\nexport const UPLOAD_MANAGER_KEY = Symbol(\"uploadManager\");\n\n/**\n * Options for the Upload Plugin\n */\nexport interface UploadPluginOptions {\n /** Request adapter for API calls (required) */\n requestAdapter: RequestAdapter;\n /** Optional configuration for the UploadManager */\n managerOptions?: Partial<UploadManagerOptions>;\n}\n\n/**\n * Vue Plugin for ChunkFlow Upload SDK\n *\n * Install this plugin in your Vue application to enable upload functionality.\n * Creates and manages a single UploadManager instance for the entire app.\n *\n * @remarks\n * - Validates: Requirement 10.2 (Vue Composables)\n * - Validates: Requirement 10.3 (auto-initialize on install)\n * - Validates: Requirement 10.4 (auto-cleanup on unmount)\n * - Creates UploadManager instance on install\n * - Provides manager via Vue's provide/inject\n * - Initializes manager automatically\n * - Cleans up manager on app unmount\n *\n * @example\n * ```typescript\n * import { createApp } from 'vue';\n * import { createUploadPlugin } from '@chunkflowjs/upload-client-vue';\n * import { myRequestAdapter } from './api';\n * import App from './App.vue';\n *\n * const app = createApp(App);\n *\n * const uploadPlugin = createUploadPlugin({\n * requestAdapter: myRequestAdapter,\n * managerOptions: {\n * maxConcurrentTasks: 5,\n * defaultChunkSize: 2 * 1024 * 1024, // 2MB\n * },\n * });\n *\n * app.use(uploadPlugin);\n * app.mount('#app');\n * ```\n */\nexport function createUploadPlugin(options: UploadPluginOptions): Plugin {\n const { requestAdapter, managerOptions } = options;\n\n // Create the plugin object\n const plugin: Plugin = {\n install(app: App) {\n // Create UploadManager instance\n const manager = new UploadManager({\n requestAdapter,\n ...managerOptions,\n });\n\n // Provide manager to all components\n // Requirement 10.2: Vue provide/inject mechanism\n app.provide(UPLOAD_MANAGER_KEY, manager);\n\n // Initialize manager\n // Requirement 10.3: Auto-initialize on install\n manager.init().catch((error) => {\n console.error(\"Failed to initialize UploadManager:\", error);\n });\n\n // Cleanup on app unmount\n // Requirement 10.4: Auto-cleanup on unmount\n // Note: Vue 3 doesn't have a built-in unmount hook for plugins,\n // but we can use the app's unmount lifecycle\n const originalUnmount = app.unmount.bind(app);\n app.unmount = () => {\n manager.close();\n originalUnmount();\n };\n },\n };\n\n return plugin;\n}\n\n/**\n * Default export for convenience\n *\n * @example\n * ```typescript\n * import UploadPlugin from '@chunkflowjs/upload-client-vue';\n * app.use(UploadPlugin, { requestAdapter: myAdapter });\n * ```\n */\nexport default {\n install(app: App, options: UploadPluginOptions) {\n const plugin = createUploadPlugin(options);\n plugin.install!(app);\n },\n} as Plugin<UploadPluginOptions>;\n","/**\n * useUploadManager - Vue Composable for accessing UploadManager\n *\n * Provides access to the UploadManager instance via Vue's inject API.\n * Must be used within a component that has the Upload Plugin installed.\n *\n * @remarks\n * - Validates: Requirement 10.2 (Vue Composables)\n */\n\nimport { inject } from \"vue\";\nimport type { UploadManager } from \"@chunkflowjs/core\";\nimport { UPLOAD_MANAGER_KEY } from \"./plugin\";\n\n/**\n * Composable for accessing the UploadManager instance\n *\n * Retrieves the UploadManager instance provided by the Upload Plugin.\n * Throws an error if used outside of a component tree with the plugin installed.\n *\n * @returns The UploadManager instance\n * @throws Error if Upload Plugin is not installed\n *\n * @remarks\n * - Validates: Requirement 10.2 (Vue Composables)\n * - Uses Vue's inject API\n * - Validates plugin installation\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useUploadManager } from '@chunkflowjs/upload-client-vue';\n *\n * const manager = useUploadManager();\n *\n * // Use manager to create tasks, etc.\n * const task = manager.createTask(file);\n * </script>\n * ```\n */\nexport function useUploadManager(): UploadManager {\n const manager = inject<UploadManager>(UPLOAD_MANAGER_KEY);\n\n if (!manager) {\n throw new Error(\n \"useUploadManager must be used within a component tree that has the Upload Plugin installed. \" +\n \"Make sure you have called app.use(createUploadPlugin({ ... })) in your main.ts file.\",\n );\n }\n\n return manager;\n}\n","/**\n * useUpload - Vue Composable for single file upload\n *\n * Provides a simple interface for uploading a single file with reactive state.\n * Handles task creation, lifecycle events, and state management.\n *\n * @remarks\n * - Validates: Requirement 10.2 (Vue Composables)\n * - Validates: Requirement 10.5 (reactive upload state)\n */\n\nimport { ref, onUnmounted, type Ref } from \"vue\";\nimport type { UploadTask, UploadTaskOptions } from \"@chunkflowjs/core\";\nimport type { UploadStatus, UploadProgress } from \"@chunkflowjs/protocol\";\nimport { useUploadManager } from \"./useUploadManager\";\n\n/**\n * Options for useUpload composable\n */\nexport interface UseUploadOptions extends Partial<UploadTaskOptions> {\n /** Callback when upload succeeds */\n onSuccess?: (fileUrl: string) => void;\n /** Callback when upload fails */\n onError?: (error: Error) => void;\n /** Callback on progress updates */\n onProgress?: (progress: UploadProgress) => void;\n /** Callback when upload starts */\n onStart?: () => void;\n /** Callback when upload is paused */\n onPause?: () => void;\n /** Callback when upload is resumed */\n onResume?: () => void;\n /** Callback when upload is cancelled */\n onCancel?: () => void;\n}\n\n/**\n * Return value from useUpload composable\n */\nexport interface UseUploadReturn {\n /** Function to start uploading a file */\n upload: (file: File) => void;\n /** Function to pause the current upload */\n pause: () => void;\n /** Function to resume a paused upload */\n resume: () => void;\n /** Function to cancel the current upload */\n cancel: () => void;\n /** Current upload status (reactive) */\n status: Ref<UploadStatus>;\n /** Current upload progress (reactive) */\n progress: Ref<UploadProgress>;\n /** Error if upload failed (reactive) */\n error: Ref<Error | null>;\n /** The current upload task (reactive, null if no upload in progress) */\n task: Ref<UploadTask | null>;\n}\n\n/**\n * Composable for uploading a single file\n *\n * Provides reactive state and control functions for file upload.\n * Automatically manages task lifecycle and updates reactive state.\n *\n * @param options - Configuration options and callbacks\n * @returns Upload control functions and reactive state\n *\n * @remarks\n * - Validates: Requirement 10.2 (Vue Composables)\n * - Validates: Requirement 10.5 (reactive state)\n * - Creates new task for each upload\n * - Automatically subscribes to task events\n * - Updates reactive state on events\n * - Cleans up event listeners on unmount\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useUpload } from '@chunkflowjs/upload-client-vue';\n *\n * const { upload, pause, resume, cancel, status, progress, error } = useUpload({\n * onSuccess: (fileUrl) => {\n * console.log('Upload complete:', fileUrl);\n * },\n * onError: (error) => {\n * console.error('Upload failed:', error);\n * },\n * onProgress: (progress) => {\n * console.log(`Progress: ${progress.percentage}%`);\n * },\n * });\n *\n * const handleFileSelect = (event: Event) => {\n * const input = event.target as HTMLInputElement;\n * const file = input.files?.[0];\n * if (file) {\n * upload(file);\n * }\n * };\n * </script>\n *\n * <template>\n * <div>\n * <input type=\"file\" @change=\"handleFileSelect\" />\n * <div v-if=\"status === 'uploading'\">\n * <p>Progress: {{ progress.percentage.toFixed(1) }}%</p>\n * <p>Speed: {{ (progress.speed / 1024 / 1024).toFixed(2) }} MB/s</p>\n * <button @click=\"pause\">Pause</button>\n * <button @click=\"cancel\">Cancel</button>\n * </div>\n * <button v-if=\"status === 'paused'\" @click=\"resume\">Resume</button>\n * <p v-if=\"status === 'success'\">Upload complete!</p>\n * <p v-if=\"status === 'error'\">Error: {{ error?.message }}</p>\n * </div>\n * </template>\n * ```\n */\nexport function useUpload(options: UseUploadOptions = {}): UseUploadReturn {\n const manager = useUploadManager();\n\n // Reactive state\n const task = ref<UploadTask | null>(null);\n const status = ref<UploadStatus>(\"idle\" as UploadStatus);\n const progress = ref<UploadProgress>({\n uploadedBytes: 0,\n totalBytes: 0,\n percentage: 0,\n speed: 0,\n remainingTime: 0,\n uploadedChunks: 0,\n totalChunks: 0,\n });\n const error = ref<Error | null>(null);\n\n // Upload function\n const upload = (file: File) => {\n // Reset state\n error.value = null;\n status.value = \"idle\" as UploadStatus;\n\n // Create new task\n const newTask = manager.createTask(file, {\n chunkSize: options.chunkSize,\n concurrency: options.concurrency,\n retryCount: options.retryCount,\n retryDelay: options.retryDelay,\n autoStart: false, // We'll start it manually\n });\n\n // Set up event listeners\n newTask.on(\"start\", () => {\n status.value = \"uploading\" as UploadStatus;\n options.onStart?.();\n });\n\n newTask.on(\"progress\", () => {\n const progressData = newTask.getProgress();\n progress.value = progressData;\n options.onProgress?.(progressData);\n });\n\n newTask.on(\"success\", ({ fileUrl }) => {\n status.value = \"success\" as UploadStatus;\n options.onSuccess?.(fileUrl);\n });\n\n newTask.on(\"error\", ({ error: err }) => {\n status.value = \"error\" as UploadStatus;\n error.value = err;\n options.onError?.(err);\n });\n\n newTask.on(\"pause\", () => {\n status.value = \"paused\" as UploadStatus;\n options.onPause?.();\n });\n\n newTask.on(\"resume\", () => {\n status.value = \"uploading\" as UploadStatus;\n options.onResume?.();\n });\n\n newTask.on(\"cancel\", () => {\n status.value = \"cancelled\" as UploadStatus;\n options.onCancel?.();\n });\n\n // Store task reference\n task.value = newTask;\n\n // Start upload\n newTask.start().catch((err) => {\n console.error(\"Upload failed:\", err);\n });\n };\n\n // Control functions\n const pause = () => {\n task.value?.pause();\n };\n\n const resume = () => {\n task.value?.resume().catch((err) => {\n console.error(\"Resume failed:\", err);\n });\n };\n\n const cancel = () => {\n task.value?.cancel();\n };\n\n // Cleanup on unmount\n onUnmounted(() => {\n // Cancel task if still running\n if (task.value) {\n const currentStatus = task.value.getStatus();\n if (currentStatus === \"uploading\" || currentStatus === \"paused\") {\n task.value.cancel();\n }\n }\n });\n\n return {\n upload,\n pause,\n resume,\n cancel,\n status,\n progress,\n error,\n task,\n } as UseUploadReturn;\n}\n","/**\n * useUploadList - Vue Composable for managing multiple file uploads\n *\n * Provides reactive state and control functions for managing a list of upload tasks.\n * Automatically syncs with the UploadManager's task list.\n *\n * @remarks\n * - Validates: Requirement 10.2 (Vue Composables)\n * - Validates: Requirement 10.5 (reactive state)\n */\n\nimport { ref, onMounted, onUnmounted, type Ref } from \"vue\";\nimport type { UploadTask } from \"@chunkflowjs/core\";\nimport { useUploadManager } from \"./useUploadManager\";\n\n/**\n * Return value from useUploadList composable\n */\nexport interface UseUploadListReturn {\n /** Array of all upload tasks (reactive) */\n tasks: Ref<UploadTask[]>;\n /** Function to upload multiple files */\n uploadFiles: (files: File[]) => void;\n /** Function to pause all running uploads */\n pauseAll: () => void;\n /** Function to resume all paused uploads */\n resumeAll: () => void;\n /** Function to cancel all uploads */\n cancelAll: () => void;\n /** Function to remove a specific task */\n removeTask: (taskId: string) => void;\n /** Function to clear all completed tasks */\n clearCompleted: () => void;\n /** Function to get task statistics (reactive) */\n getStatistics: () => {\n total: number;\n idle: number;\n uploading: number;\n paused: number;\n success: number;\n error: number;\n cancelled: number;\n };\n}\n\n/**\n * Composable for managing multiple file uploads\n *\n * Provides reactive state for all upload tasks and batch control functions.\n * Automatically updates when tasks are added, removed, or change state.\n *\n * @returns Upload list control functions and reactive state\n *\n * @remarks\n * - Validates: Requirement 10.2 (Vue Composables)\n * - Validates: Requirement 10.5 (reactive state)\n * - Polls manager for task updates (100ms interval)\n * - Provides batch operations for all tasks\n * - Automatically cleans up on unmount\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useUploadList } from '@chunkflowjs/upload-client-vue';\n *\n * const {\n * tasks,\n * uploadFiles,\n * pauseAll,\n * resumeAll,\n * cancelAll,\n * removeTask,\n * clearCompleted,\n * getStatistics,\n * } = useUploadList();\n *\n * const handleFilesSelect = (event: Event) => {\n * const input = event.target as HTMLInputElement;\n * const files = Array.from(input.files || []);\n * uploadFiles(files);\n * };\n *\n * const stats = getStatistics();\n * </script>\n *\n * <template>\n * <div>\n * <input type=\"file\" multiple @change=\"handleFilesSelect\" />\n * <div>\n * <button @click=\"pauseAll\">Pause All</button>\n * <button @click=\"resumeAll\">Resume All</button>\n * <button @click=\"cancelAll\">Cancel All</button>\n * <button @click=\"clearCompleted\">Clear Completed</button>\n * </div>\n * <p>\n * Total: {{ stats.total }} | Uploading: {{ stats.uploading }} |\n * Success: {{ stats.success }} | Error: {{ stats.error }}\n * </p>\n * <ul>\n * <li v-for=\"task in tasks\" :key=\"task.id\">\n * <span>{{ task.file.name }}</span>\n * <span>{{ task.getStatus() }}</span>\n * <span>{{ task.getProgress().percentage.toFixed(1) }}%</span>\n * <button @click=\"removeTask(task.id)\">Remove</button>\n * </li>\n * </ul>\n * </div>\n * </template>\n * ```\n */\nexport function useUploadList(): UseUploadListReturn {\n const manager = useUploadManager();\n\n // Reactive state for task list\n const tasks = ref<UploadTask[]>([]);\n\n // Polling interval ID\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n // Initialize and start polling\n onMounted(() => {\n // Initial load\n tasks.value = manager.getAllTasks();\n\n // Set up polling interval\n // We poll every 100ms to catch task updates\n // This is a simple approach - a more sophisticated solution would use\n // event subscriptions, but that would require changes to UploadManager\n intervalId = setInterval(() => {\n tasks.value = manager.getAllTasks();\n }, 100);\n });\n\n // Cleanup on unmount\n onUnmounted(() => {\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n });\n\n // Upload multiple files\n const uploadFiles = (files: File[]) => {\n files.forEach((file) => {\n const task = manager.createTask(file);\n task.start().catch((error) => {\n console.error(`Failed to upload ${file.name}:`, error);\n });\n });\n };\n\n // Pause all running uploads\n const pauseAll = () => {\n manager.pauseAll();\n };\n\n // Resume all paused uploads\n const resumeAll = () => {\n manager.resumeAll().catch((error) => {\n console.error(\"Failed to resume all uploads:\", error);\n });\n };\n\n // Cancel all uploads\n const cancelAll = () => {\n manager.cancelAll();\n };\n\n // Remove a specific task\n const removeTask = (taskId: string) => {\n manager.deleteTask(taskId).catch((error) => {\n console.error(`Failed to remove task ${taskId}:`, error);\n });\n };\n\n // Clear all completed tasks\n const clearCompleted = () => {\n manager.clearCompletedTasks().catch((error) => {\n console.error(\"Failed to clear completed tasks:\", error);\n });\n };\n\n // Get task statistics\n const getStatistics = () => {\n return manager.getStatistics();\n };\n\n return {\n tasks,\n uploadFiles,\n pauseAll,\n resumeAll,\n cancelAll,\n removeTask,\n clearCompleted,\n getStatistics,\n } as UseUploadListReturn;\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chunkflowjs/upload-client-vue",
|
|
3
|
+
"version": "0.0.1-alpha.1",
|
|
4
|
+
"description": "Vue adapter for ChunkFlow Upload SDK",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"composables",
|
|
7
|
+
"typescript",
|
|
8
|
+
"upload",
|
|
9
|
+
"vue"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "./dist/index.mjs",
|
|
18
|
+
"module": "./dist/index.mjs",
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
23
|
+
"import": "./dist/index.mjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsdown src/index.ts --format esm --dts",
|
|
28
|
+
"dev": "tsdown src/index.ts --format esm --dts --watch",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"clean": "rm -rf dist"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@chunkflowjs/core": "workspace:*",
|
|
36
|
+
"@chunkflowjs/protocol": "workspace:*"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@vue/test-utils": "^2.4.6",
|
|
40
|
+
"happy-dom": "^20.4.0",
|
|
41
|
+
"tsdown": "^0.20.1",
|
|
42
|
+
"typescript": "^5.9.3",
|
|
43
|
+
"vitest": "^4.0.18",
|
|
44
|
+
"vue": "^3.5.27"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"vue": "^3.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|