@muhgholy/next-drive 4.23.14 → 4.23.19

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 CHANGED
@@ -193,6 +193,87 @@ function MyForm() {
193
193
 
194
194
  ---
195
195
 
196
+ ## Headless Uploads
197
+
198
+ Need to build your own upload UI? Use the framework-agnostic `uploadFile` / `uploadFiles` functions. They run the same chunked, retrying upload engine that powers the built-in components — without any React or UI — so you control the markup, loading states, and cancel buttons.
199
+
200
+ Capabilities:
201
+
202
+ - 📈 **Byte-level progress** – smooth percentage, even for small single-chunk files
203
+ - 🪵 **Logs** – the same per-step log stream shown by `DriveFileChooser`
204
+ - ✋ **Cancellation** – every call returns a `cancel()` function
205
+ - 🔓 **Anonymous** – just omit `accountId` (works with `unauthenticated` server config)
206
+ - 📦 **Consistent result** – returns a normalized `TDriveFile` on completion
207
+
208
+ ### Single File
209
+
210
+ ```tsx
211
+ import { uploadFile } from "@muhgholy/next-drive/client";
212
+
213
+ const { id, promise, cancel } = uploadFile({
214
+ apiEndpoint: "/api/drive",
215
+ file, // a File from an <input type="file" />
216
+ folderId: null, // optional: target folder id (defaults to root)
217
+ accountId: null, // optional: omit/null = anonymous upload
218
+ withCredentials: false, // optional: send cookies cross-origin
219
+ onProgress: ({ percent, uploadedBytes, totalBytes }) => {
220
+ setProgress(percent); // 0–100
221
+ },
222
+ onLog: ({ type, message, timestamp }) => {
223
+ console.log(`[${type}] ${message}`);
224
+ },
225
+ });
226
+
227
+ // Call cancel() any time to abort (e.g. a Cancel button)
228
+ // <button onClick={cancel}>Cancel</button>
229
+
230
+ const result = await promise;
231
+ if (result.status === "complete") {
232
+ result.file; // TDriveFile — same shape as DriveFileChooser's onChange
233
+ result.driveId; // server drive item id
234
+ result.item; // raw item (signed URL, full metadata, etc.)
235
+ } else if (result.status === "cancelled") {
236
+ // upload was aborted
237
+ } else {
238
+ result.error; // failure message
239
+ }
240
+ ```
241
+
242
+ ### Multiple Files (with concurrency)
243
+
244
+ ```tsx
245
+ import { uploadFiles } from "@muhgholy/next-drive/client";
246
+
247
+ const { uploads, cancelAll, promise } = uploadFiles(files, {
248
+ apiEndpoint: "/api/drive",
249
+ concurrency: 2, // optional: max parallel uploads (default 2)
250
+ onProgress: ({ id, percent }) => updateRow(id, percent),
251
+ onFileComplete: (result) => {
252
+ if (result.status === "complete") addFile(result.file);
253
+ },
254
+ });
255
+
256
+ // uploads: one handle per file — each has its own { id, promise, cancel }
257
+ // cancelAll(): abort everything
258
+
259
+ const results = await promise; // resolves once all files settle
260
+ ```
261
+
262
+ ### Result Type
263
+
264
+ The result is a discriminated union (no optional fields — narrow by `status`):
265
+
266
+ ```typescript
267
+ type TDriveUploadResult =
268
+ | { id: string; status: "complete"; driveId: string; file: TDriveFile; item: unknown }
269
+ | { id: string; status: "cancelled"; driveId: string | null }
270
+ | { id: string; status: "error"; driveId: string | null; error: string };
271
+ ```
272
+
273
+ > Prefer React state management? The `useUpload` hook wraps the same engine and exposes a reactive `uploads` array — see the source for `DriveUpload` for a reference implementation.
274
+
275
+ ---
276
+
196
277
  ## Express Integration
197
278
 
198
279
  Use the Express adapter instead of Next.js API routes:
@@ -1 +1 @@
1
- {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../../src/client/components/drive/upload.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAwC,MAAM,OAAO,CAAC;AA8G7D,eAAO,MAAM,WAAW,GAAI,OAAO,QAAQ,CAAC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC,sBAoQD,CAAC"}
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../../src/client/components/drive/upload.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAwC,MAAM,OAAO,CAAC;AA8G7D,eAAO,MAAM,WAAW,GAAI,OAAO,QAAQ,CAAC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,sBAoQD,CAAC"}
@@ -2,7 +2,7 @@ import type { TDriveUploadState } from '../../types/client';
2
2
  export declare const useUpload: (apiEndpoint: string, activeAccountId: string | null, withCredentials?: boolean, onUploadComplete?: (item: unknown) => void) => {
3
3
  uploads: TDriveUploadState[];
4
4
  uploadFiles: (files: File[], folderId: string | null) => Promise<void>;
5
- cancelUpload: (id: string) => Promise<void>;
5
+ cancelUpload: (id: string) => void;
6
6
  cancelAllUploads: () => Promise<void>;
7
7
  };
8
8
  //# sourceMappingURL=use-upload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-upload.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/use-upload.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAYxD,eAAO,MAAM,SAAS,GAAI,aAAa,MAAM,EAAE,iBAAiB,MAAM,GAAG,IAAI,EAAE,kBAAiB,OAAe,EAAE,mBAAmB,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI;;yBAkKzG,IAAI,EAAE,YAAY,MAAM,GAAG,IAAI;uBAuB9D,MAAM;;CA+CxB,CAAC"}
1
+ {"version":3,"file":"use-upload.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/use-upload.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAMxD,eAAO,MAAM,SAAS,GAAI,aAAa,MAAM,EAAE,iBAAiB,MAAM,GAAG,IAAI,EAAE,kBAAiB,OAAe,EAAE,mBAAmB,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI;;yBA0DzG,IAAI,EAAE,YAAY,MAAM,GAAG,IAAI;uBAsBpE,MAAM;;CA6BlB,CAAC"}