@transloadit/convex 0.0.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 +235 -0
- package/dist/client/index.d.ts +244 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +196 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/types.d.ts +24 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/component/_generated/api.d.ts +20 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +15 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +101 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +16 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +31 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +18 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/apiUtils.d.ts +31 -0
- package/dist/component/apiUtils.d.ts.map +1 -0
- package/dist/component/apiUtils.js +94 -0
- package/dist/component/apiUtils.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +3 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/lib.d.ts +226 -0
- package/dist/component/lib.d.ts.map +1 -0
- package/dist/component/lib.js +383 -0
- package/dist/component/lib.js.map +1 -0
- package/dist/component/schema.d.ts +67 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +45 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/package.json +3 -0
- package/dist/react/index.d.ts +85 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +163 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +86 -0
- package/src/client/index.ts +260 -0
- package/src/client/types.ts +64 -0
- package/src/component/_generated/api.ts +32 -0
- package/src/component/_generated/component.ts +122 -0
- package/src/component/_generated/dataModel.ts +30 -0
- package/src/component/_generated/server.ts +72 -0
- package/src/component/apiUtils.test.ts +48 -0
- package/src/component/apiUtils.ts +156 -0
- package/src/component/convex.config.ts +3 -0
- package/src/component/lib.test.ts +63 -0
- package/src/component/lib.ts +466 -0
- package/src/component/schema.ts +48 -0
- package/src/component/setup.test.ts +6 -0
- package/src/react/index.tsx +292 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
|
|
4
|
+
export const assemblyStatusValues = [
|
|
5
|
+
"ASSEMBLY_UPLOADING",
|
|
6
|
+
"ASSEMBLY_EXECUTING",
|
|
7
|
+
"ASSEMBLY_COMPLETED",
|
|
8
|
+
"ASSEMBLY_CANCELED",
|
|
9
|
+
"ASSEMBLY_FAILED",
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
export type AssemblyStatus = (typeof assemblyStatusValues)[number];
|
|
13
|
+
|
|
14
|
+
export default defineSchema({
|
|
15
|
+
assemblies: defineTable({
|
|
16
|
+
assemblyId: v.string(),
|
|
17
|
+
status: v.optional(v.string()),
|
|
18
|
+
ok: v.optional(v.string()),
|
|
19
|
+
message: v.optional(v.string()),
|
|
20
|
+
templateId: v.optional(v.string()),
|
|
21
|
+
notifyUrl: v.optional(v.string()),
|
|
22
|
+
numExpectedUploadFiles: v.optional(v.number()),
|
|
23
|
+
fields: v.optional(v.any()),
|
|
24
|
+
uploads: v.optional(v.any()),
|
|
25
|
+
results: v.optional(v.any()),
|
|
26
|
+
error: v.optional(v.any()),
|
|
27
|
+
raw: v.optional(v.any()),
|
|
28
|
+
createdAt: v.number(),
|
|
29
|
+
updatedAt: v.number(),
|
|
30
|
+
userId: v.optional(v.string()),
|
|
31
|
+
})
|
|
32
|
+
.index("by_assemblyId", ["assemblyId"])
|
|
33
|
+
.index("by_status", ["status"])
|
|
34
|
+
.index("by_userId", ["userId"]),
|
|
35
|
+
results: defineTable({
|
|
36
|
+
assemblyId: v.string(),
|
|
37
|
+
stepName: v.string(),
|
|
38
|
+
resultId: v.optional(v.string()),
|
|
39
|
+
sslUrl: v.optional(v.string()),
|
|
40
|
+
name: v.optional(v.string()),
|
|
41
|
+
size: v.optional(v.number()),
|
|
42
|
+
mime: v.optional(v.string()),
|
|
43
|
+
raw: v.any(),
|
|
44
|
+
createdAt: v.number(),
|
|
45
|
+
})
|
|
46
|
+
.index("by_assemblyId", ["assemblyId"])
|
|
47
|
+
.index("by_assemblyId_and_step", ["assemblyId", "stepName"]),
|
|
48
|
+
});
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { useAction, useQuery } from "convex/react";
|
|
2
|
+
import type { FunctionReference } from "convex/server";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { Upload } from "tus-js-client";
|
|
5
|
+
|
|
6
|
+
export type GenerateUploadParamsFn = FunctionReference<
|
|
7
|
+
"action",
|
|
8
|
+
"public",
|
|
9
|
+
{
|
|
10
|
+
templateId?: string;
|
|
11
|
+
steps?: unknown;
|
|
12
|
+
fields?: unknown;
|
|
13
|
+
notifyUrl?: string;
|
|
14
|
+
numExpectedUploadFiles?: number;
|
|
15
|
+
expires?: string;
|
|
16
|
+
additionalParams?: unknown;
|
|
17
|
+
userId?: string;
|
|
18
|
+
},
|
|
19
|
+
{ params: string; signature: string; url: string }
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
export type CreateAssemblyFn = FunctionReference<
|
|
23
|
+
"action",
|
|
24
|
+
"public",
|
|
25
|
+
{
|
|
26
|
+
templateId?: string;
|
|
27
|
+
steps?: unknown;
|
|
28
|
+
fields?: unknown;
|
|
29
|
+
notifyUrl?: string;
|
|
30
|
+
numExpectedUploadFiles?: number;
|
|
31
|
+
expires?: string;
|
|
32
|
+
additionalParams?: unknown;
|
|
33
|
+
userId?: string;
|
|
34
|
+
},
|
|
35
|
+
{ assemblyId: string; data: Record<string, unknown> }
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
export type GetAssemblyStatusFn = FunctionReference<
|
|
39
|
+
"query",
|
|
40
|
+
"public",
|
|
41
|
+
{ assemblyId: string },
|
|
42
|
+
unknown
|
|
43
|
+
>;
|
|
44
|
+
|
|
45
|
+
export type ListResultsFn = FunctionReference<
|
|
46
|
+
"query",
|
|
47
|
+
"public",
|
|
48
|
+
{ assemblyId: string; stepName?: string; limit?: number },
|
|
49
|
+
Array<unknown>
|
|
50
|
+
>;
|
|
51
|
+
|
|
52
|
+
export interface UploadOptions {
|
|
53
|
+
templateId?: string;
|
|
54
|
+
steps?: Record<string, unknown>;
|
|
55
|
+
fields?: Record<string, unknown>;
|
|
56
|
+
notifyUrl?: string;
|
|
57
|
+
numExpectedUploadFiles?: number;
|
|
58
|
+
expires?: string;
|
|
59
|
+
additionalParams?: Record<string, unknown>;
|
|
60
|
+
userId?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface UploadState {
|
|
64
|
+
isUploading: boolean;
|
|
65
|
+
progress: number;
|
|
66
|
+
error: Error | null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface FormUploadOptions extends UploadOptions {
|
|
70
|
+
fileField?: string;
|
|
71
|
+
onProgress?: (progress: number) => void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface TusUploadOptions extends UploadOptions {
|
|
75
|
+
metadata?: Record<string, string>;
|
|
76
|
+
chunkSize?: number;
|
|
77
|
+
retryDelays?: number[];
|
|
78
|
+
onProgress?: (progress: number) => void;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function uploadViaForm(
|
|
82
|
+
file: File,
|
|
83
|
+
params: { params: string; signature: string; url: string },
|
|
84
|
+
options: FormUploadOptions,
|
|
85
|
+
): Promise<Record<string, unknown>> {
|
|
86
|
+
const formData = new FormData();
|
|
87
|
+
formData.append("params", params.params);
|
|
88
|
+
formData.append("signature", params.signature);
|
|
89
|
+
formData.append(options.fileField ?? "file", file);
|
|
90
|
+
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const xhr = new XMLHttpRequest();
|
|
93
|
+
xhr.open("POST", params.url, true);
|
|
94
|
+
|
|
95
|
+
xhr.upload.onprogress = (event) => {
|
|
96
|
+
if (!event.lengthComputable) return;
|
|
97
|
+
const progress = Math.round((event.loaded / event.total) * 100);
|
|
98
|
+
options.onProgress?.(progress);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
xhr.onload = () => {
|
|
102
|
+
try {
|
|
103
|
+
const response = JSON.parse(xhr.responseText) as Record<
|
|
104
|
+
string,
|
|
105
|
+
unknown
|
|
106
|
+
>;
|
|
107
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
108
|
+
resolve(response);
|
|
109
|
+
} else {
|
|
110
|
+
reject(
|
|
111
|
+
new Error(
|
|
112
|
+
`Transloadit upload failed (${xhr.status}): ${JSON.stringify(response)}`,
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
} catch (error) {
|
|
117
|
+
reject(error);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
xhr.onerror = () => {
|
|
122
|
+
reject(new Error("Transloadit upload failed"));
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
xhr.send(formData);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function useTransloaditUpload(
|
|
130
|
+
generateUploadParams: GenerateUploadParamsFn,
|
|
131
|
+
) {
|
|
132
|
+
const generate = useAction(generateUploadParams);
|
|
133
|
+
const [state, setState] = useState<UploadState>({
|
|
134
|
+
isUploading: false,
|
|
135
|
+
progress: 0,
|
|
136
|
+
error: null,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const upload = useCallback(
|
|
140
|
+
async (file: File, options: FormUploadOptions) => {
|
|
141
|
+
setState({ isUploading: true, progress: 0, error: null });
|
|
142
|
+
try {
|
|
143
|
+
const params = await generate({
|
|
144
|
+
templateId: options.templateId,
|
|
145
|
+
steps: options.steps,
|
|
146
|
+
fields: options.fields,
|
|
147
|
+
notifyUrl: options.notifyUrl,
|
|
148
|
+
numExpectedUploadFiles: options.numExpectedUploadFiles,
|
|
149
|
+
expires: options.expires,
|
|
150
|
+
additionalParams: options.additionalParams,
|
|
151
|
+
userId: options.userId,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const response = await uploadViaForm(file, params, {
|
|
155
|
+
...options,
|
|
156
|
+
onProgress: (progress) => {
|
|
157
|
+
setState((prev) => ({ ...prev, progress }));
|
|
158
|
+
options.onProgress?.(progress);
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
setState({ isUploading: false, progress: 100, error: null });
|
|
163
|
+
return response;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
const err = error instanceof Error ? error : new Error("Upload failed");
|
|
166
|
+
setState({ isUploading: false, progress: 0, error: err });
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
[generate],
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const reset = useCallback(() => {
|
|
174
|
+
setState({ isUploading: false, progress: 0, error: null });
|
|
175
|
+
}, []);
|
|
176
|
+
|
|
177
|
+
return useMemo(
|
|
178
|
+
() => ({
|
|
179
|
+
upload,
|
|
180
|
+
reset,
|
|
181
|
+
isUploading: state.isUploading,
|
|
182
|
+
progress: state.progress,
|
|
183
|
+
error: state.error,
|
|
184
|
+
}),
|
|
185
|
+
[state.error, state.isUploading, state.progress, upload, reset],
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function useTransloaditTusUpload(createAssembly: CreateAssemblyFn) {
|
|
190
|
+
const create = useAction(createAssembly);
|
|
191
|
+
const [state, setState] = useState<UploadState>({
|
|
192
|
+
isUploading: false,
|
|
193
|
+
progress: 0,
|
|
194
|
+
error: null,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const upload = useCallback(
|
|
198
|
+
async (file: File, options: TusUploadOptions) => {
|
|
199
|
+
setState({ isUploading: true, progress: 0, error: null });
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
const assembly = await create({
|
|
203
|
+
templateId: options.templateId,
|
|
204
|
+
steps: options.steps,
|
|
205
|
+
fields: options.fields,
|
|
206
|
+
notifyUrl: options.notifyUrl,
|
|
207
|
+
numExpectedUploadFiles: options.numExpectedUploadFiles ?? 1,
|
|
208
|
+
expires: options.expires,
|
|
209
|
+
additionalParams: options.additionalParams,
|
|
210
|
+
userId: options.userId,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const data = assembly.data as Record<string, unknown>;
|
|
214
|
+
const tusUrl =
|
|
215
|
+
(typeof data.tus_url === "string" && data.tus_url) ||
|
|
216
|
+
(typeof data.tusUrl === "string" && data.tusUrl) ||
|
|
217
|
+
"";
|
|
218
|
+
|
|
219
|
+
if (!tusUrl) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
"Transloadit response missing tus_url for resumable upload",
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const metadata: Record<string, string> = {
|
|
226
|
+
filename: file.name,
|
|
227
|
+
filetype: file.type,
|
|
228
|
+
...options.metadata,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
await new Promise<void>((resolve, reject) => {
|
|
232
|
+
const uploader = new Upload(file, {
|
|
233
|
+
endpoint: tusUrl,
|
|
234
|
+
metadata,
|
|
235
|
+
chunkSize: options.chunkSize,
|
|
236
|
+
retryDelays: options.retryDelays ?? [0, 3000, 5000, 10000],
|
|
237
|
+
onProgress: (bytesUploaded, bytesTotal) => {
|
|
238
|
+
const progress = Math.round((bytesUploaded / bytesTotal) * 100);
|
|
239
|
+
setState((prev) => ({ ...prev, progress }));
|
|
240
|
+
options.onProgress?.(progress);
|
|
241
|
+
},
|
|
242
|
+
onError: (error) => {
|
|
243
|
+
reject(error);
|
|
244
|
+
},
|
|
245
|
+
onSuccess: () => {
|
|
246
|
+
resolve();
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
uploader.start();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
setState({ isUploading: false, progress: 100, error: null });
|
|
254
|
+
return assembly;
|
|
255
|
+
} catch (error) {
|
|
256
|
+
const err = error instanceof Error ? error : new Error("Upload failed");
|
|
257
|
+
setState({ isUploading: false, progress: 0, error: err });
|
|
258
|
+
throw err;
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
[create],
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const reset = useCallback(() => {
|
|
265
|
+
setState({ isUploading: false, progress: 0, error: null });
|
|
266
|
+
}, []);
|
|
267
|
+
|
|
268
|
+
return useMemo(
|
|
269
|
+
() => ({
|
|
270
|
+
upload,
|
|
271
|
+
reset,
|
|
272
|
+
isUploading: state.isUploading,
|
|
273
|
+
progress: state.progress,
|
|
274
|
+
error: state.error,
|
|
275
|
+
}),
|
|
276
|
+
[state.error, state.isUploading, state.progress, upload, reset],
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function useAssemblyStatus(
|
|
281
|
+
getStatus: GetAssemblyStatusFn,
|
|
282
|
+
assemblyId: string,
|
|
283
|
+
) {
|
|
284
|
+
return useQuery(getStatus, { assemblyId });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function useTransloaditFiles(
|
|
288
|
+
listResults: ListResultsFn,
|
|
289
|
+
args: { assemblyId: string; stepName?: string; limit?: number },
|
|
290
|
+
) {
|
|
291
|
+
return useQuery(listResults, args);
|
|
292
|
+
}
|