@transloadit/convex 0.0.6 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +26 -17
  2. package/dist/client/index.d.ts +39 -92
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +34 -104
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/component/_generated/component.d.ts +18 -0
  7. package/dist/component/_generated/component.d.ts.map +1 -1
  8. package/dist/component/apiUtils.d.ts +1 -32
  9. package/dist/component/apiUtils.d.ts.map +1 -1
  10. package/dist/component/apiUtils.js.map +1 -1
  11. package/dist/component/lib.d.ts +33 -90
  12. package/dist/component/lib.d.ts.map +1 -1
  13. package/dist/component/lib.js +48 -157
  14. package/dist/component/lib.js.map +1 -1
  15. package/dist/component/schema.d.ts +4 -4
  16. package/dist/component/schema.d.ts.map +1 -1
  17. package/dist/component/schema.js +3 -31
  18. package/dist/component/schema.js.map +1 -1
  19. package/dist/shared/schemas.d.ts +419 -0
  20. package/dist/shared/schemas.d.ts.map +1 -0
  21. package/dist/shared/schemas.js +194 -0
  22. package/dist/shared/schemas.js.map +1 -0
  23. package/dist/test/index.d.ts +4 -4
  24. package/package.json +4 -16
  25. package/src/client/index.ts +68 -123
  26. package/src/component/_generated/component.ts +17 -0
  27. package/src/component/apiUtils.ts +7 -38
  28. package/src/component/lib.test.ts +19 -0
  29. package/src/component/lib.ts +80 -180
  30. package/src/component/schema.ts +3 -31
  31. package/src/shared/schemas.ts +279 -0
  32. package/dist/react/index.d.ts +0 -281
  33. package/dist/react/index.d.ts.map +0 -1
  34. package/dist/react/index.js +0 -784
  35. package/dist/react/index.js.map +0 -1
  36. package/dist/shared/tusUpload.d.ts +0 -13
  37. package/dist/shared/tusUpload.d.ts.map +0 -1
  38. package/dist/shared/tusUpload.js +0 -32
  39. package/dist/shared/tusUpload.js.map +0 -1
  40. package/src/react/index.test.tsx +0 -340
  41. package/src/react/index.tsx +0 -1245
  42. package/src/react/uploadWithTus.test.tsx +0 -192
  43. package/src/shared/tusUpload.ts +0 -59
