@transloadit/convex 0.0.2 → 0.0.4

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 (87) hide show
  1. package/README.md +184 -121
  2. package/dist/client/index.d.ts +100 -60
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +69 -31
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/component/_generated/api.d.ts +2 -2
  7. package/dist/component/_generated/component.d.ts +35 -15
  8. package/dist/component/_generated/component.d.ts.map +1 -1
  9. package/dist/component/_generated/dataModel.d.ts +1 -1
  10. package/dist/component/_generated/server.d.ts +1 -1
  11. package/dist/component/apiUtils.d.ts +36 -7
  12. package/dist/component/apiUtils.d.ts.map +1 -1
  13. package/dist/component/apiUtils.js +60 -40
  14. package/dist/component/apiUtils.js.map +1 -1
  15. package/dist/component/lib.d.ts +71 -49
  16. package/dist/component/lib.d.ts.map +1 -1
  17. package/dist/component/lib.js +206 -73
  18. package/dist/component/lib.js.map +1 -1
  19. package/dist/component/schema.d.ts +11 -13
  20. package/dist/component/schema.d.ts.map +1 -1
  21. package/dist/component/schema.js +3 -10
  22. package/dist/component/schema.js.map +1 -1
  23. package/dist/debug/index.d.ts +19 -0
  24. package/dist/debug/index.d.ts.map +1 -0
  25. package/dist/debug/index.js +49 -0
  26. package/dist/debug/index.js.map +1 -0
  27. package/dist/react/index.d.ts +213 -17
  28. package/dist/react/index.d.ts.map +1 -1
  29. package/dist/react/index.js +726 -105
  30. package/dist/react/index.js.map +1 -1
  31. package/dist/shared/assemblyUrls.d.ts +10 -0
  32. package/dist/shared/assemblyUrls.d.ts.map +1 -0
  33. package/dist/shared/assemblyUrls.js +26 -0
  34. package/dist/shared/assemblyUrls.js.map +1 -0
  35. package/dist/shared/errors.d.ts +7 -0
  36. package/dist/shared/errors.d.ts.map +1 -0
  37. package/dist/shared/errors.js +10 -0
  38. package/dist/shared/errors.js.map +1 -0
  39. package/dist/shared/pollAssembly.d.ts +12 -0
  40. package/dist/shared/pollAssembly.d.ts.map +1 -0
  41. package/dist/shared/pollAssembly.js +50 -0
  42. package/dist/shared/pollAssembly.js.map +1 -0
  43. package/dist/shared/resultTypes.d.ts +37 -0
  44. package/dist/shared/resultTypes.d.ts.map +1 -0
  45. package/dist/shared/resultTypes.js +2 -0
  46. package/dist/shared/resultTypes.js.map +1 -0
  47. package/dist/shared/resultUtils.d.ts +4 -0
  48. package/dist/shared/resultUtils.d.ts.map +1 -0
  49. package/dist/shared/resultUtils.js +69 -0
  50. package/dist/shared/resultUtils.js.map +1 -0
  51. package/dist/shared/tusUpload.d.ts +13 -0
  52. package/dist/shared/tusUpload.d.ts.map +1 -0
  53. package/dist/shared/tusUpload.js +32 -0
  54. package/dist/shared/tusUpload.js.map +1 -0
  55. package/dist/test/index.d.ts +65 -0
  56. package/dist/test/index.d.ts.map +1 -0
  57. package/dist/test/index.js +8 -0
  58. package/dist/test/index.js.map +1 -0
  59. package/dist/test/nodeModules.d.ts +2 -0
  60. package/dist/test/nodeModules.d.ts.map +1 -0
  61. package/dist/test/nodeModules.js +19 -0
  62. package/dist/test/nodeModules.js.map +1 -0
  63. package/package.json +53 -15
  64. package/src/client/index.ts +141 -38
  65. package/src/component/_generated/api.ts +2 -2
  66. package/src/component/_generated/component.ts +44 -13
  67. package/src/component/_generated/dataModel.ts +1 -1
  68. package/src/component/_generated/server.ts +1 -1
  69. package/src/component/apiUtils.test.ts +195 -2
  70. package/src/component/apiUtils.ts +124 -66
  71. package/src/component/lib.test.ts +243 -7
  72. package/src/component/lib.ts +302 -90
  73. package/src/component/schema.ts +3 -13
  74. package/src/debug/index.ts +84 -0
  75. package/src/react/index.test.tsx +340 -0
  76. package/src/react/index.tsx +1105 -152
  77. package/src/react/uploadWithTus.test.tsx +192 -0
  78. package/src/shared/assemblyUrls.test.ts +71 -0
  79. package/src/shared/assemblyUrls.ts +59 -0
  80. package/src/shared/errors.ts +23 -0
  81. package/src/shared/pollAssembly.ts +65 -0
  82. package/src/shared/resultTypes.ts +44 -0
  83. package/src/shared/resultUtils.test.ts +29 -0
  84. package/src/shared/resultUtils.ts +71 -0
  85. package/src/shared/tusUpload.ts +59 -0
  86. package/src/test/index.ts +10 -0
  87. package/src/test/nodeModules.ts +19 -0
