@transloadit/convex 0.0.1 → 0.0.3
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 +162 -79
- package/dist/client/index.d.ts +92 -63
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +57 -30
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/_generated/component.d.ts +35 -15
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/apiUtils.d.ts +13 -4
- package/dist/component/apiUtils.d.ts.map +1 -1
- package/dist/component/apiUtils.js +22 -12
- package/dist/component/apiUtils.js.map +1 -1
- package/dist/component/lib.d.ts +69 -45
- package/dist/component/lib.d.ts.map +1 -1
- package/dist/component/lib.js +154 -77
- package/dist/component/lib.js.map +1 -1
- package/dist/component/schema.d.ts +9 -9
- package/dist/component/schema.js +3 -3
- package/dist/component/schema.js.map +1 -1
- package/dist/react/index.d.ts +23 -25
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +129 -88
- package/dist/react/index.js.map +1 -1
- package/dist/test/index.d.ts +65 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/index.js +8 -0
- package/dist/test/index.js.map +1 -0
- package/package.json +27 -15
- package/src/client/index.ts +72 -35
- package/src/client/types.ts +12 -11
- package/src/component/_generated/component.ts +44 -13
- package/src/component/apiUtils.test.ts +29 -0
- package/src/component/apiUtils.ts +52 -26
- package/src/component/lib.test.ts +73 -3
- package/src/component/lib.ts +220 -97
- package/src/component/schema.ts +3 -3
- package/src/react/index.tsx +193 -150
- package/src/test/index.ts +10 -0
package/src/component/lib.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { AssemblyStatus } from "@transloadit/types/assemblyStatus";
|
|
2
|
+
import type { AssemblyInstructionsInput } from "@transloadit/types/template";
|
|
3
|
+
import { anyApi, type FunctionReference } from "convex/server";
|
|
1
4
|
import { type Infer, v } from "convex/values";
|
|
2
|
-
import { internal } from "./_generated/api.js";
|
|
3
|
-
import type { Id } from "./_generated/dataModel.js";
|
|
4
5
|
import {
|
|
5
6
|
action,
|
|
7
|
+
internalAction,
|
|
6
8
|
internalMutation,
|
|
7
9
|
mutation,
|
|
8
10
|
query,
|
|
@@ -16,6 +18,101 @@ import {
|
|
|
16
18
|
|
|
17
19
|
const TRANSLOADIT_ASSEMBLY_URL = "https://api2.transloadit.com/assemblies";
|
|
18
20
|
|
|
21
|
+
type ProcessWebhookResult = {
|
|
22
|
+
assemblyId: string;
|
|
23
|
+
resultCount: number;
|
|
24
|
+
ok?: string;
|
|
25
|
+
status?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type InternalApi = {
|
|
29
|
+
lib: {
|
|
30
|
+
upsertAssembly: FunctionReference<
|
|
31
|
+
"mutation",
|
|
32
|
+
"internal",
|
|
33
|
+
Record<string, unknown>,
|
|
34
|
+
unknown
|
|
35
|
+
>;
|
|
36
|
+
replaceResultsForAssembly: FunctionReference<
|
|
37
|
+
"mutation",
|
|
38
|
+
"internal",
|
|
39
|
+
Record<string, unknown>,
|
|
40
|
+
unknown
|
|
41
|
+
>;
|
|
42
|
+
processWebhook: FunctionReference<
|
|
43
|
+
"action",
|
|
44
|
+
"internal",
|
|
45
|
+
Record<string, unknown>,
|
|
46
|
+
ProcessWebhookResult
|
|
47
|
+
>;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const internal = anyApi as unknown as InternalApi;
|
|
52
|
+
|
|
53
|
+
const resolveAssemblyId = (payload: AssemblyStatus): string => {
|
|
54
|
+
if (typeof payload.assembly_id === "string") return payload.assembly_id;
|
|
55
|
+
if (typeof payload.assemblyId === "string") return payload.assemblyId;
|
|
56
|
+
return "";
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const buildSignedAssemblyUrl = async (
|
|
60
|
+
assemblyId: string,
|
|
61
|
+
authKey: string,
|
|
62
|
+
authSecret: string,
|
|
63
|
+
): Promise<string> => {
|
|
64
|
+
const params = JSON.stringify({
|
|
65
|
+
auth: {
|
|
66
|
+
key: authKey,
|
|
67
|
+
expires: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
const signature = await signTransloaditParams(params, authSecret);
|
|
71
|
+
const url = new URL(`${TRANSLOADIT_ASSEMBLY_URL}/${assemblyId}`);
|
|
72
|
+
url.searchParams.set("signature", signature);
|
|
73
|
+
url.searchParams.set("params", params);
|
|
74
|
+
return url.toString();
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const applyAssemblyStatus = async (
|
|
78
|
+
ctx: Pick<import("./_generated/server.js").FunctionCtx, "runMutation">,
|
|
79
|
+
payload: AssemblyStatus,
|
|
80
|
+
) => {
|
|
81
|
+
const assemblyId = resolveAssemblyId(payload);
|
|
82
|
+
if (!assemblyId) {
|
|
83
|
+
throw new Error("Webhook payload missing assembly_id");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const results = flattenResults(payload.results ?? undefined);
|
|
87
|
+
|
|
88
|
+
await ctx.runMutation(internal.lib.upsertAssembly, {
|
|
89
|
+
assemblyId,
|
|
90
|
+
status: typeof payload.ok === "string" ? payload.ok : undefined,
|
|
91
|
+
ok: typeof payload.ok === "string" ? payload.ok : undefined,
|
|
92
|
+
message: typeof payload.message === "string" ? payload.message : undefined,
|
|
93
|
+
templateId:
|
|
94
|
+
typeof payload.template_id === "string" ? payload.template_id : undefined,
|
|
95
|
+
notifyUrl:
|
|
96
|
+
typeof payload.notify_url === "string" ? payload.notify_url : undefined,
|
|
97
|
+
uploads: payload.uploads,
|
|
98
|
+
results: payload.results,
|
|
99
|
+
error: payload.error,
|
|
100
|
+
raw: payload,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await ctx.runMutation(internal.lib.replaceResultsForAssembly, {
|
|
104
|
+
assemblyId,
|
|
105
|
+
results,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
assemblyId,
|
|
110
|
+
resultCount: results.length,
|
|
111
|
+
ok: typeof payload.ok === "string" ? payload.ok : undefined,
|
|
112
|
+
status: typeof payload.ok === "string" ? payload.ok : undefined,
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
|
|
19
116
|
export const vAssembly = v.object({
|
|
20
117
|
_id: v.id("assemblies"),
|
|
21
118
|
_creationTime: v.number(),
|
|
@@ -26,9 +123,9 @@ export const vAssembly = v.object({
|
|
|
26
123
|
templateId: v.optional(v.string()),
|
|
27
124
|
notifyUrl: v.optional(v.string()),
|
|
28
125
|
numExpectedUploadFiles: v.optional(v.number()),
|
|
29
|
-
fields: v.optional(v.any()),
|
|
30
|
-
uploads: v.optional(v.any()),
|
|
31
|
-
results: v.optional(v.any()),
|
|
126
|
+
fields: v.optional(v.record(v.string(), v.any())),
|
|
127
|
+
uploads: v.optional(v.array(v.any())),
|
|
128
|
+
results: v.optional(v.record(v.string(), v.array(v.any()))),
|
|
32
129
|
error: v.optional(v.any()),
|
|
33
130
|
raw: v.optional(v.any()),
|
|
34
131
|
createdAt: v.number(),
|
|
@@ -61,12 +158,12 @@ export const vTransloaditConfig = v.object({
|
|
|
61
158
|
|
|
62
159
|
const vAssemblyBaseArgs = {
|
|
63
160
|
templateId: v.optional(v.string()),
|
|
64
|
-
steps: v.optional(v.any()),
|
|
65
|
-
fields: v.optional(v.any()),
|
|
161
|
+
steps: v.optional(v.record(v.string(), v.any())),
|
|
162
|
+
fields: v.optional(v.record(v.string(), v.any())),
|
|
66
163
|
notifyUrl: v.optional(v.string()),
|
|
67
164
|
numExpectedUploadFiles: v.optional(v.number()),
|
|
68
165
|
expires: v.optional(v.string()),
|
|
69
|
-
additionalParams: v.optional(v.any()),
|
|
166
|
+
additionalParams: v.optional(v.record(v.string(), v.any())),
|
|
70
167
|
userId: v.optional(v.string()),
|
|
71
168
|
};
|
|
72
169
|
|
|
@@ -79,9 +176,9 @@ export const upsertAssembly = internalMutation({
|
|
|
79
176
|
templateId: v.optional(v.string()),
|
|
80
177
|
notifyUrl: v.optional(v.string()),
|
|
81
178
|
numExpectedUploadFiles: v.optional(v.number()),
|
|
82
|
-
fields: v.optional(v.any()),
|
|
83
|
-
uploads: v.optional(v.any()),
|
|
84
|
-
results: v.optional(v.any()),
|
|
179
|
+
fields: v.optional(v.record(v.string(), v.any())),
|
|
180
|
+
uploads: v.optional(v.array(v.any())),
|
|
181
|
+
results: v.optional(v.record(v.string(), v.array(v.any()))),
|
|
85
182
|
error: v.optional(v.any()),
|
|
86
183
|
raw: v.optional(v.any()),
|
|
87
184
|
userId: v.optional(v.string()),
|
|
@@ -189,10 +286,10 @@ export const createAssembly = action({
|
|
|
189
286
|
const { paramsString, params } = buildTransloaditParams({
|
|
190
287
|
authKey: args.config.authKey,
|
|
191
288
|
templateId: args.templateId,
|
|
192
|
-
steps: args.steps as
|
|
193
|
-
fields: args.fields as
|
|
289
|
+
steps: args.steps as AssemblyInstructionsInput["steps"],
|
|
290
|
+
fields: args.fields as AssemblyInstructionsInput["fields"],
|
|
194
291
|
notifyUrl: args.notifyUrl,
|
|
195
|
-
numExpectedUploadFiles:
|
|
292
|
+
numExpectedUploadFiles: undefined,
|
|
196
293
|
expires: args.expires,
|
|
197
294
|
additionalParams: args.additionalParams as
|
|
198
295
|
| Record<string, unknown>
|
|
@@ -207,6 +304,12 @@ export const createAssembly = action({
|
|
|
207
304
|
const formData = new FormData();
|
|
208
305
|
formData.append("params", paramsString);
|
|
209
306
|
formData.append("signature", signature);
|
|
307
|
+
if (typeof args.numExpectedUploadFiles === "number") {
|
|
308
|
+
formData.append(
|
|
309
|
+
"tus_num_expected_upload_files",
|
|
310
|
+
String(args.numExpectedUploadFiles),
|
|
311
|
+
);
|
|
312
|
+
}
|
|
210
313
|
|
|
211
314
|
const response = await fetch(TRANSLOADIT_ASSEMBLY_URL, {
|
|
212
315
|
method: "POST",
|
|
@@ -251,72 +354,30 @@ export const createAssembly = action({
|
|
|
251
354
|
},
|
|
252
355
|
});
|
|
253
356
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
signature: v.string(),
|
|
262
|
-
url: v.string(),
|
|
263
|
-
}),
|
|
264
|
-
handler: async (ctx, args) => {
|
|
265
|
-
const { paramsString } = buildTransloaditParams({
|
|
266
|
-
authKey: args.config.authKey,
|
|
267
|
-
templateId: args.templateId,
|
|
268
|
-
steps: args.steps as Record<string, unknown> | undefined,
|
|
269
|
-
fields: args.fields as Record<string, unknown> | undefined,
|
|
270
|
-
notifyUrl: args.notifyUrl,
|
|
271
|
-
numExpectedUploadFiles: args.numExpectedUploadFiles,
|
|
272
|
-
expires: args.expires,
|
|
273
|
-
additionalParams: args.additionalParams as
|
|
274
|
-
| Record<string, unknown>
|
|
275
|
-
| undefined,
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
const signature = await signTransloaditParams(
|
|
279
|
-
paramsString,
|
|
280
|
-
args.config.authSecret,
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
return {
|
|
284
|
-
params: paramsString,
|
|
285
|
-
signature,
|
|
286
|
-
url: TRANSLOADIT_ASSEMBLY_URL,
|
|
287
|
-
};
|
|
288
|
-
},
|
|
289
|
-
});
|
|
357
|
+
const vWebhookArgs = {
|
|
358
|
+
payload: v.any(),
|
|
359
|
+
rawBody: v.optional(v.string()),
|
|
360
|
+
signature: v.optional(v.string()),
|
|
361
|
+
verifySignature: v.optional(v.boolean()),
|
|
362
|
+
authSecret: v.optional(v.string()),
|
|
363
|
+
};
|
|
290
364
|
|
|
291
|
-
export const
|
|
292
|
-
args:
|
|
293
|
-
payload: v.any(),
|
|
294
|
-
rawBody: v.optional(v.string()),
|
|
295
|
-
signature: v.optional(v.string()),
|
|
296
|
-
verifySignature: v.optional(v.boolean()),
|
|
297
|
-
config: v.optional(
|
|
298
|
-
v.object({
|
|
299
|
-
authSecret: v.string(),
|
|
300
|
-
}),
|
|
301
|
-
),
|
|
302
|
-
},
|
|
365
|
+
export const processWebhook = internalAction({
|
|
366
|
+
args: vWebhookArgs,
|
|
303
367
|
returns: v.object({
|
|
304
368
|
assemblyId: v.string(),
|
|
305
369
|
resultCount: v.number(),
|
|
370
|
+
ok: v.optional(v.string()),
|
|
371
|
+
status: v.optional(v.string()),
|
|
306
372
|
}),
|
|
307
373
|
handler: async (ctx, args) => {
|
|
308
374
|
const rawBody = args.rawBody ?? JSON.stringify(args.payload ?? {});
|
|
309
375
|
const shouldVerify = args.verifySignature ?? true;
|
|
310
|
-
const authSecret =
|
|
311
|
-
args.config?.authSecret ??
|
|
312
|
-
process.env.TRANSLOADIT_AUTH_SECRET ??
|
|
313
|
-
process.env.TRANSLOADIT_SECRET;
|
|
376
|
+
const authSecret = args.authSecret ?? process.env.TRANSLOADIT_SECRET;
|
|
314
377
|
|
|
315
378
|
if (shouldVerify) {
|
|
316
379
|
if (!authSecret) {
|
|
317
|
-
throw new Error(
|
|
318
|
-
"Missing TRANSLOADIT_AUTH_SECRET for webhook validation",
|
|
319
|
-
);
|
|
380
|
+
throw new Error("Missing TRANSLOADIT_SECRET for webhook validation");
|
|
320
381
|
}
|
|
321
382
|
const verified = await verifyWebhookSignature({
|
|
322
383
|
rawBody,
|
|
@@ -328,41 +389,103 @@ export const handleWebhook = action({
|
|
|
328
389
|
}
|
|
329
390
|
}
|
|
330
391
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
? payload.assembly_id
|
|
335
|
-
: typeof payload.assemblyId === "string"
|
|
336
|
-
? payload.assemblyId
|
|
337
|
-
: "";
|
|
392
|
+
return applyAssemblyStatus(ctx, args.payload as AssemblyStatus);
|
|
393
|
+
},
|
|
394
|
+
});
|
|
338
395
|
|
|
396
|
+
export const handleWebhook = action({
|
|
397
|
+
args: {
|
|
398
|
+
...vWebhookArgs,
|
|
399
|
+
config: v.optional(
|
|
400
|
+
v.object({
|
|
401
|
+
authSecret: v.string(),
|
|
402
|
+
}),
|
|
403
|
+
),
|
|
404
|
+
},
|
|
405
|
+
returns: v.object({
|
|
406
|
+
assemblyId: v.string(),
|
|
407
|
+
resultCount: v.number(),
|
|
408
|
+
ok: v.optional(v.string()),
|
|
409
|
+
status: v.optional(v.string()),
|
|
410
|
+
}),
|
|
411
|
+
handler: async (ctx, args) => {
|
|
412
|
+
return ctx.runAction(internal.lib.processWebhook, {
|
|
413
|
+
payload: args.payload,
|
|
414
|
+
rawBody: args.rawBody,
|
|
415
|
+
signature: args.signature,
|
|
416
|
+
verifySignature: args.verifySignature,
|
|
417
|
+
authSecret: args.config?.authSecret,
|
|
418
|
+
});
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
export const queueWebhook = action({
|
|
423
|
+
args: {
|
|
424
|
+
...vWebhookArgs,
|
|
425
|
+
config: v.optional(
|
|
426
|
+
v.object({
|
|
427
|
+
authSecret: v.string(),
|
|
428
|
+
}),
|
|
429
|
+
),
|
|
430
|
+
},
|
|
431
|
+
returns: v.object({
|
|
432
|
+
assemblyId: v.string(),
|
|
433
|
+
queued: v.boolean(),
|
|
434
|
+
}),
|
|
435
|
+
handler: async (ctx, args) => {
|
|
436
|
+
const payload = args.payload as AssemblyStatus;
|
|
437
|
+
const assemblyId = resolveAssemblyId(payload);
|
|
339
438
|
if (!assemblyId) {
|
|
340
439
|
throw new Error("Webhook payload missing assembly_id");
|
|
341
440
|
}
|
|
342
441
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
assemblyId,
|
|
350
|
-
status: typeof payload.ok === "string" ? payload.ok : undefined,
|
|
351
|
-
ok: typeof payload.ok === "string" ? payload.ok : undefined,
|
|
352
|
-
message:
|
|
353
|
-
typeof payload.message === "string" ? payload.message : undefined,
|
|
354
|
-
uploads: payload.uploads,
|
|
355
|
-
results: payload.results,
|
|
356
|
-
error: payload.error,
|
|
357
|
-
raw: payload,
|
|
442
|
+
await ctx.scheduler.runAfter(0, internal.lib.processWebhook, {
|
|
443
|
+
payload: args.payload,
|
|
444
|
+
rawBody: args.rawBody,
|
|
445
|
+
signature: args.signature,
|
|
446
|
+
verifySignature: args.verifySignature,
|
|
447
|
+
authSecret: args.config?.authSecret,
|
|
358
448
|
});
|
|
359
449
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
450
|
+
return { assemblyId, queued: true };
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
export const refreshAssembly = action({
|
|
455
|
+
args: {
|
|
456
|
+
assemblyId: v.string(),
|
|
457
|
+
config: v.optional(
|
|
458
|
+
v.object({
|
|
459
|
+
authKey: v.string(),
|
|
460
|
+
authSecret: v.string(),
|
|
461
|
+
}),
|
|
462
|
+
),
|
|
463
|
+
},
|
|
464
|
+
returns: v.object({
|
|
465
|
+
assemblyId: v.string(),
|
|
466
|
+
resultCount: v.number(),
|
|
467
|
+
ok: v.optional(v.string()),
|
|
468
|
+
status: v.optional(v.string()),
|
|
469
|
+
}),
|
|
470
|
+
handler: async (ctx, args) => {
|
|
471
|
+
const { assemblyId } = args;
|
|
472
|
+
const authKey = args.config?.authKey ?? process.env.TRANSLOADIT_KEY;
|
|
473
|
+
const authSecret =
|
|
474
|
+
args.config?.authSecret ?? process.env.TRANSLOADIT_SECRET;
|
|
475
|
+
const url =
|
|
476
|
+
authKey && authSecret
|
|
477
|
+
? await buildSignedAssemblyUrl(assemblyId, authKey, authSecret)
|
|
478
|
+
: `${TRANSLOADIT_ASSEMBLY_URL}/${assemblyId}`;
|
|
479
|
+
|
|
480
|
+
const response = await fetch(url);
|
|
481
|
+
const payload = (await response.json()) as AssemblyStatus;
|
|
482
|
+
if (!response.ok) {
|
|
483
|
+
throw new Error(
|
|
484
|
+
`Transloadit status error ${response.status}: ${JSON.stringify(payload)}`,
|
|
485
|
+
);
|
|
486
|
+
}
|
|
364
487
|
|
|
365
|
-
return
|
|
488
|
+
return applyAssemblyStatus(ctx, payload);
|
|
366
489
|
},
|
|
367
490
|
});
|
|
368
491
|
|
|
@@ -438,7 +561,7 @@ export const storeAssemblyMetadata = mutation({
|
|
|
438
561
|
args: {
|
|
439
562
|
assemblyId: v.string(),
|
|
440
563
|
userId: v.optional(v.string()),
|
|
441
|
-
fields: v.optional(v.any()),
|
|
564
|
+
fields: v.optional(v.record(v.string(), v.any())),
|
|
442
565
|
},
|
|
443
566
|
returns: v.union(vAssembly, v.null()),
|
|
444
567
|
handler: async (ctx, args) => {
|
package/src/component/schema.ts
CHANGED
|
@@ -20,9 +20,9 @@ export default defineSchema({
|
|
|
20
20
|
templateId: v.optional(v.string()),
|
|
21
21
|
notifyUrl: v.optional(v.string()),
|
|
22
22
|
numExpectedUploadFiles: v.optional(v.number()),
|
|
23
|
-
fields: v.optional(v.any()),
|
|
24
|
-
uploads: v.optional(v.any()),
|
|
25
|
-
results: v.optional(v.any()),
|
|
23
|
+
fields: v.optional(v.record(v.string(), v.any())),
|
|
24
|
+
uploads: v.optional(v.array(v.any())),
|
|
25
|
+
results: v.optional(v.record(v.string(), v.array(v.any()))),
|
|
26
26
|
error: v.optional(v.any()),
|
|
27
27
|
raw: v.optional(v.any()),
|
|
28
28
|
createdAt: v.number(),
|