@@ -1,784 +0,0 @@
1
- import { isAssemblyTerminal, } from "@transloadit/zod/v3/assemblyStatus";
2
- import { useAction, useQuery } from "convex/react";
3
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
- import { Upload } from "tus-js-client";
5
- import { getAssemblyStage, parseAssemblyStatus, } from "../shared/assemblyUrls.js";
6
- import { transloaditError } from "../shared/errors.js";
7
- import { pollAssembly } from "../shared/pollAssembly.js";
8
- import { buildTusUploadConfig } from "../shared/tusUpload.js";
9
- export async function uploadWithAssembly(createAssembly, uppy, options) {
10
- const files = uppy.getFiles();
11
- if (files.length === 0) {
12
- throw transloaditError("upload", "No files provided for upload");
13
- }
14
- const args = {
15
- ...(options.createAssemblyArgs ?? {}),
16
- fileCount: options.fileCount ?? files.length,
17
- };
18
- const assembly = await createAssembly(args);
19
- options.onAssemblyCreated?.(assembly);
20
- const tusPlugin = uppy.getPlugin("Tus");
21
- if (!tusPlugin) {
22
- throw transloaditError("upload", 'Uppy Tus plugin is required. Call uppy.use(Tus, { endpoint: "" }) before uploadWithAssembly.');
23
- }
24
- let tusEndpoint = null;
25
- const addRequestId = options.addRequestId ?? true;
26
- for (const file of files) {
27
- if (!file.data ||
28
- typeof Blob === "undefined" ||
29
- !(file.data instanceof Blob)) {
30
- throw transloaditError("upload", "Uppy file is missing binary data for upload");
31
- }
32
- const uploadFile = file.data instanceof File
33
- ? file.data
34
- : new File([file.data], file.name ?? "file", {
35
- type: file.data.type || file.type,
36
- });
37
- const { endpoint, metadata } = buildTusUploadConfig(assembly.data, uploadFile, {
38
- fieldName: options.fieldName,
39
- metadata: options.metadata,
40
- });
41
- if (!tusEndpoint) {
42
- tusEndpoint = endpoint;
43
- }
44
- uppy.setFileMeta(file.id, metadata);
45
- uppy.setFileState(file.id, {
46
- tus: {
47
- ...(file.tus ?? {}),
48
- endpoint,
49
- addRequestId,
50
- },
51
- });
52
- }
53
- if (tusPlugin && "setOptions" in tusPlugin && tusEndpoint) {
54
- tusPlugin.setOptions?.({ endpoint: tusEndpoint, addRequestId });
55
- }
56
- const uploadResult = await uppy.upload();
57
- if (!uploadResult) {
58
- throw transloaditError("upload", "Uppy upload did not return a result");
59
- }
60
- return { assembly, uploadResult };
61
- }
62
- /**
63
- * Low-level tus upload helper. Prefer `useTransloaditUpload` for new code.
64
- */
65
- /**
66
- * Low-level tus upload helper. Prefer `useTransloaditUpload` for new code.
67
- */
68
- export async function uploadWithTransloaditTus(createAssembly, file, options, events = {}) {
69
- let currentState = {
70
- isUploading: true,
71
- progress: 0,
72
- error: null,
73
- };
74
- const emitState = (next) => {
75
- currentState = next;
76
- events.onStateChange?.(next);
77
- };
78
- emitState(currentState);
79
- try {
80
- const assembly = await createAssembly({
81
- templateId: options.templateId,
82
- steps: options.steps,
83
- fields: options.fields,
84
- notifyUrl: options.notifyUrl,
85
- numExpectedUploadFiles: options.numExpectedUploadFiles ?? 1,
86
- expires: options.expires,
87
- additionalParams: options.additionalParams,
88
- userId: options.userId,
89
- });
90
- const data = assembly.data;
91
- options.onAssemblyCreated?.(assembly);
92
- const { endpoint, metadata } = buildTusUploadConfig(data, file, {
93
- fieldName: options.fieldName,
94
- metadata: options.metadata,
95
- });
96
- const getStatus = (error) => error.originalResponse?.getStatus &&
97
- typeof error.originalResponse.getStatus === "function"
98
- ? error.originalResponse.getStatus()
99
- : 0;
100
- const retryDelays = options.retryDelays
101
- ? [...options.retryDelays]
102
- : [1000, 5000, 15000, 30000];
103
- const rateLimitRetryDelays = options.rateLimitRetryDelays
104
- ? [...options.rateLimitRetryDelays]
105
- : [20_000, 40_000, 80_000];
106
- const shouldRetry = (error) => {
107
- const status = getStatus(error);
108
- if (!status)
109
- return true;
110
- if (status === 409 || status === 423)
111
- return true;
112
- return status < 400 || status >= 500;
113
- };
114
- let uploadUrl = null;
115
- let rateLimitAttempt = 0;
116
- const runUpload = () => new Promise((resolve, reject) => {
117
- let uploader;
118
- const uploadOptions = {
119
- endpoint,
120
- metadata,
121
- retryDelays,
122
- uploadDataDuringCreation: options.uploadDataDuringCreation ?? false,
123
- onUploadUrlAvailable: () => {
124
- uploadUrl = uploader.url;
125
- },
126
- onShouldRetry: (error, retryAttempt) => options.onShouldRetry?.(error, retryAttempt) ?? shouldRetry(error),
127
- onProgress: (bytesUploaded, bytesTotal) => {
128
- const progress = Math.round((bytesUploaded / bytesTotal) * 100);
129
- emitState({ isUploading: true, progress, error: null });
130
- options.onProgress?.(progress);
131
- },
132
- onError: (error) => {
133
- reject(error);
134
- },
135
- onSuccess: () => {
136
- resolve();
137
- },
138
- };
139
- if (options.chunkSize !== undefined) {
140
- uploadOptions.chunkSize = options.chunkSize;
141
- }
142
- if (uploadUrl) {
143
- uploadOptions.uploadUrl = uploadUrl;
144
- }
145
- if (options.overridePatchMethod !== undefined) {
146
- uploadOptions.overridePatchMethod = options.overridePatchMethod;
147
- }
148
- if (options.storeFingerprintForResuming !== undefined) {
149
- uploadOptions.storeFingerprintForResuming =
150
- options.storeFingerprintForResuming;
151
- }
152
- if (options.removeFingerprintOnSuccess !== undefined) {
153
- uploadOptions.removeFingerprintOnSuccess =
154
- options.removeFingerprintOnSuccess;
155
- }
156
- uploader = new Upload(file, uploadOptions);
157
- uploader.start();
158
- });
159
- while (true) {
160
- try {
161
- await runUpload();
162
- break;
163
- }
164
- catch (error) {
165
- const status = getStatus(error);
166
- if (status === 429 && rateLimitAttempt < rateLimitRetryDelays.length) {
167
- const delay = rateLimitRetryDelays[rateLimitAttempt] ?? 0;
168
- rateLimitAttempt += 1;
169
- await new Promise((resolve) => setTimeout(resolve, delay));
170
- continue;
171
- }
172
- throw error;
173
- }
174
- }
175
- emitState({ isUploading: false, progress: 100, error: null });
176
- return assembly;
177
- }
178
- catch (error) {
179
- const err = error instanceof Error
180
- ? error
181
- : transloaditError("upload", "Upload failed");
182
- emitState({ isUploading: false, progress: 0, error: err });
183
- throw err;
184
- }
185
- }
186
- /**
187
- * @deprecated Prefer `useTransloaditUpload` (single + multi-file) for new code.
188
- */
189
- export function useTransloaditTusUpload(createAssembly) {
190
- const create = useAction(createAssembly);
191
- const [state, setState] = useState({
192
- isUploading: false,
193
- progress: 0,
194
- error: null,
195
- });
196
- const upload = useCallback(async (file, options) => uploadWithTransloaditTus(create, file, options, {
197
- onStateChange: setState,
198
- }), [create]);
199
- const reset = useCallback(() => {
200
- setState({ isUploading: false, progress: 0, error: null });
201
- }, []);
202
- return useMemo(() => ({
203
- upload,
204
- reset,
205
- isUploading: state.isUploading,
206
- progress: state.progress,
207
- error: state.error,
208
- }), [state.error, state.isUploading, state.progress, upload, reset]);
209
- }
210
- /**
211
- * Low-level multi-file tus uploader. Prefer `useTransloaditUpload` for new code.
212
- */
213
- export function uploadFilesWithTransloaditTus(createAssembly, files, options) {
214
- const concurrency = Math.max(1, options.concurrency ?? 3);
215
- const state = {
216
- isUploading: true,
217
- progress: 0,
218
- error: null,
219
- };
220
- const results = files.map((file) => ({
221
- file,
222
- status: "canceled",
223
- }));
224
- const inFlight = new Set();
225
- const abortController = new AbortController();
226
- let cancelled = false;
227
- const emitState = (next) => {
228
- state.isUploading = next.isUploading;
229
- state.progress = next.progress;
230
- state.error = next.error;
231
- options.onStateChange?.(next);
232
- };
233
- const cancel = () => {
234
- if (cancelled)
235
- return;
236
- cancelled = true;
237
- abortController.abort();
238
- for (const uploader of inFlight) {
239
- try {
240
- uploader.abort(true);
241
- }
242
- catch {
243
- // ignore abort errors
244
- }
245
- }
246
- };
247
- if (options.signal) {
248
- if (options.signal.aborted) {
249
- cancel();
250
- }
251
- else {
252
- options.signal.addEventListener("abort", cancel, { once: true });
253
- }
254
- }
255
- const promise = (async () => {
256
- if (files.length === 0) {
257
- throw transloaditError("upload", "No files provided for upload");
258
- }
259
- emitState({ ...state });
260
- const assembly = await createAssembly({
261
- templateId: options.templateId,
262
- steps: options.steps,
263
- fields: options.fields,
264
- notifyUrl: options.notifyUrl,
265
- numExpectedUploadFiles: options.numExpectedUploadFiles ?? files.length,
266
- expires: options.expires,
267
- additionalParams: options.additionalParams,
268
- userId: options.userId,
269
- });
270
- options.onAssemblyCreated?.(assembly);
271
- const getStatus = (error) => error.originalResponse?.getStatus &&
272
- typeof error.originalResponse.getStatus === "function"
273
- ? error.originalResponse.getStatus()
274
- : 0;
275
- const retryDelays = options.retryDelays
276
- ? [...options.retryDelays]
277
- : [1000, 5000, 15000, 30000];
278
- const rateLimitRetryDelays = options.rateLimitRetryDelays
279
- ? [...options.rateLimitRetryDelays]
280
- : [20_000, 40_000, 80_000];
281
- const shouldRetry = (error) => {
282
- const status = getStatus(error);
283
- if (!status)
284
- return true;
285
- if (status === 409 || status === 423)
286
- return true;
287
- return status < 400 || status >= 500;
288
- };
289
- const perFileBytes = new Map();
290
- files.forEach((file, index) => {
291
- perFileBytes.set(index, { uploaded: 0, total: file.size });
292
- });
293
- const updateOverallProgress = () => {
294
- let totalUploaded = 0;
295
- let totalBytes = 0;
296
- for (const { uploaded, total } of perFileBytes.values()) {
297
- totalUploaded += uploaded;
298
- totalBytes += total;
299
- }
300
- const overall = totalBytes > 0 ? Math.round((totalUploaded / totalBytes) * 100) : 0;
301
- emitState({ isUploading: true, progress: overall, error: null });
302
- options.onOverallProgress?.(overall);
303
- };
304
- const resolveMetadata = (file) => typeof options.metadata === "function"
305
- ? options.metadata(file)
306
- : options.metadata;
307
- const resolveFieldName = (file) => typeof options.fieldName === "function"
308
- ? options.fieldName(file)
309
- : options.fieldName;
310
- const uploadFile = async (file, index) => {
311
- const { endpoint, metadata } = buildTusUploadConfig(assembly.data, file, {
312
- fieldName: resolveFieldName(file),
313
- metadata: resolveMetadata(file),
314
- });
315
- let uploadUrl = null;
316
- let rateLimitAttempt = 0;
317
- let uploader = null;
318
- const runUpload = () => new Promise((resolve, reject) => {
319
- if (cancelled) {
320
- reject(transloaditError("upload", "Upload canceled"));
321
- return;
322
- }
323
- const onAbort = () => {
324
- reject(transloaditError("upload", "Upload canceled"));
325
- };
326
- abortController.signal.addEventListener("abort", onAbort, {
327
- once: true,
328
- });
329
- let currentUploader;
330
- const uploadOptions = {
331
- endpoint,
332
- metadata,
333
- retryDelays,
334
- uploadDataDuringCreation: options.uploadDataDuringCreation ?? false,
335
- onUploadUrlAvailable: () => {
336
- uploadUrl = currentUploader.url;
337
- },
338
- onShouldRetry: (error, retryAttempt) => options.onShouldRetry?.(error, retryAttempt) ??
339
- shouldRetry(error),
340
- onProgress: (bytesUploaded, bytesTotal) => {
341
- perFileBytes.set(index, {
342
- uploaded: bytesUploaded,
343
- total: bytesTotal,
344
- });
345
- const progress = Math.round((bytesUploaded / bytesTotal) * 100);
346
- options.onFileProgress?.(file, progress);
347
- updateOverallProgress();
348
- },
349
- onError: (error) => {
350
- abortController.signal.removeEventListener("abort", onAbort);
351
- reject(error);
352
- },
353
- onSuccess: () => {
354
- abortController.signal.removeEventListener("abort", onAbort);
355
- resolve();
356
- },
357
- };
358
- if (options.chunkSize !== undefined) {
359
- uploadOptions.chunkSize = options.chunkSize;
360
- }
361
- if (uploadUrl) {
362
- uploadOptions.uploadUrl = uploadUrl;
363
- }
364
- if (options.overridePatchMethod !== undefined) {
365
- uploadOptions.overridePatchMethod = options.overridePatchMethod;
366
- }
367
- if (options.storeFingerprintForResuming !== undefined) {
368
- uploadOptions.storeFingerprintForResuming =
369
- options.storeFingerprintForResuming;
370
- }
371
- if (options.removeFingerprintOnSuccess !== undefined) {
372
- uploadOptions.removeFingerprintOnSuccess =
373
- options.removeFingerprintOnSuccess;
374
- }
375
- currentUploader = new Upload(file, uploadOptions);
376
- uploader = currentUploader;
377
- inFlight.add(currentUploader);
378
- currentUploader.start();
379
- }).finally(() => {
380
- if (uploader) {
381
- inFlight.delete(uploader);
382
- }
383
- });
384
- while (true) {
385
- try {
386
- await runUpload();
387
- break;
388
- }
389
- catch (error) {
390
- if (cancelled) {
391
- throw error;
392
- }
393
- const status = getStatus(error);
394
- if (status === 429 &&
395
- rateLimitAttempt < rateLimitRetryDelays.length) {
396
- const delay = rateLimitRetryDelays[rateLimitAttempt] ?? 0;
397
- rateLimitAttempt += 1;
398
- await new Promise((resolve) => setTimeout(resolve, delay));
399
- continue;
400
- }
401
- throw error;
402
- }
403
- }
404
- };
405
- let nextIndex = 0;
406
- const errors = [];
407
- const worker = async () => {
408
- while (true) {
409
- if (cancelled)
410
- return;
411
- const index = nextIndex;
412
- nextIndex += 1;
413
- if (index >= files.length)
414
- return;
415
- const file = files[index];
416
- try {
417
- await uploadFile(file, index);
418
- results[index] = { file, status: "success" };
419
- options.onFileComplete?.(file);
420
- }
421
- catch (error) {
422
- if (cancelled) {
423
- results[index] = { file, status: "canceled" };
424
- return;
425
- }
426
- const err = error instanceof Error
427
- ? error
428
- : transloaditError("upload", "Upload failed");
429
- results[index] = { file, status: "error", error: err };
430
- errors.push(err);
431
- options.onFileError?.(file, err);
432
- if (options.failFast ?? false) {
433
- cancel();
434
- return;
435
- }
436
- }
437
- }
438
- };
439
- await Promise.all(Array.from({ length: Math.min(concurrency, files.length) }, worker));
440
- if (cancelled) {
441
- const error = transloaditError("upload", "Upload canceled");
442
- error.results = {
443
- assemblyId: assembly.assemblyId,
444
- data: assembly.data,
445
- files: results,
446
- };
447
- throw error;
448
- }
449
- const hasErrors = results.some((result) => result.status === "error");
450
- const resultPayload = {
451
- assemblyId: assembly.assemblyId,
452
- data: assembly.data,
453
- files: results,
454
- };
455
- if (hasErrors) {
456
- const error = transloaditError("upload", `Failed to upload ${errors.length} file${errors.length === 1 ? "" : "s"}`);
457
- error.results =
458
- resultPayload;
459
- throw error;
460
- }
461
- emitState({ isUploading: false, progress: 100, error: null });
462
- return resultPayload;
463
- })();
464
- return { promise, cancel };
465
- }
466
- export function useTransloaditUpload(options) {
467
- const create = useAction(options.createAssembly);
468
- const refresh = useAction(options.refreshAssembly);
469
- const [state, setState] = useState({
470
- isUploading: false,
471
- progress: 0,
472
- error: null,
473
- });
474
- const [assemblyId, setAssemblyId] = useState(null);
475
- const [assemblyData, setAssemblyData] = useState(null);
476
- const cancelRef = useRef(null);
477
- const upload = useCallback(async (files, uploadOptions) => {
478
- const resolved = files instanceof FileList
479
- ? Array.from(files)
480
- : Array.isArray(files)
481
- ? files
482
- : [files];
483
- const controller = uploadFilesWithTransloaditTus(create, resolved, {
484
- ...uploadOptions,
485
- onStateChange: setState,
486
- onAssemblyCreated: (assembly) => {
487
- setAssemblyId(assembly.assemblyId);
488
- setAssemblyData(assembly.data);
489
- uploadOptions.onAssemblyCreated?.(assembly);
490
- },
491
- });
492
- cancelRef.current = controller.cancel;
493
- try {
494
- const result = await controller.promise;
495
- setAssemblyId(result.assemblyId);
496
- setAssemblyData(result.data);
497
- return result;
498
- }
499
- catch (error) {
500
- const resolvedError = error instanceof Error
501
- ? error
502
- : transloaditError("upload", "Upload failed");
503
- setState({ isUploading: false, progress: 0, error: resolvedError });
504
- throw error;
505
- }
506
- finally {
507
- cancelRef.current = null;
508
- }
509
- }, [create]);
510
- const cancel = useCallback(() => {
511
- cancelRef.current?.();
512
- }, []);
513
- const reset = useCallback(() => {
514
- cancelRef.current?.();
515
- cancelRef.current = null;
516
- setAssemblyId(null);
517
- setAssemblyData(null);
518
- setState({ isUploading: false, progress: 0, error: null });
519
- }, []);
520
- const assembly = useQuery(options.getStatus, assemblyId ? { assemblyId } : "skip");
521
- const parsedStatus = useMemo(() => {
522
- const candidate = assembly && typeof assembly === "object"
523
- ? (assembly.raw ?? assembly)
524
- : assembly;
525
- return parseAssemblyStatus(candidate);
526
- }, [assembly]);
527
- const results = useQuery(options.listResults, assemblyId ? { assemblyId } : "skip");
528
- useAssemblyPoller({
529
- assemblyId,
530
- status: parsedStatus,
531
- refresh: async () => {
532
- if (!assemblyId)
533
- return;
534
- await refresh({ assemblyId });
535
- },
536
- intervalMs: options.pollIntervalMs ?? 5000,
537
- shouldContinue: options.shouldContinue,
538
- onError: options.onError,
539
- });
540
- return {
541
- upload,
542
- cancel,
543
- reset,
544
- isUploading: state.isUploading,
545
- progress: state.progress,
546
- error: state.error,
547
- assemblyId,
548
- assemblyData,
549
- assembly,
550
- status: parsedStatus,
551
- results,
552
- };
553
- }
554
- export function useTransloaditUppy(options) {
555
- const create = useAction(options.createAssembly);
556
- const refresh = useAction(options.refreshAssembly);
557
- const [isUploading, setIsUploading] = useState(false);
558
- const [error, setError] = useState(null);
559
- const [assemblyId, setAssemblyId] = useState(null);
560
- const [assemblyData, setAssemblyData] = useState(null);
561
- const [uploadResult, setUploadResult] = useState(null);
562
- const assembly = useQuery(options.getStatus, assemblyId ? { assemblyId } : "skip");
563
- const results = useQuery(options.listResults, assemblyId ? { assemblyId } : "skip");
564
- const parsedStatus = useMemo(() => {
565
- const candidate = assembly && typeof assembly === "object"
566
- ? (assembly.raw ?? assembly)
567
- : assembly;
568
- return parseAssemblyStatus(candidate);
569
- }, [assembly]);
570
- useAssemblyPoller({
571
- assemblyId,
572
- status: parsedStatus,
573
- refresh: async () => {
574
- if (!assemblyId)
575
- return;
576
- await refresh({ assemblyId });
577
- },
578
- intervalMs: options.pollIntervalMs ?? 5000,
579
- shouldContinue: options.shouldContinue,
580
- onError: options.onError,
581
- });
582
- const startUpload = useCallback(async (overrides) => {
583
- setError(null);
584
- setIsUploading(true);
585
- try {
586
- const files = options.uppy.getFiles();
587
- if (files.length === 0) {
588
- throw transloaditError("upload", "No files provided for upload");
589
- }
590
- const createAssemblyArgs = {
591
- ...(options.createAssemblyArgs ?? {}),
592
- ...(overrides?.createAssemblyArgs ?? {}),
593
- };
594
- const { assembly, uploadResult: result } = await uploadWithAssembly(create, options.uppy, {
595
- fileCount: overrides?.fileCount ?? options.fileCount ?? files.length,
596
- fieldName: overrides?.fieldName ?? options.fieldName,
597
- metadata: overrides?.metadata ?? options.metadata,
598
- addRequestId: overrides?.addRequestId ?? options.addRequestId,
599
- createAssemblyArgs,
600
- onAssemblyCreated: (created) => {
601
- const typed = created;
602
- setAssemblyId(typed.assemblyId);
603
- setAssemblyData(typed.data);
604
- options.onAssemblyCreated?.(typed);
605
- overrides?.onAssemblyCreated?.(created);
606
- },
607
- });
608
- setAssemblyId(assembly.assemblyId);
609
- setAssemblyData(assembly.data);
610
- setUploadResult(result);
611
- options.onUploadResult?.(result);
612
- setIsUploading(false);
613
- return { assembly, uploadResult: result };
614
- }
615
- catch (err) {
616
- const resolved = err instanceof Error
617
- ? err
618
- : transloaditError("upload", "Upload failed");
619
- setError(resolved);
620
- setIsUploading(false);
621
- throw resolved;
622
- }
623
- }, [
624
- create,
625
- options.addRequestId,
626
- options.createAssemblyArgs,
627
- options.fieldName,
628
- options.fileCount,
629
- options.metadata,
630
- options.onAssemblyCreated,
631
- options.onUploadResult,
632
- options.uppy,
633
- ]);
634
- const reset = useCallback(() => {
635
- setIsUploading(false);
636
- setError(null);
637
- setAssemblyId(null);
638
- setAssemblyData(null);
639
- setUploadResult(null);
640
- }, []);
641
- const stage = useMemo(() => {
642
- if (error)
643
- return "error";
644
- if (isUploading)
645
- return "uploading";
646
- return parsedStatus ? getAssemblyStage(parsedStatus) : null;
647
- }, [error, isUploading, parsedStatus]);
648
- return {
649
- startUpload,
650
- reset,
651
- isUploading,
652
- error,
653
- assemblyId,
654
- assemblyData,
655
- assembly,
656
- status: parsedStatus,
657
- results,
658
- stage,
659
- uploadResult,
660
- };
661
- }
662
- export function useAssemblyStatus(getStatus, assemblyId) {
663
- return useQuery(getStatus, { assemblyId });
664
- }
665
- export function useAssemblyStatusWithPolling(getStatus, refreshAssembly, assemblyId, options) {
666
- const status = useQuery(getStatus, { assemblyId });
667
- const refresh = useAction(refreshAssembly);
668
- const statusRef = useRef(status);
669
- const shouldContinueRef = useRef(options?.shouldContinue);
670
- const onErrorRef = useRef(options?.onError);
671
- useEffect(() => {
672
- statusRef.current = status;
673
- }, [status]);
674
- useEffect(() => {
675
- shouldContinueRef.current = options?.shouldContinue;
676
- }, [options?.shouldContinue]);
677
- useEffect(() => {
678
- onErrorRef.current = options?.onError;
679
- }, [options?.onError]);
680
- useEffect(() => {
681
- if (!assemblyId)
682
- return;
683
- const intervalMs = options?.pollIntervalMs ?? 5000;
684
- if (intervalMs <= 0)
685
- return;
686
- const shouldKeepPolling = () => {
687
- const shouldContinue = shouldContinueRef.current?.();
688
- if (shouldContinue === false)
689
- return false;
690
- if (!options?.stopOnTerminal)
691
- return true;
692
- const current = statusRef.current;
693
- const rawCandidate = current && typeof current === "object"
694
- ? (current.raw ?? current)
695
- : current;
696
- const parsed = parseAssemblyStatus(rawCandidate);
697
- return !(parsed ? isAssemblyTerminal(parsed) : false);
698
- };
699
- if (!shouldKeepPolling())
700
- return;
701
- let cancelled = false;
702
- let intervalId = null;
703
- let inFlight = false;
704
- const tick = async () => {
705
- if (cancelled)
706
- return;
707
- if (!shouldKeepPolling()) {
708
- if (intervalId)
709
- clearInterval(intervalId);
710
- cancelled = true;
711
- return;
712
- }
713
- if (inFlight)
714
- return;
715
- inFlight = true;
716
- try {
717
- await refresh({ assemblyId });
718
- }
719
- catch (error) {
720
- const resolved = error instanceof Error
721
- ? error
722
- : transloaditError("polling", "Refresh failed");
723
- onErrorRef.current?.(resolved);
724
- }
725
- finally {
726
- inFlight = false;
727
- }
728
- };
729
- intervalId = setInterval(() => {
730
- void tick();
731
- }, intervalMs);
732
- void tick();
733
- return () => {
734
- cancelled = true;
735
- if (intervalId)
736
- clearInterval(intervalId);
737
- };
738
- }, [assemblyId, options?.pollIntervalMs, options?.stopOnTerminal, refresh]);
739
- return status;
740
- }
741
- /**
742
- * @deprecated Prefer `useAssemblyStatusWithPolling` for public usage.
743
- */
744
- export function useAssemblyPoller(options) {
745
- const refreshRef = useRef(options.refresh);
746
- const onErrorRef = useRef(options.onError);
747
- const shouldContinueRef = useRef(options.shouldContinue);
748
- const statusRef = useRef(options.status);
749
- useEffect(() => {
750
- refreshRef.current = options.refresh;
751
- }, [options.refresh]);
752
- useEffect(() => {
753
- onErrorRef.current = options.onError;
754
- }, [options.onError]);
755
- useEffect(() => {
756
- shouldContinueRef.current = options.shouldContinue;
757
- }, [options.shouldContinue]);
758
- useEffect(() => {
759
- statusRef.current = options.status;
760
- }, [options.status]);
761
- useEffect(() => {
762
- if (!options.assemblyId)
763
- return;
764
- const controller = pollAssembly({
765
- intervalMs: options.intervalMs,
766
- refresh: () => refreshRef.current(),
767
- shouldContinue: () => shouldContinueRef.current?.() ?? false,
768
- isTerminal: () => {
769
- const current = statusRef.current;
770
- return current ? isAssemblyTerminal(current) : false;
771
- },
772
- onError: (error) => {
773
- onErrorRef.current?.(error);
774
- },
775
- });
776
- return () => {
777
- controller.stop();
778
- };
779
- }, [options.assemblyId, options.intervalMs]);
780
- }
781
- export function useTransloaditFiles(listResults, args) {
782
- return useQuery(listResults, args);
783
- }
784
- //# sourceMappingURL=index.js.map