package/README.md CHANGED
@@ -1,16 +1,20 @@
1
1
  # Transloadit Convex Component
2
2
 
3
- A Convex component for creating Transloadit Assemblies, tracking their status/results, and supporting both form uploads and resumable tus uploads.
3
+ A Convex component for creating Transloadit Assemblies, handling resumable uploads with tus, and persisting status/results in Convex.
4
4
 
5
5
  ## Features
6
6
 
7
- - Create Assemblies with templates or inline steps.
8
- - Generate signed upload params for browser form uploads.
9
- - Resumable uploads via tus (client-side hook).
10
- - Webhook ingestion with signature verification.
11
- - Persist Assembly status + results in Convex.
7
+ - Create Assemblies with Templates or inline Steps.
8
+ - Resumable uploads via tus (client-side hook; form/XHR uploads are intentionally not supported).
9
+ - Webhook ingestion with signature verification (direct or queued).
10
+ - Persist Assembly status + results in Convex tables.
12
11
  - Typed API wrappers and React hooks.
13
12
 
13
+ ## Requirements
14
+
15
+ - Node.js 24+
16
+ - Yarn 4 (Corepack)
17
+
14
18
  ## Install
15
19
 
16
20
  ```bash
@@ -34,31 +38,17 @@ export default app;
34
38
 
35
39
  ### 2) Set environment variables
36
40
 
37
- Preferred names:
38
-
39
- ```bash
40
- npx convex env set TRANSLOADIT_AUTH_KEY <your_auth_key>
41
- npx convex env set TRANSLOADIT_AUTH_SECRET <your_auth_secret>
42
- ```
43
-
44
- Aliases also supported:
45
-
46
41
  ```bash
47
42
  npx convex env set TRANSLOADIT_KEY <your_auth_key>
48
43
  npx convex env set TRANSLOADIT_SECRET <your_auth_secret>
