@devultur/convex 1.0.2 → 1.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.
- package/README.md +151 -5
- package/dist/bin/init.js +9 -2
- package/dist/{chunk-FHWT3GD6.js → chunk-GKLFRSGI.js} +2 -0
- package/dist/component/_generated/api.d.ts +12 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +55 -1
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +1 -1
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/lock.d.ts +33 -0
- package/dist/component/lock.d.ts.map +1 -0
- package/dist/component/lock.js +47 -0
- package/dist/component/lock.js.map +1 -0
- package/dist/component/lockValidators.d.ts +25 -0
- package/dist/component/lockValidators.d.ts.map +1 -0
- package/dist/component/lockValidators.js +14 -0
- package/dist/component/lockValidators.js.map +1 -0
- package/dist/component/mediaClient.d.ts +3 -0
- package/dist/component/mediaClient.d.ts.map +1 -0
- package/dist/component/mediaClient.js +7 -0
- package/dist/component/mediaClient.js.map +1 -0
- package/dist/component/public.d.ts +78 -0
- package/dist/component/public.d.ts.map +1 -1
- package/dist/component/public.js +83 -19
- package/dist/component/public.js.map +1 -1
- package/dist/component/schema.d.ts +12 -1
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +13 -1
- package/dist/component/schema.js.map +1 -1
- package/dist/component/uploadKeys.d.ts +14 -0
- package/dist/component/uploadKeys.d.ts.map +1 -0
- package/dist/component/uploadKeys.js +23 -0
- package/dist/component/uploadKeys.js.map +1 -0
- package/dist/component/uploads.d.ts +119 -0
- package/dist/component/uploads.d.ts.map +1 -0
- package/dist/component/uploads.js +106 -0
- package/dist/component/uploads.js.map +1 -0
- package/dist/component/workerErrors.d.ts +17 -0
- package/dist/component/workerErrors.d.ts.map +1 -0
- package/dist/component/workerErrors.js +41 -0
- package/dist/component/workerErrors.js.map +1 -0
- package/dist/index.d.ts +147 -6
- package/dist/index.js +102 -4
- package/dist/react.d.ts +18 -5
- package/dist/react.js +35 -13
- package/dist/validators.d.ts +5 -3
- package/dist/validators.js +1 -1
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @devultur/convex
|
|
2
2
|
|
|
3
3
|
[Convex](https://convex.dev) component for Devultur media: stores media records and watch progress in isolated
|
|
4
|
-
tables, exposes upload, token, transcode and delete functions for your app,
|
|
4
|
+
tables, exposes upload, token, transcode and delete functions for your app, serves the transcode webhook route, and
|
|
5
5
|
ships typed validators and React bindings.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
@@ -11,8 +11,26 @@ pnpm add @devultur/convex
|
|
|
11
11
|
pnpm exec devultur-init
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
`devultur-init` adds the component to `convex/convex.config.ts
|
|
15
|
-
functions, and prints the Convex env
|
|
14
|
+
`devultur-init` adds the component to `convex/convex.config.ts` (mounted with `httpPrefix: "/devultur/"`, see
|
|
15
|
+
[Webhook route](#webhook-route)), creates `convex/devultur.ts` with the exposed functions, and prints the Convex env
|
|
16
|
+
vars to set (your media Worker's URL and credential).
|
|
17
|
+
|
|
18
|
+
## Webhook route
|
|
19
|
+
|
|
20
|
+
The component receives transcode and captions events on its own HTTP route, `POST /webhook`. Convex serves a
|
|
21
|
+
component's HTTP routes only when the app mounts the component with an `httpPrefix`:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// convex/convex.config.ts
|
|
25
|
+
app.use(devultur, { httpPrefix: "/devultur/", env: { /* see below */ } });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
With that prefix the route is `https://<deployment>.convex.site/devultur/webhook`. You don't need an `http.ts` entry
|
|
29
|
+
for it. `processVideo` sends the Worker that URL as the `callbackUrl`, and it follows the prefix you choose (with
|
|
30
|
+
`httpPrefix: "/media/"` it is `https://<deployment>.convex.site/media/webhook`).
|
|
31
|
+
|
|
32
|
+
Without `httpPrefix` the route doesn't exist: the Worker's events get `404`, the media record never leaves
|
|
33
|
+
`queued`, and every transcode fails with a timeout.
|
|
16
34
|
|
|
17
35
|
## Example
|
|
18
36
|
|
|
@@ -49,6 +67,94 @@ function CoverUpload({ onSaved }) {
|
|
|
49
67
|
}
|
|
50
68
|
```
|
|
51
69
|
|
|
70
|
+
## Upload progress
|
|
71
|
+
|
|
72
|
+
`upload(file, { onProgress, signal })` reports the PUT's progress and resolves to the stored reference:
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
const { upload } = useDevultur();
|
|
76
|
+
|
|
77
|
+
const media = await upload(file, {
|
|
78
|
+
onProgress: ({ loadedBytes, totalBytes, fraction }) => setPercent(Math.round(fraction * 100)),
|
|
79
|
+
signal: controller.signal, // aborts the PUT; upload() rejects with an AbortError
|
|
80
|
+
});
|
|
81
|
+
media.key; // the uploaded object's key ("private/image/<id>/<name>"; a video's raw file)
|
|
82
|
+
await saveCover({ cover: media }); // a DevulturMedia: stores as-is in a dv.media() field, key included
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The PUT goes through `XMLHttpRequest` (`fetch` reports no upload progress), in parts for large files. `key` is an
|
|
86
|
+
optional field of `DevulturMedia` and of `dv.media()` (since 1.1.0), so references stored without it stay valid. It
|
|
87
|
+
is informational: delivery URLs are still derived from `id`, `kind`, `visibility` and `filename`.
|
|
88
|
+
|
|
89
|
+
## Media library
|
|
90
|
+
|
|
91
|
+
Every upload, whatever its kind, gets a record in the component's `media` table once its bytes are stored, so
|
|
92
|
+
`listMedia` lists your images, files and videos:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// convex/library.ts
|
|
96
|
+
import { Devultur, mediaFromRecord } from "@devultur/convex";
|
|
97
|
+
import { query } from "./_generated/server";
|
|
98
|
+
import { components } from "./_generated/api";
|
|
99
|
+
|
|
100
|
+
const devultur = new Devultur(components.devultur);
|
|
101
|
+
|
|
102
|
+
export const images = query({
|
|
103
|
+
args: {},
|
|
104
|
+
handler: async (ctx) => {
|
|
105
|
+
// Check who's asking first: listMedia returns every record, of every user.
|
|
106
|
+
const records = await devultur.listMedia(ctx, { type: "image" }); // or "video" | "file", or no filter
|
|
107
|
+
return records.map((record) => ({ record, media: mediaFromRecord(record) }));
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`listMedia` reads every record at once: fine for a handful, unbounded for a library. Page a library with
|
|
113
|
+
`listMediaPage`, newest first, on a `by_type` index:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { paginationOptsValidator } from "convex/server";
|
|
117
|
+
import { v } from "convex/values";
|
|
118
|
+
|
|
119
|
+
export const imagesPage = query({
|
|
120
|
+
args: { paginationOpts: paginationOptsValidator },
|
|
121
|
+
handler: async (ctx, { paginationOpts }) => {
|
|
122
|
+
// Check who's asking first.
|
|
123
|
+
return devultur.listMediaPage(ctx, { type: "image", paginationOpts });
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
// React: usePaginatedQuery(api.library.imagesPage, {}, { initialNumItems: 24 })
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`Devultur.exposeApi` also returns a `listMediaPage` query (`{ type?, paginationOpts }`). It lists every user's media,
|
|
130
|
+
so export it only when every caller may see the whole library.
|
|
131
|
+
|
|
132
|
+
A record is written only after the Worker confirms the object exists, so an abandoned upload never shows up:
|
|
133
|
+
|
|
134
|
+
1. After its PUT, the client calls `confirmUpload({ key, filename, width, height })`. The component asks the Worker
|
|
135
|
+
for the object (a `HEAD`) and records its size and content type. `useDevultur().upload()` and `useMediaUpload` do
|
|
136
|
+
this for you when the `api` you pass `<DevulturProvider>` has `confirmUpload` (add it to the functions you export
|
|
137
|
+
from `Devultur.exposeApi` in `convex/devultur.ts`). From your server: `devultur.confirmUpload(ctx, { key })`.
|
|
138
|
+
2. `createUploadUrl` also schedules the same check for 25 hours later, past the longest an upload can take. An upload
|
|
139
|
+
whose client never confirmed is recorded then; one that was never stored, or was deleted, isn't.
|
|
140
|
+
|
|
141
|
+
A record's `key` is the media id for every kind (what `getMedia` takes), and `objectKey` is the uploaded object's key.
|
|
142
|
+
`confirmUpload` only accepts upload keys (`{visibility}/{image|file}/{id}/{name}`, `{visibility}/video/{id}/raw/{name}`)
|
|
143
|
+
and is idempotent: a video's transcode state stays as it is.
|
|
144
|
+
|
|
145
|
+
`confirmUpload`, like every function `exposeApi` returns (and the `devultur-init` template exports), has no auth of
|
|
146
|
+
its own: anyone who can call your Convex deployment can call it. Wrap it with your app's auth, as you would the other
|
|
147
|
+
exposed functions: export your own action that checks `ctx.auth` and then calls `devultur.confirmUpload(ctx, { key })`.
|
|
148
|
+
The caller only names the key: the record's visibility, size and content type come from the Worker's `HEAD` of the
|
|
149
|
+
object, never from the arguments.
|
|
150
|
+
|
|
151
|
+
`deleteMedia` removes an image's or a file's record once
|
|
152
|
+
the Worker deleted the object (a delete refused by a lock keeps it). `deleteVideo` removes a video's.
|
|
153
|
+
|
|
154
|
+
Records from before 1.1.0 (and a video only `processVideo` recorded) have no `visibility` or `objectKey`;
|
|
155
|
+
`mediaFromRecord` returns `null` for them. Nothing needs migrating: the new fields are optional. `listMedia` doesn't
|
|
156
|
+
report an object's lock: read it from an action with `devultur.getLock(ctx, record.objectKey)`.
|
|
157
|
+
|
|
52
158
|
## Using a self-hosted Worker
|
|
53
159
|
|
|
54
160
|
The component talks to any Worker built with `createWorker()` from
|
|
@@ -83,8 +189,10 @@ npx convex env set DEVULTUR_API_URL https://media.example.com
|
|
|
83
189
|
npx convex env set DEVULTUR_SECRET <the Worker's WORKER_AUTH_SECRET>
|
|
84
190
|
```
|
|
85
191
|
|
|
86
|
-
**Webhooks.**
|
|
87
|
-
|
|
192
|
+
**Webhooks.** Keep the `httpPrefix` in `app.use()`: without it Convex doesn't serve the component's route and every
|
|
193
|
+
transcode times out (see [Webhook route](#webhook-route)). Nothing else needs configuring. `processVideo` sends the
|
|
194
|
+
Worker a `callbackUrl` that points at the component's route, `https://<deployment>.convex.site/devultur/webhook`
|
|
195
|
+
(your app's `CONVEX_SITE_URL` followed by the prefix and `webhook`). The transcode runner reports to the Worker with
|
|
88
196
|
`TRANSCODE_WEBHOOK_SECRET`. The Worker then forwards each event to the `callbackUrl` with
|
|
89
197
|
`Authorization: Bearer <WORKER_AUTH_SECRET>`. The component accepts an event only when that bearer equals
|
|
90
198
|
`DEVULTUR_SECRET`. If `DEVULTUR_SECRET` is wrong or missing, the component answers `401` to every event. Keep
|
|
@@ -95,6 +203,44 @@ webhooks with the `webhookCredential` your plugin sets in the request state (or
|
|
|
95
203
|
none; see [Plugins](https://github.com/devultur/devultur/blob/main/docs/guides/plugins.md)). Set `DEVULTUR_SECRET`
|
|
96
204
|
to that value, and make sure your plugin also accepts it as the bearer of the component's calls.
|
|
97
205
|
|
|
206
|
+
## Errors
|
|
207
|
+
|
|
208
|
+
When the media Worker refuses a request, component actions throw a `ConvexError` whose `data` is
|
|
209
|
+
`{ code, message, status, details? }`. A plain `Error` would lose these fields across the component boundary. `code` is
|
|
210
|
+
the Worker's stable code: for example `retention_active`, `compliance_extend_only`, `lock_conflict`,
|
|
211
|
+
`lock_privilege_required`, `file_too_large`, or `worker_error` when the Worker sent none. `details` carries the rest of
|
|
212
|
+
the Worker's answer, such as the `{ key, mode, until, hold }` of a `retention_active`.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { devulturErrorOf } from "@devultur/convex";
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
await devultur.setLock(ctx, key, { until });
|
|
219
|
+
} catch (error) {
|
|
220
|
+
const refusal = devulturErrorOf(error); // null when it isn't a Worker refusal
|
|
221
|
+
if (refusal?.code === "compliance_extend_only") {
|
|
222
|
+
// the date can only move later
|
|
223
|
+
}
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`isDevulturError(error)` is the matching type guard. Errors that aren't Worker refusals, such as a network failure,
|
|
229
|
+
are rethrown unchanged.
|
|
230
|
+
|
|
231
|
+
## Object lock
|
|
232
|
+
|
|
233
|
+
- `devultur.createUploadUrl(ctx, { filename, contentType, lock, hold })` locks a file from its first write.
|
|
234
|
+
- `devultur.getLock(ctx, key)` and `devultur.setLock(ctx, key, { mode, until, period, derivatives })` read and change
|
|
235
|
+
a file's retention.
|
|
236
|
+
- `devultur.setHold(ctx, keys)` and `devultur.releaseHold(ctx, keys)` set and release holds.
|
|
237
|
+
|
|
238
|
+
These are server-side calls: `exposeApi` never lets a browser choose or change a lock. Its `createUploadUrl` applies
|
|
239
|
+
`config.upload.lock` instead. A bulk hold by prefix runs on your server with `@devultur/core`'s
|
|
240
|
+
`createWorkerClient(...).lock.holdAll({ prefix })`. The
|
|
241
|
+
[Retention and holds](https://github.com/devultur/devultur/blob/main/docs/guides/object-lock.md) guide covers the model,
|
|
242
|
+
what's enforced and the caveats.
|
|
243
|
+
|
|
98
244
|
## Documentation
|
|
99
245
|
|
|
100
246
|
Guides and API reference: [github.com/devultur/devultur/tree/main/docs](https://github.com/devultur/devultur/tree/main/docs).
|
package/dist/bin/init.js
CHANGED
|
@@ -37,6 +37,10 @@ export default app;
|
|
|
37
37
|
let content = readFileSync(configPath, "utf-8");
|
|
38
38
|
if (content.includes("@devultur/convex")) {
|
|
39
39
|
log("\u23ED\uFE0F convex.config.ts already has devultur \u2014 skipping");
|
|
40
|
+
if (!content.includes("httpPrefix")) {
|
|
41
|
+
log("\u26A0\uFE0F Mount it with an httpPrefix, or Convex doesn't serve its webhook route and every transcode times out:");
|
|
42
|
+
log(' app.use(devultur, { httpPrefix: "/devultur/", env: { ... } })');
|
|
43
|
+
}
|
|
40
44
|
return;
|
|
41
45
|
}
|
|
42
46
|
if (!content.includes("defineApp")) {
|
|
@@ -72,6 +76,7 @@ import { components } from "./_generated/api";
|
|
|
72
76
|
export const {
|
|
73
77
|
issueViewerToken,
|
|
74
78
|
createUploadUrl,
|
|
79
|
+
confirmUpload,
|
|
75
80
|
deleteMedia,
|
|
76
81
|
deleteVideo,
|
|
77
82
|
processVideo,
|
|
@@ -112,6 +117,8 @@ console.log(`
|
|
|
112
117
|
<App />
|
|
113
118
|
</DevulturProvider>
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
Transcode webhooks: Convex serves the component's webhook route only because
|
|
121
|
+
convex.config.ts mounts it with httpPrefix "/devultur/". The route is
|
|
122
|
+
POST https://<deployment>.convex.site/devultur/webhook
|
|
123
|
+
and processVideo sends it to the Worker as the callbackUrl (no changes needed in your http.ts).
|
|
117
124
|
`);
|
|
@@ -4,6 +4,8 @@ var mediaFields = {
|
|
|
4
4
|
id: v.string(),
|
|
5
5
|
visibility: v.union(v.literal("public"), v.literal("private")),
|
|
6
6
|
filename: v.optional(v.string()),
|
|
7
|
+
// The uploaded object's key, as `upload()` returns it. Optional: references stored before it stay valid.
|
|
8
|
+
key: v.optional(v.string()),
|
|
7
9
|
width: v.optional(v.number()),
|
|
8
10
|
height: v.optional(v.number())
|
|
9
11
|
};
|
|
@@ -7,11 +7,23 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import type * as http from "../http.js";
|
|
10
|
+
import type * as lock from "../lock.js";
|
|
11
|
+
import type * as lockValidators from "../lockValidators.js";
|
|
12
|
+
import type * as mediaClient from "../mediaClient.js";
|
|
10
13
|
import type * as public_ from "../public.js";
|
|
14
|
+
import type * as uploadKeys from "../uploadKeys.js";
|
|
15
|
+
import type * as uploads from "../uploads.js";
|
|
16
|
+
import type * as workerErrors from "../workerErrors.js";
|
|
11
17
|
import type { ApiFromModules, FilterApi, FunctionReference } from "convex/server";
|
|
12
18
|
declare const fullApi: ApiFromModules<{
|
|
13
19
|
http: typeof http;
|
|
20
|
+
lock: typeof lock;
|
|
21
|
+
lockValidators: typeof lockValidators;
|
|
22
|
+
mediaClient: typeof mediaClient;
|
|
14
23
|
public: typeof public_;
|
|
24
|
+
uploadKeys: typeof uploadKeys;
|
|
25
|
+
uploads: typeof uploads;
|
|
26
|
+
workerErrors: typeof workerErrors;
|
|
15
27
|
}>;
|
|
16
28
|
/**
|
|
17
29
|
* A utility for referencing Convex functions in your app's public API.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/api.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,IAAI,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,KAAK,OAAO,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/api.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,IAAI,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,KAAK,IAAI,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,KAAK,cAAc,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,KAAK,WAAW,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,KAAK,OAAO,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,KAAK,UAAU,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,KAAK,OAAO,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,KAAK,YAAY,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAGvB,QAAA,MAAM,OAAO,EAAE,cAAc,CAAC;IAC5B,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,cAAc,EAAE,OAAO,cAAc,CAAC;IACtC,WAAW,EAAE,OAAO,WAAW,CAAC;IAChC,MAAM,EAAE,OAAO,OAAO,CAAC;IACvB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,YAAY,CAAC;CACnC,CAAiB,CAAC;AAEnB;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG,EAAE,SAAS,CACzB,OAAO,OAAO,EACd,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CACjB,CAAC;AAElB;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,SAAS,CAC9B,OAAO,OAAO,EACd,iBAAiB,CAAC,GAAG,EAAE,UAAU,CAAC,CACnB,CAAC;AAElB,eAAO,MAAM,UAAU,EAAqC,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/component/_generated/api.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/component/_generated/api.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAgBH,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE1D,MAAM,OAAO,GASR,MAAa,CAAC;AAEnB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,GAAG,GAGZ,MAAa,CAAC;AAElB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAGjB,MAAa,CAAC;AAElB,MAAM,CAAC,MAAM,UAAU,GAAG,iBAAiB,EAAmB,CAAC"}
|
|
@@ -19,6 +19,26 @@ import type { FunctionReference } from "convex/server";
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
export type ComponentApi<Name extends string | undefined = string | undefined> = {
|
|
22
|
+
lock: {
|
|
23
|
+
getLock: FunctionReference<"action", "internal", {
|
|
24
|
+
key: string;
|
|
25
|
+
}, any, Name>;
|
|
26
|
+
releaseHold: FunctionReference<"action", "internal", {
|
|
27
|
+
keys: Array<string>;
|
|
28
|
+
}, any, Name>;
|
|
29
|
+
setHold: FunctionReference<"action", "internal", {
|
|
30
|
+
keys: Array<string>;
|
|
31
|
+
}, any, Name>;
|
|
32
|
+
setLock: FunctionReference<"action", "internal", {
|
|
33
|
+
bypassGovernance?: boolean;
|
|
34
|
+
derivatives?: "inherit" | "original-only";
|
|
35
|
+
key: string;
|
|
36
|
+
mode?: "governance" | "compliance";
|
|
37
|
+
period?: number;
|
|
38
|
+
remove?: true;
|
|
39
|
+
until?: string;
|
|
40
|
+
}, any, Name>;
|
|
41
|
+
};
|
|
22
42
|
public: {
|
|
23
43
|
createMedia: FunctionReference<"mutation", "internal", {
|
|
24
44
|
contentType: string;
|
|
@@ -30,12 +50,24 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
30
50
|
createUploadUrl: FunctionReference<"action", "internal", {
|
|
31
51
|
contentType: string;
|
|
32
52
|
filename: string;
|
|
53
|
+
hold?: boolean;
|
|
54
|
+
lock?: {
|
|
55
|
+
hold?: boolean;
|
|
56
|
+
retention?: {
|
|
57
|
+
derivatives?: "inherit" | "original-only";
|
|
58
|
+
mode: "governance" | "compliance";
|
|
59
|
+
period?: number;
|
|
60
|
+
until?: string;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
visibility?: "public" | "private";
|
|
33
64
|
}, any, Name>;
|
|
34
65
|
deleteMediaAction: FunctionReference<"action", "internal", {
|
|
35
66
|
key: string;
|
|
36
67
|
}, any, Name>;
|
|
37
68
|
deleteVideo: FunctionReference<"action", "internal", {
|
|
38
69
|
videoId: string;
|
|
70
|
+
visibility?: "public" | "private";
|
|
39
71
|
}, any, Name>;
|
|
40
72
|
getCourseProgress: FunctionReference<"query", "internal", {
|
|
41
73
|
userId: string;
|
|
@@ -53,6 +85,17 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
53
85
|
listMedia: FunctionReference<"query", "internal", {
|
|
54
86
|
type?: "video" | "image" | "file";
|
|
55
87
|
}, any, Name>;
|
|
88
|
+
listMediaPage: FunctionReference<"query", "internal", {
|
|
89
|
+
paginationOpts: {
|
|
90
|
+
cursor: string | null;
|
|
91
|
+
endCursor?: string | null;
|
|
92
|
+
id?: number;
|
|
93
|
+
maximumBytesRead?: number;
|
|
94
|
+
maximumRowsRead?: number;
|
|
95
|
+
numItems: number;
|
|
96
|
+
};
|
|
97
|
+
type?: "video" | "image" | "file";
|
|
98
|
+
}, any, Name>;
|
|
56
99
|
loadProgress: FunctionReference<"query", "internal", {
|
|
57
100
|
userId: string;
|
|
58
101
|
videoId: string;
|
|
@@ -78,16 +121,19 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
78
121
|
}, any, Name>;
|
|
79
122
|
updateMedia: FunctionReference<"mutation", "internal", {
|
|
80
123
|
captionLocales?: Array<string>;
|
|
124
|
+
captionsPending?: boolean;
|
|
81
125
|
duration?: number;
|
|
126
|
+
failureReason?: "invalid_input" | "unsupported_codec" | "timeout" | "capacity" | "internal";
|
|
82
127
|
key: string;
|
|
83
128
|
playlistUrl?: string;
|
|
129
|
+
retriable?: boolean;
|
|
84
130
|
thumbnailKey?: string;
|
|
85
131
|
transcodeError?: string;
|
|
86
132
|
transcodeProgress?: {
|
|
87
133
|
current: number;
|
|
88
134
|
total: number;
|
|
89
135
|
};
|
|
90
|
-
transcodeStatus?: "transcoding" | "captioning" | "ready" | "failed";
|
|
136
|
+
transcodeStatus?: "queued" | "transcoding" | "captioning" | "ready" | "failed";
|
|
91
137
|
}, any, Name>;
|
|
92
138
|
upsertCaption: FunctionReference<"mutation", "internal", {
|
|
93
139
|
locale: string;
|
|
@@ -96,5 +142,13 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
96
142
|
vttUrl: string;
|
|
97
143
|
}, any, Name>;
|
|
98
144
|
};
|
|
145
|
+
uploads: {
|
|
146
|
+
confirmUpload: FunctionReference<"action", "internal", {
|
|
147
|
+
filename?: string;
|
|
148
|
+
height?: number;
|
|
149
|
+
key: string;
|
|
150
|
+
width?: number;
|
|
151
|
+
}, any, Name>;
|
|
152
|
+
};
|
|
99
153
|
};
|
|
100
154
|
//# sourceMappingURL=component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/component.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,IAC3E;IACE,MAAM,EAAE;QACN,WAAW,EAAE,iBAAiB,CAC5B,UAAU,EACV,UAAU,EACV;YACE,WAAW,EAAE,MAAM,CAAC;YACpB,QAAQ,EAAE,MAAM,CAAC;YACjB,GAAG,EAAE,MAAM,CAAC;YACZ,SAAS,EAAE,MAAM,CAAC;YAClB,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;SAClC,EACD,GAAG,EACH,IAAI,CACL,CAAC;QACF,eAAe,EAAE,iBAAiB,CAChC,QAAQ,EACR,UAAU,EACV;
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/component.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,8 +12,8 @@ import type { DataModel } from "./dataModel.js";
|
|
|
12
12
|
* Typesafe environment variables declared in `convex.config.ts`.
|
|
13
13
|
*/
|
|
14
14
|
type Env = {
|
|
15
|
-
readonly DEVULTUR_SECRET: string;
|
|
16
15
|
readonly DEVULTUR_API_URL: string;
|
|
16
|
+
readonly DEVULTUR_SECRET: string;
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
19
|
* Define a query in this Convex app's public API.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAUvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;GAEG;AACH,KAAK,GAAG,GAAG;IACT,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAUvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;GAEG;AACH,KAAK,GAAG,GAAG;IACT,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,EAAE,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAgB,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,YAAY,CAAC,SAAS,EAAE,UAAU,CACxC,CAAC;AAEvB;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAmB,CAAC;AAE9E;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAC3C,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAiB,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,SAAS,EAAE,UAAU,CACzC,CAAC;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,EAAE,iBAAqC,CAAC;AAC/D,eAAO,MAAM,GAAG,EAAE,GAAmC,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;AAExD;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;AAE9D;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const getLock: import("convex/server").RegisteredAction<"public", {
|
|
2
|
+
key: string;
|
|
3
|
+
}, Promise<import("@devultur/core").LockView>>;
|
|
4
|
+
export declare const setLock: import("convex/server").RegisteredAction<"public", {
|
|
5
|
+
mode?: "governance" | "compliance" | undefined;
|
|
6
|
+
until?: string | undefined;
|
|
7
|
+
period?: number | undefined;
|
|
8
|
+
derivatives?: "inherit" | "original-only" | undefined;
|
|
9
|
+
remove?: true | undefined;
|
|
10
|
+
bypassGovernance?: boolean | undefined;
|
|
11
|
+
key: string;
|
|
12
|
+
}, Promise<import("@devultur/core").LockView>>;
|
|
13
|
+
export declare const setHold: import("convex/server").RegisteredAction<"public", {
|
|
14
|
+
keys: string[];
|
|
15
|
+
}, Promise<{
|
|
16
|
+
updated: string[];
|
|
17
|
+
skipped: {
|
|
18
|
+
key: string;
|
|
19
|
+
reason: string;
|
|
20
|
+
}[];
|
|
21
|
+
pageSize: number;
|
|
22
|
+
}>>;
|
|
23
|
+
export declare const releaseHold: import("convex/server").RegisteredAction<"public", {
|
|
24
|
+
keys: string[];
|
|
25
|
+
}, Promise<{
|
|
26
|
+
updated: string[];
|
|
27
|
+
skipped: {
|
|
28
|
+
key: string;
|
|
29
|
+
reason: string;
|
|
30
|
+
}[];
|
|
31
|
+
pageSize: number;
|
|
32
|
+
}>>;
|
|
33
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/component/lock.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,OAAO;;8CAGlB,CAAC;AAEH,eAAO,MAAM,OAAO;;;;;;;;8CAWlB,CAAC;AAcH,eAAO,MAAM,OAAO;;;;;aARG,MAAM;gBAAU,MAAM;;;GAW3C,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;aAbD,MAAM;gBAAU,MAAM;;;GAgB3C,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Object lock from the app's server: read and change an object's retention, and set or release holds on a list of
|
|
2
|
+
// keys. These are component functions, so only the app's own functions can call them (never a browser). A bulk hold
|
|
3
|
+
// by prefix stays on the app's server, with `@devultur/core`'s `createWorkerClient(...).lock.holdAll({ prefix })`.
|
|
4
|
+
// A refusal throws a `ConvexError` whose `data.code` is the Worker's (`retention_active`, `compliance_extend_only`,
|
|
5
|
+
// `lock_privilege_required`, `lock_conflict`, ...); see workerErrors.ts.
|
|
6
|
+
import { HOLD_PAGE_SIZE } from "@devultur/core";
|
|
7
|
+
import { v } from "convex/values";
|
|
8
|
+
import { derivativesValidator, retentionModeValidator } from "./lockValidators";
|
|
9
|
+
import { getMediaClient } from "./mediaClient";
|
|
10
|
+
import { workerAction } from "./workerErrors";
|
|
11
|
+
const keysValidator = v.array(v.string());
|
|
12
|
+
export const getLock = workerAction({
|
|
13
|
+
args: { key: v.string() },
|
|
14
|
+
handler: async (_ctx, args) => getMediaClient().lock.get(args.key),
|
|
15
|
+
});
|
|
16
|
+
export const setLock = workerAction({
|
|
17
|
+
args: {
|
|
18
|
+
key: v.string(),
|
|
19
|
+
mode: v.optional(retentionModeValidator),
|
|
20
|
+
until: v.optional(v.string()),
|
|
21
|
+
period: v.optional(v.number()),
|
|
22
|
+
derivatives: v.optional(derivativesValidator),
|
|
23
|
+
remove: v.optional(v.literal(true)),
|
|
24
|
+
bypassGovernance: v.optional(v.boolean()),
|
|
25
|
+
},
|
|
26
|
+
handler: async (_ctx, { key, ...options }) => getMediaClient().lock.set(key, options),
|
|
27
|
+
});
|
|
28
|
+
/** Holds every key, a page of {@link HOLD_PAGE_SIZE} at a time; returns what changed and what didn't. */
|
|
29
|
+
async function changeHolds(keys, hold) {
|
|
30
|
+
const lock = getMediaClient().lock;
|
|
31
|
+
const updated = [];
|
|
32
|
+
const skipped = [];
|
|
33
|
+
for await (const page of hold ? lock.holdAll(keys) : lock.releaseAll(keys)) {
|
|
34
|
+
updated.push(...page.updated);
|
|
35
|
+
skipped.push(...page.skipped);
|
|
36
|
+
}
|
|
37
|
+
return { updated, skipped, pageSize: HOLD_PAGE_SIZE };
|
|
38
|
+
}
|
|
39
|
+
export const setHold = workerAction({
|
|
40
|
+
args: { keys: keysValidator },
|
|
41
|
+
handler: async (_ctx, args) => changeHolds(args.keys, true),
|
|
42
|
+
});
|
|
43
|
+
export const releaseHold = workerAction({
|
|
44
|
+
args: { keys: keysValidator },
|
|
45
|
+
handler: async (_ctx, args) => changeHolds(args.keys, false),
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/component/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const retentionModeValidator: import("convex/values").VUnion<"governance" | "compliance", [import("convex/values").VLiteral<"governance", "required">, import("convex/values").VLiteral<"compliance", "required">], "required", never>;
|
|
2
|
+
export declare const derivativesValidator: import("convex/values").VUnion<"inherit" | "original-only", [import("convex/values").VLiteral<"inherit", "required">, import("convex/values").VLiteral<"original-only", "required">], "required", never>;
|
|
3
|
+
export declare const objectLockValidator: import("convex/values").VObject<{
|
|
4
|
+
retention?: {
|
|
5
|
+
until?: string | undefined;
|
|
6
|
+
period?: number | undefined;
|
|
7
|
+
derivatives?: "inherit" | "original-only" | undefined;
|
|
8
|
+
mode: "governance" | "compliance";
|
|
9
|
+
} | undefined;
|
|
10
|
+
hold?: boolean | undefined;
|
|
11
|
+
}, {
|
|
12
|
+
retention: import("convex/values").VObject<{
|
|
13
|
+
until?: string | undefined;
|
|
14
|
+
period?: number | undefined;
|
|
15
|
+
derivatives?: "inherit" | "original-only" | undefined;
|
|
16
|
+
mode: "governance" | "compliance";
|
|
17
|
+
} | undefined, {
|
|
18
|
+
mode: import("convex/values").VUnion<"governance" | "compliance", [import("convex/values").VLiteral<"governance", "required">, import("convex/values").VLiteral<"compliance", "required">], "required", never>;
|
|
19
|
+
until: import("convex/values").VString<string | undefined, "optional">;
|
|
20
|
+
period: import("convex/values").VFloat64<number | undefined, "optional">;
|
|
21
|
+
derivatives: import("convex/values").VUnion<"inherit" | "original-only" | undefined, [import("convex/values").VLiteral<"inherit", "required">, import("convex/values").VLiteral<"original-only", "required">], "optional", never>;
|
|
22
|
+
}, "optional", "mode" | "until" | "period" | "derivatives">;
|
|
23
|
+
hold: import("convex/values").VBoolean<boolean | undefined, "optional">;
|
|
24
|
+
}, "required", "retention" | "hold" | "retention.mode" | "retention.until" | "retention.period" | "retention.derivatives">;
|
|
25
|
+
//# sourceMappingURL=lockValidators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockValidators.d.ts","sourceRoot":"","sources":["../../src/component/lockValidators.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,0MAA4D,CAAC;AAChG,eAAO,MAAM,oBAAoB,0MAA4D,CAAC;AAE9F,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;0HAU9B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Validators of the object lock arguments (the shapes of `@devultur/core`'s `ObjectLock` and `SetLockOptions`).
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
export const retentionModeValidator = v.union(v.literal("governance"), v.literal("compliance"));
|
|
4
|
+
export const derivativesValidator = v.union(v.literal("inherit"), v.literal("original-only"));
|
|
5
|
+
export const objectLockValidator = v.object({
|
|
6
|
+
retention: v.optional(v.object({
|
|
7
|
+
mode: retentionModeValidator,
|
|
8
|
+
until: v.optional(v.string()),
|
|
9
|
+
period: v.optional(v.number()),
|
|
10
|
+
derivatives: v.optional(derivativesValidator),
|
|
11
|
+
})),
|
|
12
|
+
hold: v.optional(v.boolean()),
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=lockValidators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockValidators.js","sourceRoot":"","sources":["../../src/component/lockValidators.ts"],"names":[],"mappings":"AAAA,gHAAgH;AAChH,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;AAChG,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;AAE9F,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,SAAS,EAAE,CAAC,CAAC,QAAQ,CACpB,CAAC,CAAC,MAAM,CAAC;QACR,IAAI,EAAE,sBAAsB;QAC5B,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC7B,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC9B,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC;KAC7C,CAAC,CACF;IACD,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;CAC7B,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mediaClient.d.ts","sourceRoot":"","sources":["../../src/component/mediaClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGvE,wBAAgB,cAAc,IAAI,YAAY,CAE7C"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// The component's client of the media Worker, from the env the app mounts the component with.
|
|
2
|
+
import { createWorkerClient } from "@devultur/core";
|
|
3
|
+
import { env } from "./_generated/server";
|
|
4
|
+
export function getMediaClient() {
|
|
5
|
+
return createWorkerClient(env.DEVULTUR_SECRET, env.DEVULTUR_API_URL);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=mediaClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mediaClient.js","sourceRoot":"","sources":["../../src/component/mediaClient.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,OAAO,EAAE,kBAAkB,EAAqB,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAE1C,MAAM,UAAU,cAAc;IAC7B,OAAO,kBAAkB,CAAC,GAAG,CAAC,eAAe,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;AACtE,CAAC"}
|