@transloadit/convex 0.0.2 → 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 +83 -54
- 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/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 +21 -13
- package/src/client/index.ts +72 -35
- 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 -96
- 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,7 +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
5
|
import {
|
|
4
6
|
action,
|
|
7
|
+
internalAction,
|
|
5
8
|
internalMutation,
|
|
6
9
|
mutation,
|
|
7
10
|
query,
|
|
@@ -15,6 +18,101 @@ import {
|
|
|
15
18
|
|
|
16
19
|
const TRANSLOADIT_ASSEMBLY_URL = "https://api2.transloadit.com/assemblies";
|
|
17
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
|
+
|
|
18
116
|
export const vAssembly = v.object({
|
|
19
117
|
_id: v.id("assemblies"),
|
|
20
118
|
_creationTime: v.number(),
|
|
@@ -25,9 +123,9 @@ export const vAssembly = v.object({
|
|
|
25
123
|
templateId: v.optional(v.string()),
|
|
26
124
|
notifyUrl: v.optional(v.string()),
|
|
27
125
|
numExpectedUploadFiles: v.optional(v.number()),
|
|
28
|
-
fields: v.optional(v.any()),
|
|
29
|
-
uploads: v.optional(v.any()),
|
|
30
|
-
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()))),
|
|
31
129
|
error: v.optional(v.any()),
|
|
32
130
|
raw: v.optional(v.any()),
|
|
33
131
|
createdAt: v.number(),
|
|
@@ -60,12 +158,12 @@ export const vTransloaditConfig = v.object({
|
|
|
60
158
|
|
|
61
159
|
const vAssemblyBaseArgs = {
|
|
62
160
|
templateId: v.optional(v.string()),
|
|
63
|
-
steps: v.optional(v.any()),
|
|
64
|
-
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())),
|
|
65
163
|
notifyUrl: v.optional(v.string()),
|
|
66
164
|
numExpectedUploadFiles: v.optional(v.number()),
|
|
67
165
|
expires: v.optional(v.string()),
|
|
68
|
-
additionalParams: v.optional(v.any()),
|
|
166
|
+
additionalParams: v.optional(v.record(v.string(), v.any())),
|
|
69
167
|
userId: v.optional(v.string()),
|
|
70
168
|
};
|
|
71
169
|
|
|
@@ -78,9 +176,9 @@ export const upsertAssembly = internalMutation({
|
|
|
78
176
|
templateId: v.optional(v.string()),
|
|
79
177
|
notifyUrl: v.optional(v.string()),
|
|
80
178
|
numExpectedUploadFiles: v.optional(v.number()),
|
|
81
|
-
fields: v.optional(v.any()),
|
|
82
|
-
uploads: v.optional(v.any()),
|
|
83
|
-
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()))),
|
|
84
182
|
error: v.optional(v.any()),
|
|
85
183
|
raw: v.optional(v.any()),
|
|
86
184
|
userId: v.optional(v.string()),
|
|
@@ -188,10 +286,10 @@ export const createAssembly = action({
|
|
|
188
286
|
const { paramsString, params } = buildTransloaditParams({
|
|
189
287
|
authKey: args.config.authKey,
|
|
190
288
|
templateId: args.templateId,
|
|
191
|
-
steps: args.steps as
|
|
192
|
-
fields: args.fields as
|
|
289
|
+
steps: args.steps as AssemblyInstructionsInput["steps"],
|
|
290
|
+
fields: args.fields as AssemblyInstructionsInput["fields"],
|
|
193
291
|
notifyUrl: args.notifyUrl,
|
|
194
|
-
numExpectedUploadFiles:
|
|
292
|
+
numExpectedUploadFiles: undefined,
|
|
195
293
|
expires: args.expires,
|
|
196
294
|
additionalParams: args.additionalParams as
|
|
197
295
|
| Record<string, unknown>
|
|
@@ -206,6 +304,12 @@ export const createAssembly = action({
|
|
|
206
304
|
const formData = new FormData();
|
|
207
305
|
formData.append("params", paramsString);
|
|
208
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
|
+
}
|
|
209
313
|
|
|
210
314
|
const response = await fetch(TRANSLOADIT_ASSEMBLY_URL, {
|
|
211
315
|
method: "POST",
|
|
@@ -250,72 +354,30 @@ export const createAssembly = action({
|
|
|
250
354
|
},
|
|
251
355
|
});
|
|
252
356
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
signature: v.string(),
|
|
261
|
-
url: v.string(),
|
|
262
|
-
}),
|
|
263
|
-
handler: async (_ctx, args) => {
|
|
264
|
-
const { paramsString } = buildTransloaditParams({
|
|
265
|
-
authKey: args.config.authKey,
|
|
266
|
-
templateId: args.templateId,
|
|
267
|
-
steps: args.steps as Record<string, unknown> | undefined,
|
|
268
|
-
fields: args.fields as Record<string, unknown> | undefined,
|
|
269
|
-
notifyUrl: args.notifyUrl,
|
|
270
|
-
numExpectedUploadFiles: args.numExpectedUploadFiles,
|
|
271
|
-
expires: args.expires,
|
|
272
|
-
additionalParams: args.additionalParams as
|
|
273
|
-
| Record<string, unknown>
|
|
274
|
-
| undefined,
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
const signature = await signTransloaditParams(
|
|
278
|
-
paramsString,
|
|
279
|
-
args.config.authSecret,
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
return {
|
|
283
|
-
params: paramsString,
|
|
284
|
-
signature,
|
|
285
|
-
url: TRANSLOADIT_ASSEMBLY_URL,
|
|
286
|
-
};
|
|
287
|
-
},
|
|
288
|
-
});
|
|
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
|
+
};
|
|
289
364
|
|
|
290
|
-
export const
|
|
291
|
-
args:
|
|
292
|
-
payload: v.any(),
|
|
293
|
-
rawBody: v.optional(v.string()),
|
|
294
|
-
signature: v.optional(v.string()),
|
|
295
|
-
verifySignature: v.optional(v.boolean()),
|
|
296
|
-
config: v.optional(
|
|
297
|
-
v.object({
|
|
298
|
-
authSecret: v.string(),
|
|
299
|
-
}),
|
|
300
|
-
),
|
|
301
|
-
},
|
|
365
|
+
export const processWebhook = internalAction({
|
|
366
|
+
args: vWebhookArgs,
|
|
302
367
|
returns: v.object({
|
|
303
368
|
assemblyId: v.string(),
|
|
304
369
|
resultCount: v.number(),
|
|
370
|
+
ok: v.optional(v.string()),
|
|
371
|
+
status: v.optional(v.string()),
|
|
305
372
|
}),
|
|
306
373
|
handler: async (ctx, args) => {
|
|
307
374
|
const rawBody = args.rawBody ?? JSON.stringify(args.payload ?? {});
|
|
308
375
|
const shouldVerify = args.verifySignature ?? true;
|
|
309
|
-
const authSecret =
|
|
310
|
-
args.config?.authSecret ??
|
|
311
|
-
process.env.TRANSLOADIT_AUTH_SECRET ??
|
|
312
|
-
process.env.TRANSLOADIT_SECRET;
|
|
376
|
+
const authSecret = args.authSecret ?? process.env.TRANSLOADIT_SECRET;
|
|
313
377
|
|
|
314
378
|
if (shouldVerify) {
|
|
315
379
|
if (!authSecret) {
|
|
316
|
-
throw new Error(
|
|
317
|
-
"Missing TRANSLOADIT_AUTH_SECRET for webhook validation",
|
|
318
|
-
);
|
|
380
|
+
throw new Error("Missing TRANSLOADIT_SECRET for webhook validation");
|
|
319
381
|
}
|
|
320
382
|
const verified = await verifyWebhookSignature({
|
|
321
383
|
rawBody,
|
|
@@ -327,41 +389,103 @@ export const handleWebhook = action({
|
|
|
327
389
|
}
|
|
328
390
|
}
|
|
329
391
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
392
|
+
return applyAssemblyStatus(ctx, args.payload as AssemblyStatus);
|
|
393
|
+
},
|
|
394
|
+
});
|
|
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
|
+
});
|
|
337
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);
|
|
338
438
|
if (!assemblyId) {
|
|
339
439
|
throw new Error("Webhook payload missing assembly_id");
|
|
340
440
|
}
|
|
341
441
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
assemblyId,
|
|
349
|
-
status: typeof payload.ok === "string" ? payload.ok : undefined,
|
|
350
|
-
ok: typeof payload.ok === "string" ? payload.ok : undefined,
|
|
351
|
-
message:
|
|
352
|
-
typeof payload.message === "string" ? payload.message : undefined,
|
|
353
|
-
uploads: payload.uploads,
|
|
354
|
-
results: payload.results,
|
|
355
|
-
error: payload.error,
|
|
356
|
-
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,
|
|
357
448
|
});
|
|
358
449
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
+
}
|
|
363
487
|
|
|
364
|
-
return
|
|
488
|
+
return applyAssemblyStatus(ctx, payload);
|
|
365
489
|
},
|
|
366
490
|
});
|
|
367
491
|
|
|
@@ -437,7 +561,7 @@ export const storeAssemblyMetadata = mutation({
|
|
|
437
561
|
args: {
|
|
438
562
|
assemblyId: v.string(),
|
|
439
563
|
userId: v.optional(v.string()),
|
|
440
|
-
fields: v.optional(v.any()),
|
|
564
|
+
fields: v.optional(v.record(v.string(), v.any())),
|
|
441
565
|
},
|
|
442
566
|
returns: v.union(vAssembly, v.null()),
|
|
443
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(),
|