49
44
  ```
50
45
 
51
- ### 3) Create a demo template (idempotent)
46
+ ## Golden path (secure by default)
52
47
 
53
- We use the Transloadit CLI under the hood for the best DX and to avoid hand-rolling API calls.
54
-
55
- ```bash
56
- yarn template:ensure
57
- ```
58
-
59
- The script reads `TRANSLOADIT_KEY/TRANSLOADIT_SECRET` (or `TRANSLOADIT_AUTH_KEY/TRANSLOADIT_AUTH_SECRET`) from `.env`,
60
- creates or updates the template `convex-demo`, and prints the template id.
61
- Use that id as `VITE_TRANSLOADIT_TEMPLATE_ID` in `example/.env` when running the demo app.
48
+ 1. **Server-only create**: a Convex action creates the Assembly (auth secret stays server-side).
49
+ 2. **Client upload**: use `useTransloaditUppy` for resumable uploads.
50
+ 3. **Webhook ingestion**: verify the signature and `queueWebhook` for durable processing.
51
+ 4. **Realtime UI**: query status/results and render the gallery.
62
52
 
63
53
  ## Backend API
64
54
 
@@ -69,8 +59,9 @@ import { components } from "./_generated/api";
69
59
 
70
60
  export const {
71
61
  createAssembly,
72
- generateUploadParams,
73
62
  handleWebhook,
63
+ queueWebhook,
64
+ refreshAssembly,
74
65
  getAssemblyStatus,
75
66
  listAssemblies,
76
67
  listResults,
@@ -78,7 +69,23 @@ export const {
78
69
  } = makeTransloaditAPI(components.transloadit);
79
70
  ```
80
71
 
81
- Note: if you don’t supply `expires`, the component defaults it to 1 hour from now.
72
+ Note: pass `expires` in `createAssembly` when you need a custom expiry; otherwise the component defaults to 1 hour from now.
73
+
74
+ ## Data model
75
+
76
+ The component stores Transloadit metadata in two tables:
77
+
78
+ ```
79
+ assemblies 1 ──── * results
80
+ ```
81
+
82
+ - `assemblies`: one row per Transloadit Assembly (status/ok, notify URL, uploads, raw payload, etc).
83
+ - `results`: one row per output file, keyed by `assemblyId` + `stepName`, plus normalized fields (name/size/mime/url) and the raw Transloadit output object.
84
+
85
+ Lifecycle:
86
+ 1. `createAssembly` inserts the initial `assemblies` row.
87
+ 2. `handleWebhook`, `queueWebhook`, or `refreshAssembly` upserts the assembly + replaces results.
88
+ 3. `listResults` returns flattened step outputs for use in UIs.
82
89
 
83
90
  ## Webhook route
84
91
 
@@ -86,150 +93,206 @@ Transloadit sends webhooks as `multipart/form-data` with `transloadit` (JSON) an
86
93
 
87
94
  ```ts
88
95
  // convex/http.ts
89
- import { httpAction, httpRouter } from "convex/server";
96
+ import { httpRouter } from "convex/server";
97
+ import { handleWebhookRequest } from "@transloadit/convex";
90
98
  import { api } from "./_generated/api";
99
+ import { httpAction } from "./_generated/server";
91
100
 
92
101
  const http = httpRouter();
93
102
 
94
103
  http.route({
95
104
  path: "/transloadit/webhook",
96
105
  method: "POST",
97
- handler: httpAction(async (ctx, request) => {
98
- const formData = await request.formData();
99
- const rawPayload = formData.get("transloadit");
100
- const signature = formData.get("signature");
106
+ handler: httpAction((ctx, request) =>
107
+ handleWebhookRequest(request, {
108
+ mode: "queue",
109
+ runAction: (args) => ctx.runAction(api.transloadit.queueWebhook, args),
110
+ }),
111
+ ),
112
+ });
101
113
 
102
- if (typeof rawPayload !== "string") {
103
- return new Response("Missing payload", { status: 400 });
104
- }
114
+ export default http;
115
+ ```
105
116
 
106
- const payload = JSON.parse(rawPayload);
117
+ ## Client wrapper (optional)
107
118
 
108
- await ctx.runAction(api.transloadit.handleWebhook, {
109
- payload,
110
- rawBody: rawPayload,
111
- signature: typeof signature === "string" ? signature : undefined,
112
- });
119
+ Most integrations should use `makeTransloaditAPI` (above). If you prefer a class-based API
120
+ (similar to other Convex components), use `Transloadit`:
113
121
 
