nuxt-upload-kit 0.1.18 → 0.1.21

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/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-upload-kit",
3
3
  "configKey": "uploadKit",
4
- "version": "0.1.18",
4
+ "version": "0.1.21",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -0,0 +1,41 @@
1
+ import type { Ref } from "vue";
2
+ import type { Emitter } from "mitt";
3
+ import type { UploadFile, UploadOptions, StoragePlugin, PluginLifecycleStage } from "./types.js";
4
+ export interface FileOperationsDeps<TUploadResult = any> {
5
+ /** Reactive ref containing the files array */
6
+ files: Ref<UploadFile<TUploadResult>[]>;
7
+ /** Event emitter for file events */
8
+ emitter: Emitter<any>;
9
+ /** Upload options configuration */
10
+ options: UploadOptions;
11
+ /** Map tracking created object URLs for cleanup */
12
+ createdObjectURLs: Map<string, string>;
13
+ /** Function to get the active storage plugin */
14
+ getStoragePlugin: () => StoragePlugin<any, any> | null;
15
+ /** Function to run plugin lifecycle stages */
16
+ runPluginStage: (stage: Exclude<PluginLifecycleStage, "upload">, file?: UploadFile) => Promise<UploadFile | undefined | null>;
17
+ /** Function to trigger upload */
18
+ upload: () => Promise<void>;
19
+ /** Function to update the hasEmittedFilesUploaded flag */
20
+ setHasEmittedFilesUploaded: (value: boolean) => void;
21
+ }
22
+ /**
23
+ * Creates file operation functions with injected dependencies.
24
+ *
25
+ * This factory function creates all file access and manipulation operations
26
+ * with proper closure over the composable's internal state.
27
+ */
28
+ export declare function createFileOperations<TUploadResult = any>(deps: FileOperationsDeps<TUploadResult>): {
29
+ getFile: (fileId: string) => UploadFile<TUploadResult>;
30
+ getFileData: (fileId: string) => Promise<Blob>;
31
+ getFileURL: (fileId: string) => Promise<string>;
32
+ getFileStream: (fileId: string) => Promise<ReadableStream<Uint8Array>>;
33
+ replaceFileData: (fileId: string, newData: Blob, newName?: string, shouldAutoUpload?: boolean) => Promise<UploadFile<TUploadResult>>;
34
+ reorderFile: (oldIndex: number, newIndex: number) => void;
35
+ removeFile: (fileId: string, removeOptions?: {
36
+ deleteFromStorage?: "always" | "never" | "local-only";
37
+ }) => Promise<void>;
38
+ removeFiles: (fileIds: string[]) => UploadFile<TUploadResult>[];
39
+ clearFiles: () => UploadFile<TUploadResult>[];
40
+ reset: () => void;
41
+ };
@@ -0,0 +1,167 @@
1
+ import { cleanupObjectURLs, createPluginContext } from "./utils.js";
2
+ export function createFileOperations(deps) {
3
+ const { files, emitter, options, createdObjectURLs, getStoragePlugin, runPluginStage, upload, setHasEmittedFilesUploaded } = deps;
4
+ const getFile = (fileId) => {
5
+ const file = files.value.find((f) => f.id === fileId);
6
+ if (!file) {
7
+ throw new Error(`File not found: ${fileId}`);
8
+ }
9
+ return file;
10
+ };
11
+ const getFileData = async (fileId) => {
12
+ const file = getFile(fileId);
13
+ if (file.size > 100 * 1024 * 1024) {
14
+ console.warn(
15
+ `getFileData: Loading large file (${(file.size / 1024 / 1024).toFixed(2)}MB) into memory. Consider using getFileURL() or getFileStream() for better performance.`
16
+ );
17
+ }
18
+ if (file.source === "local") {
19
+ return file.data;
20
+ }
21
+ const response = await fetch(file.remoteUrl);
22
+ if (!response.ok) {
23
+ throw new Error(`Failed to fetch file: ${response.statusText}`);
24
+ }
25
+ return await response.blob();
26
+ };
27
+ const getFileURL = async (fileId) => {
28
+ const file = getFile(fileId);
29
+ if (file.source === "local") {
30
+ const existingURL = createdObjectURLs.get(file.id);
31
+ if (existingURL) {
32
+ return existingURL;
33
+ }
34
+ const objectURL = URL.createObjectURL(file.data);
35
+ createdObjectURLs.set(file.id, objectURL);
36
+ return objectURL;
37
+ }
38
+ return file.remoteUrl;
39
+ };
40
+ const getFileStream = async (fileId) => {
41
+ const file = getFile(fileId);
42
+ if (file.source === "local") {
43
+ return file.data.stream();
44
+ }
45
+ const response = await fetch(file.remoteUrl);
46
+ if (!response.ok || !response.body) {
47
+ throw new Error(`Failed to fetch file stream: ${response.statusText}`);
48
+ }
49
+ return response.body;
50
+ };
51
+ const replaceFileData = async (fileId, newData, newName, shouldAutoUpload) => {
52
+ const file = getFile(fileId);
53
+ cleanupObjectURLs(createdObjectURLs, fileId);
54
+ const updatedFile = {
55
+ ...file,
56
+ source: "local",
57
+ data: newData,
58
+ name: newName || file.name,
59
+ size: newData.size,
60
+ status: "waiting",
61
+ progress: { percentage: 0 },
62
+ remoteUrl: void 0,
63
+ meta: {}
64
+ };
65
+ const preprocessedFile = await runPluginStage("preprocess", updatedFile);
66
+ const finalFile = preprocessedFile || updatedFile;
67
+ const index = files.value.findIndex((f) => f.id === fileId);
68
+ if (index === -1) {
69
+ throw new Error(`File not found: ${fileId}`);
70
+ }
71
+ files.value[index] = finalFile;
72
+ emitter.emit("file:replaced", finalFile);
73
+ emitter.emit("file:added", finalFile);
74
+ setHasEmittedFilesUploaded(false);
75
+ const shouldUpload = shouldAutoUpload ?? options.autoUpload;
76
+ if (shouldUpload) {
77
+ upload();
78
+ }
79
+ return finalFile;
80
+ };
81
+ const reorderFile = (oldIndex, newIndex) => {
82
+ if (oldIndex === newIndex) {
83
+ if (import.meta.dev) {
84
+ console.warn("Cannot reorder file to the same index");
85
+ }
86
+ return;
87
+ }
88
+ if (oldIndex < 0 || oldIndex >= files.value.length || newIndex < 0 || newIndex >= files.value.length) {
89
+ if (import.meta.dev) {
90
+ console.warn(`Cannot reorder file from ${oldIndex} to ${newIndex} since it is out of bounds`);
91
+ }
92
+ return;
93
+ }
94
+ const filesCopy = [...files.value];
95
+ const [movedFile] = filesCopy.splice(oldIndex, 1);
96
+ filesCopy.splice(newIndex, 0, movedFile);
97
+ files.value = filesCopy;
98
+ emitter.emit("files:reorder", { oldIndex, newIndex });
99
+ };
100
+ const removeFile = async (fileId, removeOptions) => {
101
+ const { deleteFromStorage = "always" } = removeOptions ?? {};
102
+ const file = files.value.find((f) => f.id === fileId);
103
+ if (!file) return;
104
+ let shouldDelete;
105
+ switch (deleteFromStorage) {
106
+ case "always":
107
+ shouldDelete = true;
108
+ break;
109
+ case "never":
110
+ shouldDelete = false;
111
+ break;
112
+ case "local-only":
113
+ shouldDelete = file.source === "local";
114
+ break;
115
+ }
116
+ if (shouldDelete && file.remoteUrl) {
117
+ const storagePlugin = getStoragePlugin();
118
+ if (storagePlugin?.hooks.remove) {
119
+ try {
120
+ const context = createPluginContext(storagePlugin.id, files.value, options, emitter);
121
+ await storagePlugin.hooks.remove(file, context);
122
+ } catch (error) {
123
+ console.error(`Storage plugin remove error:`, error);
124
+ }
125
+ }
126
+ }
127
+ cleanupObjectURLs(createdObjectURLs, file.id);
128
+ files.value = files.value.filter((f) => f.id !== fileId);
129
+ emitter.emit("file:removed", file);
130
+ };
131
+ const removeFiles = (fileIds) => {
132
+ const removedFiles = files.value.filter((f) => fileIds.includes(f.id));
133
+ removedFiles.forEach((file) => {
134
+ cleanupObjectURLs(createdObjectURLs, file.id);
135
+ });
136
+ files.value = files.value.filter((f) => !fileIds.includes(f.id));
137
+ removedFiles.forEach((file) => {
138
+ emitter.emit("file:removed", file);
139
+ });
140
+ return removedFiles;
141
+ };
142
+ const clearFiles = () => {
143
+ const allFiles = [...files.value];
144
+ cleanupObjectURLs(createdObjectURLs);
145
+ files.value = [];
146
+ allFiles.forEach((file) => {
147
+ emitter.emit("file:removed", file);
148
+ });
149
+ return allFiles;
150
+ };
151
+ const reset = () => {
152
+ cleanupObjectURLs(createdObjectURLs);
153
+ files.value = [];
154
+ };
155
+ return {
156
+ getFile,
157
+ getFileData,
158
+ getFileURL,
159
+ getFileStream,
160
+ replaceFileData,
161
+ reorderFile,
162
+ removeFile,
163
+ removeFiles,
164
+ clearFiles,
165
+ reset
166
+ };
167
+ }
@@ -1,4 +1,4 @@
1
- import type { UploaderEvents, UploadFile, UploadOptions, UploadStatus, UploadFn, GetRemoteFileFn, Plugin as UploaderPlugin } from "./types.js";
1
+ import type { UploaderEvents, UploadFile, UploadOptions, UploadStatus, Plugin as UploaderPlugin, InitialFileInput } from "./types.js";
2
2
  export declare const useUploadKit: <TUploadResult = any>(_options?: UploadOptions) => {
3
3
  files: Readonly<import("vue").Ref<readonly ({
4
4
  readonly source: "local";
@@ -54,6 +54,7 @@ export declare const useUploadKit: <TUploadResult = any>(_options?: UploadOption
54
54
  };
55
55
  readonly remoteUrl?: string | undefined;
56
56
  readonly id: string;
57
+ readonly storageKey?: string | undefined;
57
58
  readonly name: string;
58
59
  readonly size: number;
59
60
  readonly mimeType: string;
@@ -75,6 +76,7 @@ export declare const useUploadKit: <TUploadResult = any>(_options?: UploadOption
75
76
  readonly data: null;
76
77
  readonly remoteUrl: string;
77
78
  readonly id: string;
79
+ readonly storageKey?: string | undefined;
78
80
  readonly name: string;
79
81
  readonly size: number;
80
82
  readonly mimeType: string;
@@ -145,6 +147,7 @@ export declare const useUploadKit: <TUploadResult = any>(_options?: UploadOption
145
147
  };
146
148
  readonly remoteUrl?: string | undefined;
147
149
  readonly id: string;
150
+ readonly storageKey?: string | undefined;
148
151
  readonly name: string;
149
152
  readonly size: number;
150
153
  readonly mimeType: string;
@@ -166,6 +169,7 @@ export declare const useUploadKit: <TUploadResult = any>(_options?: UploadOption
166
169
  readonly data: null;
167
170
  readonly remoteUrl: string;
168
171
  readonly id: string;
172
+ readonly storageKey?: string | undefined;
169
173
  readonly name: string;
170
174
  readonly size: number;
171
175
  readonly mimeType: string;
@@ -187,285 +191,22 @@ export declare const useUploadKit: <TUploadResult = any>(_options?: UploadOption
187
191
  isReady: Readonly<import("vue").Ref<boolean, boolean>>;
188
192
  addFiles: (newFiles: File[]) => Promise<UploadFile[]>;
189
193
  addFile: (file: File) => Promise<UploadFile>;
190
- onGetRemoteFile: (fn: GetRemoteFileFn) => void;
191
- onUpload: (fn: UploadFn<TUploadResult>) => void;
192
194
  removeFile: (fileId: string, removeOptions?: {
193
195
  deleteFromStorage?: "always" | "never" | "local-only";
194
- }) => Promise<void>;
195
- removeFiles: (fileIds: string[]) => ({
196
- source: "local";
197
- data: {
198
- readonly lastModified: number;
199
- readonly name: string;
200
- readonly webkitRelativePath: string;
201
- readonly size: number;
202
- readonly type: string;
203
- arrayBuffer: {
204
- (): Promise<ArrayBuffer>;
205
- (): Promise<ArrayBuffer>;
206
- };
207
- bytes: {
208
- (): Promise<Uint8Array<ArrayBuffer>>;
209
- (): Promise<Uint8Array<ArrayBuffer>>;
210
- };
211
- slice: {
212
- (start?: number, end?: number, contentType?: string): Blob;
213
- (start?: number, end?: number, contentType?: string): Blob;
214
- };
215
- stream: {
216
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
217
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
218
- };
219
- text: {
220
- (): Promise<string>;
221
- (): Promise<string>;
222
- };
223
- } | {
224
- readonly size: number;
225
- readonly type: string;
226
- arrayBuffer: {
227
- (): Promise<ArrayBuffer>;
228
- (): Promise<ArrayBuffer>;
229
- };
230
- bytes: {
231
- (): Promise<Uint8Array<ArrayBuffer>>;
232
- (): Promise<Uint8Array<ArrayBuffer>>;
233
- };
234
- slice: {
235
- (start?: number, end?: number, contentType?: string): Blob;
236
- (start?: number, end?: number, contentType?: string): Blob;
237
- };
238
- stream: {
239
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
240
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
241
- };
242
- text: {
243
- (): Promise<string>;
244
- (): Promise<string>;
245
- };
246
- };
247
- remoteUrl?: string | undefined;
248
- id: string;
249
- name: string;
250
- size: number;
251
- mimeType: string;
252
- status: import("./types.js").FileStatus;
253
- preview?: string | undefined;
254
- progress: {
255
- percentage: number;
256
- };
257
- error?: {
258
- message: string;
259
- details?: unknown;
260
- } | undefined;
261
- uploadResult?: import("vue").UnwrapRef<TUploadResult> | undefined;
262
- meta: Record<string, unknown>;
263
- } | {
264
- source: Exclude<import("./types.js").FileSource, "local">;
265
- data: null;
266
- remoteUrl: string;
267
- id: string;
268
- name: string;
269
- size: number;
270
- mimeType: string;
271
- status: import("./types.js").FileStatus;
272
- preview?: string | undefined;
273
- progress: {
274
- percentage: number;
275
- };
276
- error?: {
277
- message: string;
278
- details?: unknown;
279
- } | undefined;
280
- uploadResult?: import("vue").UnwrapRef<TUploadResult> | undefined;
281
- meta: Record<string, unknown>;
282
- })[];
283
- clearFiles: () => ({
284
- source: "local";
285
- data: {
286
- readonly lastModified: number;
287
- readonly name: string;
288
- readonly webkitRelativePath: string;
289
- readonly size: number;
290
- readonly type: string;
291
- arrayBuffer: {
292
- (): Promise<ArrayBuffer>;
293
- (): Promise<ArrayBuffer>;
294
- };
295
- bytes: {
296
- (): Promise<Uint8Array<ArrayBuffer>>;
297
- (): Promise<Uint8Array<ArrayBuffer>>;
298
- };
299
- slice: {
300
- (start?: number, end?: number, contentType?: string): Blob;
301
- (start?: number, end?: number, contentType?: string): Blob;
302
- };
303
- stream: {
304
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
305
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
306
- };
307
- text: {
308
- (): Promise<string>;
309
- (): Promise<string>;
310
- };
311
- } | {
312
- readonly size: number;
313
- readonly type: string;
314
- arrayBuffer: {
315
- (): Promise<ArrayBuffer>;
316
- (): Promise<ArrayBuffer>;
317
- };
318
- bytes: {
319
- (): Promise<Uint8Array<ArrayBuffer>>;
320
- (): Promise<Uint8Array<ArrayBuffer>>;
321
- };
322
- slice: {
323
- (start?: number, end?: number, contentType?: string): Blob;
324
- (start?: number, end?: number, contentType?: string): Blob;
325
- };
326
- stream: {
327
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
328
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
329
- };
330
- text: {
331
- (): Promise<string>;
332
- (): Promise<string>;
333
- };
334
- };
335
- remoteUrl?: string | undefined;
336
- id: string;
337
- name: string;
338
- size: number;
339
- mimeType: string;
340
- status: import("./types.js").FileStatus;
341
- preview?: string | undefined;
342
- progress: {
343
- percentage: number;
344
- };
345
- error?: {
346
- message: string;
347
- details?: unknown;
348
- } | undefined;
349
- uploadResult?: import("vue").UnwrapRef<TUploadResult> | undefined;
350
- meta: Record<string, unknown>;
351
- } | {
352
- source: Exclude<import("./types.js").FileSource, "local">;
353
- data: null;
354
- remoteUrl: string;
355
- id: string;
356
- name: string;
357
- size: number;
358
- mimeType: string;
359
- status: import("./types.js").FileStatus;
360
- preview?: string | undefined;
361
- progress: {
362
- percentage: number;
363
- };
364
- error?: {
365
- message: string;
366
- details?: unknown;
367
- } | undefined;
368
- uploadResult?: import("vue").UnwrapRef<TUploadResult> | undefined;
369
- meta: Record<string, unknown>;
370
- })[];
196
+ } | undefined) => Promise<void>;
197
+ removeFiles: (fileIds: string[]) => UploadFile<any>[];
198
+ clearFiles: () => UploadFile<any>[];
371
199
  reorderFile: (oldIndex: number, newIndex: number) => void;
372
- getFile: (fileId: string) => {
373
- source: "local";
374
- data: {
375
- readonly lastModified: number;
376
- readonly name: string;
377
- readonly webkitRelativePath: string;
378
- readonly size: number;
379
- readonly type: string;
380
- arrayBuffer: {
381
- (): Promise<ArrayBuffer>;
382
- (): Promise<ArrayBuffer>;
383
- };
384
- bytes: {
385
- (): Promise<Uint8Array<ArrayBuffer>>;
386
- (): Promise<Uint8Array<ArrayBuffer>>;
387
- };
388
- slice: {
389
- (start?: number, end?: number, contentType?: string): Blob;
390
- (start?: number, end?: number, contentType?: string): Blob;
391
- };
392
- stream: {
393
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
394
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
395
- };
396
- text: {
397
- (): Promise<string>;
398
- (): Promise<string>;
399
- };
400
- } | {
401
- readonly size: number;
402
- readonly type: string;
403
- arrayBuffer: {
404
- (): Promise<ArrayBuffer>;
405
- (): Promise<ArrayBuffer>;
406
- };
407
- bytes: {
408
- (): Promise<Uint8Array<ArrayBuffer>>;
409
- (): Promise<Uint8Array<ArrayBuffer>>;
410
- };
411
- slice: {
412
- (start?: number, end?: number, contentType?: string): Blob;
413
- (start?: number, end?: number, contentType?: string): Blob;
414
- };
415
- stream: {
416
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
417
- (): ReadableStream<Uint8Array<ArrayBuffer>>;
418
- };
419
- text: {
420
- (): Promise<string>;
421
- (): Promise<string>;
422
- };
423
- };
424
- remoteUrl?: string | undefined;
425
- id: string;
426
- name: string;
427
- size: number;
428
- mimeType: string;
429
- status: import("./types.js").FileStatus;
430
- preview?: string | undefined;
431
- progress: {
432
- percentage: number;
433
- };
434
- error?: {
435
- message: string;
436
- details?: unknown;
437
- } | undefined;
438
- uploadResult?: import("vue").UnwrapRef<TUploadResult> | undefined;
439
- meta: Record<string, unknown>;
440
- } | {
441
- source: Exclude<import("./types.js").FileSource, "local">;
442
- data: null;
443
- remoteUrl: string;
444
- id: string;
445
- name: string;
446
- size: number;
447
- mimeType: string;
448
- status: import("./types.js").FileStatus;
449
- preview?: string | undefined;
450
- progress: {
451
- percentage: number;
452
- };
453
- error?: {
454
- message: string;
455
- details?: unknown;
456
- } | undefined;
457
- uploadResult?: import("vue").UnwrapRef<TUploadResult> | undefined;
458
- meta: Record<string, unknown>;
459
- };
200
+ getFile: (fileId: string) => UploadFile<any>;
460
201
  upload: () => Promise<void>;
461
202
  reset: () => void;
462
203
  status: import("vue").Ref<UploadStatus, UploadStatus>;
463
204
  getFileData: (fileId: string) => Promise<Blob>;
464
205
  getFileURL: (fileId: string) => Promise<string>;
465
206
  getFileStream: (fileId: string) => Promise<ReadableStream<Uint8Array>>;
466
- replaceFileData: (fileId: string, newData: Blob, newName?: string, shouldAutoUpload?: boolean) => Promise<UploadFile>;
207
+ replaceFileData: (fileId: string, newData: Blob, newName?: string, shouldAutoUpload?: boolean) => Promise<UploadFile<any>>;
467
208
  updateFile: (fileId: string, updatedFile: Partial<UploadFile<TUploadResult>>) => void;
468
- initializeExistingFiles: (initialFiles: Array<Partial<UploadFile>>) => Promise<void>;
209
+ initializeExistingFiles: (initialFiles: InitialFileInput[]) => Promise<void>;
469
210
  addPlugin: (plugin: UploaderPlugin<any, any>) => void;
470
211
  on: {
471
212
  <K extends keyof UploaderEvents<TUploadResult>>(type: K, handler: (event: UploaderEvents<TUploadResult>[K]) => void): void;