114
- return new Response(null, { status: 204 });
115
- }),
122
+ ```ts
123
+ import { Transloadit } from "@transloadit/convex";
124
+ import { components } from "./_generated/api";
125
+
126
+ const transloadit = new Transloadit(components.transloadit, {
127
+ authKey: process.env.TRANSLOADIT_KEY!,
128
+ authSecret: process.env.TRANSLOADIT_SECRET!,
116
129
  });
130
+ ```
117
131
 
118
- export default http;
132
+ ## React usage (Uppy)
133
+
134
+ ```tsx
135
+ import { useTransloaditUppy } from "@transloadit/convex/react";
136
+ import { api } from "../convex/_generated/api";
137
+
138
+ const { startUpload, status, results, stage } = useTransloaditUppy({
139
+ uppy,
140
+ createAssembly: api.wedding.createWeddingAssembly,
141
+ getStatus: api.transloadit.getAssemblyStatus,
142
+ listResults: api.transloadit.listResults,
143
+ refreshAssembly: api.transloadit.refreshAssembly,
144
+ });
145
+
146
+ await startUpload({
147
+ createAssemblyArgs: { guestName, uploadCode },
148
+ });
119
149
  ```
150
+ For advanced/legacy helpers (raw parsing, low-level tus uploads, polling utilities), see `docs/advanced.md`.
151
+
152
+ ## Example app (Next.js + Uppy wedding gallery)
120
153
 
121
- ### Local webhook testing with cloudflared
154
+ The `example/` app is a wedding gallery where guests upload photos + short videos. It uses Uppy on the client and Convex Auth (anonymous sign-in) to create assemblies securely. If you do not set `NEXT_PUBLIC_CONVEX_URL`, the example falls back to the in-process Convex test harness.
155
+ Uploads are stored via Transloadit directly into Cloudflare R2.
156
+ The client wiring uses the `useTransloaditUppy` hook from `@transloadit/convex/react` to keep Uppy + polling in sync.
122
157
 
123
- If you want to test webhooks locally, tunnel your Convex dev HTTP endpoint:
158
+ Quick start (local):
124
159
 
125
160
  ```bash
126
- yarn tunnel
161
+ # In repo root
162
+ export TRANSLOADIT_KEY=...
163
+ export TRANSLOADIT_SECRET=...
164
+ export TRANSLOADIT_R2_CREDENTIALS=...
165
+
166
+ # Get a public webhook URL (cloudflared is auto-downloaded if needed)
167
+ yarn tunnel --once
168
+ # Set TRANSLOADIT_NOTIFY_URL to the printed notifyUrl
169
+ export TRANSLOADIT_NOTIFY_URL=...
170
+
171
+ yarn example:dev
127
172
  ```
128
173
 
129
- Use the generated public URL as `notifyUrl` when creating Assemblies or set
130
- `VITE_TRANSLOADIT_NOTIFY_URL` for the example app.
174
+ If you want the API routes to talk to an existing Convex deployment (bypassing Convex Auth), set:
131
175
 
132
- You can also run `yarn tunnel --once` to print the URL and exit.
176
+ ```bash
177
+ export CONVEX_URL=...
178
+ export CONVEX_ADMIN_KEY=...
179
+ ```
133
180
 
134
- ### Full QA flow (template + tunnel + webhook)
181
+ The example exposes `POST /transloadit/webhook` and forwards webhooks into Convex via `queueWebhook`.
182
+ Realtime “new upload” toasts use a Convex subscription on recent assemblies.
183
+ The demo also applies a simple per-user upload limit in the Convex backend (see `example/convex/wedding.ts`).
135
184
 
136
- This runs an end-to-end webhook QA flow against Transloadit using a local webhook server
137
- and cloudflared (auto-downloaded if missing):
185
+ ### Storage (required R2 persistence)
186
+
187
+ The example uses the `/cloudflare/store` robot to write processed files into Cloudflare R2. Configure one of these:
138
188
 
139
189
  ```bash
140
- yarn qa:full
190
+ # Option A: Transloadit template credentials (recommended)
191
+ export TRANSLOADIT_R2_CREDENTIALS=...
192
+
193
+ # Option B: supply R2 details directly
194
+ export R2_BUCKET=...
195
+ export R2_ACCESS_KEY_ID=...
196
+ export R2_SECRET_ACCESS_KEY=...
197
+ export R2_ACCOUNT_ID=... # or R2_HOST
198
+ export R2_PUBLIC_URL=... # optional public URL prefix
141
199
  ```
142
200
 
143
- It prints a JSON summary including the assembly id, webhook status, and number of stored results.
201
+ The UI hides older items based on `NEXT_PUBLIC_GALLERY_RETENTION_HOURS` (default: 24) to discourage spam/abuse.
202
+ If you set `WEDDING_UPLOAD_CODE` on the Convex deployment, guests must enter the passcode before uploads can start.
144
203
 
145
- ## React usage
204
+ ### Deploy the example (Vercel + stable Convex)
146
205
 
147
- ### Form upload
206
+ For a public demo, deploy the `example/` app and point it at a stable Convex deployment.
148
207
 
149
- ```tsx
150
- import { useTransloaditUpload } from "@transloadit/convex/react";
151
- import { api } from "../convex/_generated/api";
208
+ 1. Deploy a Convex app that includes this component (stable/prod deployment).
209
+ 2. Set Vercel environment variables for the project:
210
+ - `NEXT_PUBLIC_CONVEX_URL` (point to the stable Convex deployment)
211
+ - `NEXT_PUBLIC_GALLERY_RETENTION_HOURS` (optional)
212
+ 3. Set Convex environment variables on the deployment:
213
+ - `TRANSLOADIT_KEY` and `TRANSLOADIT_SECRET`
214
+ - `TRANSLOADIT_NOTIFY_URL` (set to `https://<deployment>.convex.site/transloadit/webhook`)
215
+ - R2 credentials (see above)
216
+ - `WEDDING_UPLOAD_CODE` (optional passcode for uploads)
217
+ 4. Trigger the Vercel deploy hook (or deploy manually).
152
218
 
153
- function UploadButton() {
154
- const { upload, isUploading, progress, error } = useTransloaditUpload(
155
- api.transloadit.generateUploadParams,
156
- );
219
+ To deploy a stable Convex backend for the demo (once per environment), run:
157
220
 
158
- const handleUpload = async (file: File) => {
159
- await upload(file, {
160
- templateId: "template_id_here",
161
- onProgress: (percent) => console.log(percent),
162
- });
163
- };
221
+ ```bash
222
+ export CONVEX_DEPLOY_KEY=...
223
+ export TRANSLOADIT_KEY=...
224
+ export TRANSLOADIT_SECRET=...
164
225
 
165
- return (
166
- <div>
167
- <input type="file" onChange={(e) => handleUpload(e.target.files![0])} />
168
- {isUploading && <p>Uploading: {progress}%</p>}
169
- {error && <p>{error.message}</p>}
170
- </div>
171
- );
172
- }
226
+ yarn deploy:cloud
173
227
  ```
174
228
 
175
- ### Resumable tus upload
229
+ Once deployed, use the Vercel URL as `E2E_REMOTE_APP_URL` for `yarn verify:cloud`.
230
+ CI expects a stable Vercel production URL in the `E2E_REMOTE_APP_URL` secret on `main`.
176
231
 
177
- ```tsx
178
- import { useTransloaditTusUpload } from "@transloadit/convex/react";
179
- import { api } from "../convex/_generated/api";
232
+ ## Verification and QA
233
+
234
+ Fast checks:
180
235
 
181
- function TusUpload() {
182
- const { upload, isUploading, progress } = useTransloaditTusUpload(
183
- api.transloadit.createAssembly,
184
- );
236
+ ```bash
237
+ yarn check
238
+ ```
185
239
 
186
- const handleUpload = async (file: File) => {
187
- await upload(file, {
188
- templateId: "template_id_here",
189
- });
190
- };
240
+ This runs format, lint, typecheck, and unit tests. For a full verification run:
191
241
 
192
- return (
193
- <div>
194
- <input type="file" onChange={(e) => handleUpload(e.target.files![0])} />
195
- {isUploading && <p>Uploading: {progress}%</p>}
196
- </div>
197
- );
198
- }
242
+ ```bash
243
+ yarn verify
199
244
  ```
200
245
 
201
- ### Reactive status/results
246
+ Additional commands:
202
247
 
203
- ```tsx
204
- import { useAssemblyStatus, useTransloaditFiles } from "@transloadit/convex/react";
205
- import { api } from "../convex/_generated/api";
248
+ - `yarn lint` (Biome)
249
+ - `yarn format` (Biome write)
250
+ - `yarn typecheck` (tsc)
251
+ - `yarn test` (Vitest unit tests)
252
+ - `yarn verify:local` (runs the Next.js wedding example + uploads an image + video)
253
+ - `yarn verify:cloud` (runs the browser flow against a deployed Next.js app)
254
+ - `yarn deploy:cloud` (deploys a stable Convex backend for the demo app)
255
+ - `yarn build` (tsc build + emit package json)
256
+
257
+ Notes:
258
+ - `yarn tunnel` is a support tool, not verification.
259
+ - CI should run non-mutating checks; local `yarn check` may format/fix.
260
+ - `yarn verify:local` needs `TRANSLOADIT_KEY`, `TRANSLOADIT_SECRET`, `TRANSLOADIT_NOTIFY_URL`, and R2 credentials.
261
+ - `yarn verify:cloud` needs `E2E_REMOTE_APP_URL`.
262
+ - Set `TRANSLOADIT_DEBUG=1` to enable verbose verify logs.
206
263
 
207
- function AssemblyStatus({ assemblyId }: { assemblyId: string }) {
208
- const status = useAssemblyStatus(api.transloadit.getAssemblyStatus, assemblyId);
209
- const results = useTransloaditFiles(api.transloadit.listResults, {
210
- assemblyId,
211
- });
264
+ ## Component test helpers
212
265
 
213
- if (!status) return null;
214
- return (
215
- <div>
216
- <p>Status: {status.ok}</p>
217
- <p>Results: {results?.length ?? 0}</p>
218
- </div>
219
- );
220
- }
266
+ For `convex-test`, you can use the built-in helper:
267
+
268
+ ```ts
269
+ import { createTransloaditTest } from "@transloadit/convex/test";
270
+
271
+ const t = createTransloaditTest();
221
272
  ```
222
273
 
223
- ## Smoke test
274
+ ## Generated files
275
+
276
+ `src/component/_generated` is Convex codegen output. It is checked in so tests and component consumers have stable API references. If you change component functions or schemas, regenerate with Convex codegen (for example via `npx convex dev` or `npx convex codegen`) and commit the updated files.
277
+
278
+ ## Release process
279
+
280
+ Releases are automated via Changesets + GitHub Actions and published to npm using OIDC (Trusted Publisher).
224
281
 
225
- Create a `.env` file in the repo root and run:
282
+ 1. Ensure CI is green on `main`.
283
+ 2. Run local checks:
226
284
 
227
285
  ```bash
228
- yarn smoke
286
+ yarn check
229
287
  ```
230
288
 
231
- Expected output: a JSON blob containing `assemblyId` and (when available) `tusUrl`.
289
+ 3. Add a changeset describing the release:
290
+
291
+ ```bash
292
+ yarn changeset
293
+ ```
232
294
 
233
- ## Example app
295
+ 4. Push the changeset to `main`. The Changesets workflow will open a “Version Packages” PR.
296
+ 5. Merge the “Version Packages” PR. This will publish to npm and tag the release.
234
297
 
235
- See `example/README.md` for setup and usage.
298
+ Note: This package is 0.x, so breaking changes are allowed. Use changesets to document them clearly.