@devultur/convex 1.4.2 → 1.5.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 +100 -5
- package/dist/{chunk-JZYKPLI4.js → chunk-RDVBE3G3.js} +20 -1
- package/dist/component/_generated/api.d.ts +0 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +0 -1
- package/dist/component/_generated/component.js.map +1 -1
- package/dist/component/_generated/dataModel.d.ts +0 -1
- package/dist/component/_generated/dataModel.js.map +1 -1
- package/dist/component/_generated/server.d.ts +0 -1
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/convex.config.d.ts +0 -1
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/http.d.ts +0 -1
- package/dist/component/http.js.map +1 -1
- package/dist/component/library.d.ts +0 -1
- package/dist/component/library.js.map +1 -1
- package/dist/component/lock.d.ts +0 -1
- package/dist/component/lock.js.map +1 -1
- package/dist/component/lockValidators.d.ts +0 -1
- package/dist/component/lockValidators.js.map +1 -1
- package/dist/component/mediaClient.d.ts +0 -1
- package/dist/component/mediaClient.js.map +1 -1
- package/dist/component/mediaMeta.d.ts +0 -1
- package/dist/component/mediaMeta.js.map +1 -1
- package/dist/component/metadata.d.ts +0 -1
- package/dist/component/metadata.js.map +1 -1
- package/dist/component/public.d.ts +0 -1
- package/dist/component/public.js.map +1 -1
- package/dist/component/schema.d.ts +0 -1
- package/dist/component/schema.js.map +1 -1
- package/dist/component/uploadKeys.d.ts +0 -1
- package/dist/component/uploadKeys.js.map +1 -1
- package/dist/component/uploads.d.ts +0 -1
- package/dist/component/uploads.js.map +1 -1
- package/dist/component/workerErrors.d.ts +0 -1
- package/dist/component/workerErrors.js.map +1 -1
- package/dist/index.d.ts +33 -12
- package/dist/index.js +22 -4
- package/dist/locales/es-formal.d.ts +7 -3
- package/dist/locales/es-formal.js +6 -3
- package/dist/locales/es.d.ts +7 -3
- package/dist/locales/es.js +5 -3
- package/dist/{preview-labels-B-2mra1h.d.ts → preview-labels-C2nUUY_f.d.ts} +1 -1
- package/dist/react.d.ts +133 -92
- package/dist/react.js +310 -235
- package/dist/types-C0ruHUcu.d.ts +196 -0
- package/package.json +4 -3
- package/dist/component/_generated/api.d.ts.map +0 -1
- package/dist/component/_generated/component.d.ts.map +0 -1
- package/dist/component/_generated/dataModel.d.ts.map +0 -1
- package/dist/component/_generated/server.d.ts.map +0 -1
- package/dist/component/convex.config.d.ts.map +0 -1
- package/dist/component/http.d.ts.map +0 -1
- package/dist/component/library.d.ts.map +0 -1
- package/dist/component/lock.d.ts.map +0 -1
- package/dist/component/lockValidators.d.ts.map +0 -1
- package/dist/component/mediaClient.d.ts.map +0 -1
- package/dist/component/mediaMeta.d.ts.map +0 -1
- package/dist/component/metadata.d.ts.map +0 -1
- package/dist/component/public.d.ts.map +0 -1
- package/dist/component/schema.d.ts.map +0 -1
- package/dist/component/uploadKeys.d.ts.map +0 -1
- package/dist/component/uploads.d.ts.map +0 -1
- package/dist/component/workerErrors.d.ts.map +0 -1
- package/dist/types-LrusGVDf.d.ts +0 -62
package/README.md
CHANGED
|
@@ -107,8 +107,13 @@ export const { createUploadUrl, confirmUpload, issueViewerToken, getMedia, listM
|
|
|
107
107
|
signed-in user can't read or overwrite someone else's progress. Return `{ userId, canActForOthers: true }` for a
|
|
108
108
|
caller who may (an admin resetting a learner's progress). A guard that returns no `userId` keeps the browser's, as
|
|
109
109
|
`exposeApi` does: return one whenever the progress functions are exported.
|
|
110
|
+
- The guard may also return a `rewrite`, `{ name, args }`: each key of `args` replaces the caller's argument of that
|
|
111
|
+
name, on your server, before the function runs (see [Upload visibility](#upload-visibility)). `name` is the guarded
|
|
112
|
+
operation's, so `args` are typed for it; a rewrite named for another operation throws. The rewritten arguments
|
|
113
|
+
aren't validated again.
|
|
110
114
|
- `visibility: "internal"` registers them as internal functions, for your own actions and mutations to call.
|
|
111
|
-
- `config` takes what `Devultur.exposeApi` does (transcode preset, caption locales, upload lock
|
|
115
|
+
- `config` takes what `Devultur.exposeApi` does (transcode preset, caption locales, upload lock and default
|
|
116
|
+
visibility).
|
|
112
117
|
- Each function keeps its arguments and result types, so `api.devultur.confirmUpload` is typed as before and
|
|
113
118
|
`<DevulturProvider api={api.devultur}>` takes them as it takes `exposeApi`'s.
|
|
114
119
|
|
|
@@ -117,6 +122,44 @@ export const { createUploadUrl, confirmUpload, issueViewerToken, getMedia, listM
|
|
|
117
122
|
> use it only for a deployment where every caller is trusted, or move to `defineDevulturFunctions`, which takes the
|
|
118
123
|
> same `config`.
|
|
119
124
|
|
|
125
|
+
## Upload visibility
|
|
126
|
+
|
|
127
|
+
`config.upload.visibility` sets the visibility of an upload whose caller passes none. Without it, the Worker's default
|
|
128
|
+
by type applies.
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
export const { createUploadUrl, confirmUpload } = defineDevulturFunctions(devultur, {
|
|
132
|
+
guard,
|
|
133
|
+
config: { upload: { visibility: "public" } }, // a site whose uploads are public unless the browser asks otherwise
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The browser can still pass `visibility` (`upload(file, { visibility })`, `useMediaUpload({ visibility })`). To decide
|
|
138
|
+
it on your server instead, rewrite it in the guard: the guard's `rewrite` replaces the browser's arguments.
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
guard: async (ctx, operation) => {
|
|
142
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
143
|
+
if (!identity) throw new ConvexError("Sign in first");
|
|
144
|
+
// Members' uploads are always private, whatever the browser asked for.
|
|
145
|
+
if (operation.name === "createUploadUrl") {
|
|
146
|
+
return { userId: identity.subject, rewrite: { name: "createUploadUrl", args: { visibility: "private" } } };
|
|
147
|
+
}
|
|
148
|
+
return { userId: identity.subject };
|
|
149
|
+
},
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Which one wins, from first to last:
|
|
153
|
+
|
|
154
|
+
1. The guard's `rewrite.args.visibility`.
|
|
155
|
+
2. The caller's `visibility`.
|
|
156
|
+
3. `config.upload.visibility`.
|
|
157
|
+
4. The Worker's default by type.
|
|
158
|
+
|
|
159
|
+
The guard sees the caller's arguments as they arrived (`operation.args.visibility` is `undefined` when the browser
|
|
160
|
+
passed none): the config's default is applied after the guard. `config.upload.lock` isn't an argument, so neither
|
|
161
|
+
the browser nor the guard's `rewrite` change it.
|
|
162
|
+
|
|
120
163
|
## Upload progress
|
|
121
164
|
|
|
122
165
|
`upload(file, { onProgress, signal })` reports the PUT's progress and resolves to the stored reference:
|
|
@@ -175,7 +218,7 @@ ignored. Your action returns what the component's action returns: `{ token, expi
|
|
|
175
218
|
### Display-only apps
|
|
176
219
|
|
|
177
220
|
An app that only shows media (no uploads, no deletes) uses `<DevulturViewerProvider>`, which needs only the base URL
|
|
178
|
-
and a viewer token action:
|
|
221
|
+
and, for private media, a viewer token action:
|
|
179
222
|
|
|
180
223
|
```tsx
|
|
181
224
|
import { DevulturViewerProvider, Preview } from "@devultur/convex/react";
|
|
@@ -194,13 +237,49 @@ import { DevulturViewerProvider, Preview } from "@devultur/convex/react";
|
|
|
194
237
|
that need the `api` (`useDevulturApi`, `useMediaUpload`, `<MediaLibrary>`, `<MediaPicker>`), throw a
|
|
195
238
|
`DevulturCapabilityError` whose `code` is `capability_unavailable`.
|
|
196
239
|
|
|
240
|
+
Like `<DevulturProvider>`, it issues the viewer token as soon as it mounts. With `lazyToken`, it issues it only when
|
|
241
|
+
a private media is shown, and renews it from then on: while every media on the page is public, it calls nothing,
|
|
242
|
+
`tokenStatus` stays `"idle"` and `token` stays `null`. Code that reads `token` itself then calls
|
|
243
|
+
`useDevultur().requestToken()` first; the built-in components and hooks do it for private media.
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
<DevulturViewerProvider baseUrl={MEDIA_URL} issueViewerToken={api.devultur.issueViewerToken} lazyToken>
|
|
247
|
+
<Gallery />
|
|
248
|
+
</DevulturViewerProvider>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
When all the media is public, leave `issueViewerToken` out. The provider then needs no Convex client and issues
|
|
252
|
+
nothing:
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
<DevulturViewerProvider baseUrl={MEDIA_URL}>
|
|
256
|
+
<Image media={post.cover} alt={post.title} />
|
|
257
|
+
</DevulturViewerProvider>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
A private media shown under it fails with `token_unavailable`, and `tokenError` says the provider has no
|
|
261
|
+
`issueViewerToken`.
|
|
262
|
+
|
|
263
|
+
### Components outside a provider
|
|
264
|
+
|
|
265
|
+
`useDevultur()` throws outside `<DevulturProvider>` and `<DevulturViewerProvider>`. A component that also renders
|
|
266
|
+
without one uses `useDevulturOptional()`, which returns the same value, or `null` outside both:
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
function Avatar({ media, fallbackUrl }) {
|
|
270
|
+
const devultur = useDevulturOptional();
|
|
271
|
+
if (!devultur || !media) return <img src={fallbackUrl} alt="" />;
|
|
272
|
+
return <Image media={media} alt="" width={64} />;
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
197
276
|
### When the viewer token can't be issued
|
|
198
277
|
|
|
199
278
|
`useDevultur()` tells where the viewer token stands:
|
|
200
279
|
|
|
201
280
|
```tsx
|
|
202
281
|
const { tokenStatus, tokenError, retryToken } = useDevultur();
|
|
203
|
-
// tokenStatus: "idle" | "loading" | "ready" | "error"
|
|
282
|
+
// tokenStatus: "idle" (not asked for yet) | "loading" | "ready" | "error"
|
|
204
283
|
if (tokenStatus === "error") return <button onClick={retryToken}>Try again</button>; // tokenError has why
|
|
205
284
|
```
|
|
206
285
|
|
|
@@ -210,6 +289,10 @@ there until `retryToken()`: a failed issuance isn't retried on its own. Meanwhil
|
|
|
210
289
|
`<Preview>` passes it to its `fallback`, `<PdfThumbnail>` and `<Image>` render their `fallback` (`<Image onLoadError>`
|
|
211
290
|
receives it), and `<DownloadLink onDownloadError>` receives it on click. They load once `retryToken()` gets a token.
|
|
212
291
|
|
|
292
|
+
`<Preview>`'s video plays in `<VideoPlayer>` from `@devultur/react`, with its playback metrics on: anonymous, no
|
|
293
|
+
cookies, sent to the media host (see [Playback metrics](https://github.com/devultur/devultur/blob/main/docs/guides/video-player.md#playback-metrics)).
|
|
294
|
+
`<Preview metrics={false}>` turns them off. A private video shows `loading` until its viewer token is issued.
|
|
295
|
+
|
|
213
296
|
`<Preview>`'s video re-issues the token once when the delivery answers the playlist or a segment with a 401
|
|
214
297
|
`session_mismatch`; after that, or on any other 401, it renders its `fallback` with `session_mismatch` or
|
|
215
298
|
`token_unavailable`. `<VideoPlayer onError>` (from `@devultur/react`) reports these failures when you render the
|
|
@@ -658,8 +741,8 @@ the same name.
|
|
|
658
741
|
|
|
659
742
|
`@devultur/convex/locales/es` addresses the reader with «tú» ("Vuelve a intentarlo"). For «usted» ("Vuelva a
|
|
660
743
|
intentarlo", "No tiene permiso…"), import the same names from `@devultur/convex/locales/es-formal`: it exports exactly
|
|
661
|
-
what `locales/es` does (error, attachment, preview, library and
|
|
662
|
-
address the reader.
|
|
744
|
+
what `locales/es` does (error, attachment, preview, library, picker and video player labels), and differs only in the
|
|
745
|
+
strings that address the reader.
|
|
663
746
|
|
|
664
747
|
```tsx
|
|
665
748
|
import { describeDevulturError } from "@devultur/convex";
|
|
@@ -685,6 +768,18 @@ Pass a partial map to change some messages and keep the rest, or a full one for
|
|
|
685
768
|
describeDevulturError(error, { ...spanishLabels, lock_conflict: "Alguien más acaba de cambiarlo." });
|
|
686
769
|
```
|
|
687
770
|
|
|
771
|
+
### Video player labels
|
|
772
|
+
|
|
773
|
+
`<VideoPlayer>` from `@devultur/react` takes `labels` like the components here do. Both Spanish locales export
|
|
774
|
+
`videoPlayerLabels`:
|
|
775
|
+
|
|
776
|
+
```tsx
|
|
777
|
+
import { VideoPlayer } from "@devultur/react";
|
|
778
|
+
import { videoPlayerLabels } from "@devultur/convex/locales/es";
|
|
779
|
+
|
|
780
|
+
<VideoPlayer {...useVideoProps(lesson.video)} labels={videoPlayerLabels} />;
|
|
781
|
+
```
|
|
782
|
+
|
|
688
783
|
## Object lock
|
|
689
784
|
|
|
690
785
|
- `devultur.createUploadUrl(ctx, { filename, contentType, lock, hold })` locks a file from its first write.
|
|
@@ -175,11 +175,30 @@ var mediaPickerLabels = {
|
|
|
175
175
|
removed: (name) => `${name} se quit\xF3.`,
|
|
176
176
|
cleared: "Se limpi\xF3 la selecci\xF3n."
|
|
177
177
|
};
|
|
178
|
+
var videoPlayerLabels = {
|
|
179
|
+
player: "Reproductor de video",
|
|
180
|
+
controls: "Controles del video",
|
|
181
|
+
play: "Reproducir",
|
|
182
|
+
pause: "Pausar",
|
|
183
|
+
seek: "Posici\xF3n del video",
|
|
184
|
+
mute: "Silenciar",
|
|
185
|
+
unmute: "Activar el sonido",
|
|
186
|
+
volume: "Volumen",
|
|
187
|
+
captions: "Subt\xEDtulos",
|
|
188
|
+
captionsOff: "Desactivados",
|
|
189
|
+
quality: "Calidad del video",
|
|
190
|
+
qualityAuto: "Autom\xE1tica",
|
|
191
|
+
speed: "Velocidad de reproducci\xF3n",
|
|
192
|
+
speedNormal: "Normal",
|
|
193
|
+
fullscreen: "Pantalla completa",
|
|
194
|
+
exitFullscreen: "Salir de pantalla completa"
|
|
195
|
+
};
|
|
178
196
|
|
|
179
197
|
export {
|
|
180
198
|
devulturErrorLabels,
|
|
181
199
|
previewLabels,
|
|
182
200
|
attachmentLabels,
|
|
183
201
|
mediaLibraryLabels,
|
|
184
|
-
mediaPickerLabels
|
|
202
|
+
mediaPickerLabels,
|
|
203
|
+
videoPlayerLabels
|
|
185
204
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/component/_generated/api.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAmBH,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE1D,MAAM,OAAO,GAYR,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"}
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/component/_generated/api.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAmBH,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE1D,MAAM,OAAO,GAYR,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","sourcesContent":["/* eslint-disable */\n/**\n * Generated `api` utility.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport type * as http from \"../http.js\";\nimport type * as library from \"../library.js\";\nimport type * as lock from \"../lock.js\";\nimport type * as lockValidators from \"../lockValidators.js\";\nimport type * as mediaClient from \"../mediaClient.js\";\nimport type * as mediaMeta from \"../mediaMeta.js\";\nimport type * as metadata from \"../metadata.js\";\nimport type * as public_ from \"../public.js\";\nimport type * as uploadKeys from \"../uploadKeys.js\";\nimport type * as uploads from \"../uploads.js\";\nimport type * as workerErrors from \"../workerErrors.js\";\n\nimport type {\n ApiFromModules,\n FilterApi,\n FunctionReference,\n} from \"convex/server\";\nimport { anyApi, componentsGeneric } from \"convex/server\";\n\nconst fullApi: ApiFromModules<{\n http: typeof http;\n library: typeof library;\n lock: typeof lock;\n lockValidators: typeof lockValidators;\n mediaClient: typeof mediaClient;\n mediaMeta: typeof mediaMeta;\n metadata: typeof metadata;\n public: typeof public_;\n uploadKeys: typeof uploadKeys;\n uploads: typeof uploads;\n workerErrors: typeof workerErrors;\n}> = anyApi as any;\n\n/**\n * A utility for referencing Convex functions in your app's public API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = api.myModule.myFunction;\n * ```\n */\nexport const api: FilterApi<\n typeof fullApi,\n FunctionReference<any, \"public\">\n> = anyApi as any;\n\n/**\n * A utility for referencing Convex functions in your app's internal API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = internal.myModule.myFunction;\n * ```\n */\nexport const internal: FilterApi<\n typeof fullApi,\n FunctionReference<any, \"internal\">\n> = anyApi as any;\n\nexport const components = componentsGeneric() as unknown as {};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../../src/component/_generated/component.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG"}
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../../src/component/_generated/component.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG","sourcesContent":["/* eslint-disable */\n/**\n * Generated `ComponentApi` utility.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport type { FunctionReference } from \"convex/server\";\n\n/**\n * A utility for referencing a Convex component's exposed API.\n *\n * Useful when expecting a parameter like `components.myComponent`.\n * Usage:\n * ```ts\n * async function myFunction(ctx: QueryCtx, component: ComponentApi) {\n * return ctx.runQuery(component.someFile.someQuery, { ...args });\n * }\n * ```\n */\nexport type ComponentApi<Name extends string | undefined = string | undefined> =\n {\n library: {\n backfillSearchText: FunctionReference<\n \"mutation\",\n \"internal\",\n { batchSize?: number },\n any,\n Name\n >;\n searchMedia: FunctionReference<\n \"query\",\n \"internal\",\n {\n paginationOpts: {\n cursor: string | null;\n endCursor?: string | null;\n id?: number;\n maximumBytesRead?: number;\n maximumRowsRead?: number;\n numItems: number;\n };\n search: string;\n type?: \"video\" | \"image\" | \"file\";\n },\n any,\n Name\n >;\n };\n lock: {\n getLock: FunctionReference<\n \"action\",\n \"internal\",\n { key: string },\n any,\n Name\n >;\n releaseHold: FunctionReference<\n \"action\",\n \"internal\",\n { keys: Array<string> },\n any,\n Name\n >;\n setHold: FunctionReference<\n \"action\",\n \"internal\",\n { keys: Array<string> },\n any,\n Name\n >;\n setLock: FunctionReference<\n \"action\",\n \"internal\",\n {\n bypassGovernance?: boolean;\n derivatives?: \"inherit\" | \"original-only\";\n key: string;\n mode?: \"governance\" | \"compliance\";\n period?: number;\n remove?: true;\n until?: string;\n },\n any,\n Name\n >;\n };\n metadata: {\n updateMediaMeta: FunctionReference<\n \"mutation\",\n \"internal\",\n { alt?: string; key: string; tags?: Array<string>; title?: string },\n any,\n Name\n >;\n };\n public: {\n createMedia: FunctionReference<\n \"mutation\",\n \"internal\",\n {\n contentType: string;\n filename: string;\n key: string;\n sizeBytes: number;\n type: \"video\" | \"image\" | \"file\";\n },\n any,\n Name\n >;\n createUploadUrl: FunctionReference<\n \"action\",\n \"internal\",\n {\n contentType: string;\n filename: string;\n hold?: boolean;\n lock?: {\n hold?: boolean;\n retention?: {\n derivatives?: \"inherit\" | \"original-only\";\n mode: \"governance\" | \"compliance\";\n period?: number;\n until?: string;\n };\n };\n visibility?: \"public\" | \"private\";\n },\n any,\n Name\n >;\n deleteMediaAction: FunctionReference<\n \"action\",\n \"internal\",\n { key: string },\n any,\n Name\n >;\n deleteVideo: FunctionReference<\n \"action\",\n \"internal\",\n { videoId: string; visibility?: \"public\" | \"private\" },\n any,\n Name\n >;\n getCourseProgress: FunctionReference<\n \"query\",\n \"internal\",\n { userId: string; videoIds: Array<string> },\n any,\n Name\n >;\n getMediaByKey: FunctionReference<\n \"query\",\n \"internal\",\n { key: string },\n any,\n Name\n >;\n issueViewerToken: FunctionReference<\n \"action\",\n \"internal\",\n { sessionSecret?: string; userId?: string },\n any,\n Name\n >;\n listCaptionsByMedia: FunctionReference<\n \"query\",\n \"internal\",\n { mediaKey: string },\n any,\n Name\n >;\n listMedia: FunctionReference<\n \"query\",\n \"internal\",\n { type?: \"video\" | \"image\" | \"file\" },\n any,\n Name\n >;\n listMediaPage: FunctionReference<\n \"query\",\n \"internal\",\n {\n paginationOpts: {\n cursor: string | null;\n endCursor?: string | null;\n id?: number;\n maximumBytesRead?: number;\n maximumRowsRead?: number;\n numItems: number;\n };\n type?: \"video\" | \"image\" | \"file\";\n },\n any,\n Name\n >;\n loadProgress: FunctionReference<\n \"query\",\n \"internal\",\n { userId: string; videoId: string },\n any,\n Name\n >;\n markComplete: FunctionReference<\n \"mutation\",\n \"internal\",\n { userId: string; videoId: string },\n any,\n Name\n >;\n processVideo: FunctionReference<\n \"action\",\n \"internal\",\n {\n callbackUrl?: string;\n key: string;\n locales?: Array<string>;\n preset?: string;\n },\n any,\n Name\n >;\n resetProgress: FunctionReference<\n \"mutation\",\n \"internal\",\n { userId: string; videoId: string },\n any,\n Name\n >;\n saveProgress: FunctionReference<\n \"mutation\",\n \"internal\",\n { position: number; userId: string; videoId: string },\n any,\n Name\n >;\n updateMedia: FunctionReference<\n \"mutation\",\n \"internal\",\n {\n captionLocales?: Array<string>;\n captionsPending?: boolean;\n duration?: number;\n failureReason?:\n | \"invalid_input\"\n | \"unsupported_codec\"\n | \"timeout\"\n | \"capacity\"\n | \"internal\";\n key: string;\n playlistUrl?: string;\n retriable?: boolean;\n thumbnailKey?: string;\n transcodeError?: string;\n transcodeProgress?: { current: number; total: number };\n transcodeStatus?:\n | \"queued\"\n | \"transcoding\"\n | \"captioning\"\n | \"ready\"\n | \"failed\";\n },\n any,\n Name\n >;\n upsertCaption: FunctionReference<\n \"mutation\",\n \"internal\",\n { locale: string; mediaKey: string; srtUrl?: string; vttUrl: string },\n any,\n Name\n >;\n };\n uploads: {\n confirmUpload: FunctionReference<\n \"action\",\n \"internal\",\n {\n filename?: string;\n height?: number;\n key: string;\n pageCount?: number;\n width?: number;\n },\n any,\n Name\n >;\n };\n };\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataModel.js","sourceRoot":"","sources":["../../../src/component/_generated/dataModel.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG"}
|
|
1
|
+
{"version":3,"file":"dataModel.js","sourceRoot":"","sources":["../../../src/component/_generated/dataModel.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG","sourcesContent":["/* eslint-disable */\n/**\n * Generated data model types.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport type {\n DataModelFromSchemaDefinition,\n DocumentByName,\n TableNamesInDataModel,\n SystemTableNames,\n} from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\nimport schema from \"../schema.js\";\n\n/**\n * The names of all of your Convex tables.\n */\nexport type TableNames = TableNamesInDataModel<DataModel>;\n\n/**\n * The type of a document stored in Convex.\n *\n * @typeParam TableName - A string literal type of the table name (like \"users\").\n */\nexport type Doc<TableName extends TableNames> = DocumentByName<\n DataModel,\n TableName\n>;\n\n/**\n * An identifier for a document in Convex.\n *\n * Convex documents are uniquely identified by their `Id`, which is accessible\n * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).\n *\n * Documents can be loaded using `db.get(tableName, id)` in query and mutation functions.\n *\n * IDs are just strings at runtime, but this type can be used to distinguish them from other\n * strings when type checking.\n *\n * @typeParam TableName - A string literal type of the table name (like \"users\").\n */\nexport type Id<TableName extends TableNames | SystemTableNames> =\n GenericId<TableName>;\n\n/**\n * A type describing your Convex data model.\n *\n * This type includes information about what tables you have, the type of\n * documents stored in those tables, and the indexes defined on them.\n *\n * This type is used to parameterize methods like `queryGeneric` and\n * `mutationGeneric` to make them type-safe.\n */\nexport type DataModel = DataModelFromSchemaDefinition<typeof schema>;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAaH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAWvB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,KAAK,GAAsC,YAAY,CAAC;AAErE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GACxB,oBAAoB,CAAC;AAEvB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAyC,eAAe,CAAC;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,uBAAuB,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,MAAM,GAAuC,aAAa,CAAC;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GACzB,qBAAqB,CAAC;AAExB;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAsB,iBAAiB,CAAC;AAC/D,MAAM,CAAC,MAAM,GAAG,GAAQ,OAAO,CAAC,GAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAaH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAWvB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,KAAK,GAAsC,YAAY,CAAC;AAErE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GACxB,oBAAoB,CAAC;AAEvB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAyC,eAAe,CAAC;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,uBAAuB,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,MAAM,GAAuC,aAAa,CAAC;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GACzB,qBAAqB,CAAC;AAExB;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAsB,iBAAiB,CAAC;AAC/D,MAAM,CAAC,MAAM,GAAG,GAAQ,OAAO,CAAC,GAAqB,CAAC","sourcesContent":["/* eslint-disable */\n/**\n * Generated utilities for implementing server-side Convex query and mutation functions.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport type {\n ActionBuilder,\n HttpActionBuilder,\n MutationBuilder,\n QueryBuilder,\n GenericActionCtx,\n GenericMutationCtx,\n GenericQueryCtx,\n GenericDatabaseReader,\n GenericDatabaseWriter,\n} from \"convex/server\";\nimport {\n actionGeneric,\n httpActionGeneric,\n queryGeneric,\n mutationGeneric,\n internalActionGeneric,\n internalMutationGeneric,\n internalQueryGeneric,\n} from \"convex/server\";\nimport type { DataModel } from \"./dataModel.js\";\n\n/**\n * Typesafe environment variables declared in `convex.config.ts`.\n */\ntype Env = {\n readonly DEVULTUR_API_URL: string;\n readonly DEVULTUR_SECRET: string;\n};\n\n/**\n * Define a query in this Convex app's public API.\n *\n * This function will be allowed to read your Convex database and will be accessible from the client.\n *\n * @param func - The query function. It receives a {@link QueryCtx} as its first argument.\n * @returns The wrapped query. Include this as an `export` to name it and make it accessible.\n */\nexport const query: QueryBuilder<DataModel, \"public\"> = queryGeneric;\n\n/**\n * Define a query that is only accessible from other Convex functions (but not from the client).\n *\n * This function will be allowed to read from your Convex database. It will not be accessible from the client.\n *\n * @param func - The query function. It receives a {@link QueryCtx} as its first argument.\n * @returns The wrapped query. Include this as an `export` to name it and make it accessible.\n */\nexport const internalQuery: QueryBuilder<DataModel, \"internal\"> =\n internalQueryGeneric;\n\n/**\n * Define a mutation in this Convex app's public API.\n *\n * This function will be allowed to modify your Convex database and will be accessible from the client.\n *\n * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.\n * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.\n */\nexport const mutation: MutationBuilder<DataModel, \"public\"> = mutationGeneric;\n\n/**\n * Define a mutation that is only accessible from other Convex functions (but not from the client).\n *\n * This function will be allowed to modify your Convex database. It will not be accessible from the client.\n *\n * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.\n * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.\n */\nexport const internalMutation: MutationBuilder<DataModel, \"internal\"> =\n internalMutationGeneric;\n\n/**\n * Define an action in this Convex app's public API.\n *\n * An action is a function which can execute any JavaScript code, including non-deterministic\n * code and code with side-effects, like calling third-party services.\n * They can be run in Convex's JavaScript environment or in Node.js using the \"use node\" directive.\n * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.\n *\n * @param func - The action. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped action. Include this as an `export` to name it and make it accessible.\n */\nexport const action: ActionBuilder<DataModel, \"public\"> = actionGeneric;\n\n/**\n * Define an action that is only accessible from other Convex functions (but not from the client).\n *\n * @param func - The function. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped function. Include this as an `export` to name it and make it accessible.\n */\nexport const internalAction: ActionBuilder<DataModel, \"internal\"> =\n internalActionGeneric;\n\n/**\n * Define an HTTP action.\n *\n * The wrapped function will be used to respond to HTTP requests received\n * by a Convex deployment if the requests matches the path and method where\n * this action is routed. Be sure to route your httpAction in `convex/http.js`.\n *\n * @param func - The function. It receives an {@link ActionCtx} as its first argument\n * and a Fetch API `Request` object as its second.\n * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.\n */\nexport const httpAction: HttpActionBuilder = httpActionGeneric;\nexport const env: Env = process.env as unknown as Env;\n\n/**\n * A set of services for use within Convex query functions.\n *\n * The query context is passed as the first argument to any Convex query\n * function run on the server.\n *\n * If you're using code generation, use the `QueryCtx` type in `convex/_generated/server.d.ts` instead.\n */\nexport type QueryCtx = GenericQueryCtx<DataModel>;\n\n/**\n * A set of services for use within Convex mutation functions.\n *\n * The mutation context is passed as the first argument to any Convex mutation\n * function run on the server.\n *\n * If you're using code generation, use the `MutationCtx` type in `convex/_generated/server.d.ts` instead.\n */\nexport type MutationCtx = GenericMutationCtx<DataModel>;\n\n/**\n * A set of services for use within Convex action functions.\n *\n * The action context is passed as the first argument to any Convex action\n * function run on the server.\n */\nexport type ActionCtx = GenericActionCtx<DataModel>;\n\n/**\n * An interface to read from the database within Convex query functions.\n *\n * The two entry points are {@link DatabaseReader.get}, which fetches a single\n * document by its {@link Id}, or {@link DatabaseReader.query}, which starts\n * building a query.\n */\nexport type DatabaseReader = GenericDatabaseReader<DataModel>;\n\n/**\n * An interface to read from and write to the database within Convex mutation\n * functions.\n *\n * Convex guarantees that all writes within a single mutation are\n * executed atomically, so you never have to worry about partial writes leaving\n * your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control)\n * for the guarantees Convex provides your functions.\n */\nexport type DatabaseWriter = GenericDatabaseWriter<DataModel>;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convex.config.js","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,MAAM,SAAS,GAAG,eAAe,CAAC,UAAU,EAAE;IAC7C,GAAG,EAAE;QACJ,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;QAC3B,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE;KAC5B;CACD,CAAC,CAAC;AAEH,eAAe,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"convex.config.js","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,MAAM,SAAS,GAAG,eAAe,CAAC,UAAU,EAAE;IAC7C,GAAG,EAAE;QACJ,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;QAC3B,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE;KAC5B;CACD,CAAC,CAAC;AAEH,eAAe,SAAS,CAAC","sourcesContent":["import { defineComponent } from \"convex/server\";\nimport { v } from \"convex/values\";\n\nconst component = defineComponent(\"devultur\", {\n\tenv: {\n\t\tDEVULTUR_SECRET: v.string(),\n\t\tDEVULTUR_API_URL: v.string(),\n\t},\n});\n\nexport default component;\n"]}
|
package/dist/component/http.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/component/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;AAE1B,IAAI,CAAC,KAAK,CAAC;IACV,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;QAC1C,gFAAgF;QAChF,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;YAC5E,OAAO,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACtD,CAAC;QAED,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;YACjE,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,IAAI,QAAQ,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACrD,CAAC;IACF,CAAC,CAAC;CACF,CAAC,CAAC;AAEH,eAAe,IAAI,CAAC"}
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/component/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;AAE1B,IAAI,CAAC,KAAK,CAAC;IACV,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;QAC1C,gFAAgF;QAChF,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;YAC5E,OAAO,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACtD,CAAC;QAED,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;YACjE,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,IAAI,QAAQ,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACrD,CAAC;IACF,CAAC,CAAC;CACF,CAAC,CAAC;AAEH,eAAe,IAAI,CAAC","sourcesContent":["import { isBearerOf } from \"@devultur/core\";\nimport { httpRouter } from \"convex/server\";\nimport { internal } from \"./_generated/api\";\nimport { env, httpAction } from \"./_generated/server\";\n\nconst http = httpRouter();\n\nhttp.route({\n\tpath: \"/webhook\",\n\tmethod: \"POST\",\n\thandler: httpAction(async (ctx, request) => {\n\t\t// Fails closed: with `DEVULTUR_SECRET` unset or empty no request is authorized.\n\t\tif (!isBearerOf(request.headers.get(\"Authorization\"), env.DEVULTUR_SECRET)) {\n\t\t\treturn new Response(\"Unauthorized\", { status: 401 });\n\t\t}\n\n\t\ttry {\n\t\t\tconst event = await request.json();\n\t\t\tawait ctx.runMutation(internal.public.handleWebhookEvent, event);\n\t\t\treturn new Response(\"OK\", { status: 200 });\n\t\t} catch {\n\t\t\treturn new Response(\"Bad Request\", { status: 400 });\n\t\t}\n\t}),\n});\n\nexport default http;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"library.js","sourceRoot":"","sources":["../../src/component/library.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"library.js","sourceRoot":"","sources":["../../src/component/library.ts"],"names":[],"mappings":"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","sourcesContent":["// The media library: search over the records' `searchText` (title + filename), and the one-off backfill that gives\n// records from before 1.4.0 the `searchText` they were written without.\nimport { type PaginationOptions, type PaginationResult, paginationOptsValidator } from \"convex/server\";\nimport { v } from \"convex/values\";\nimport { internal } from \"./_generated/api\";\nimport type { Doc } from \"./_generated/dataModel\";\nimport type { MutationCtx, QueryCtx } from \"./_generated/server\";\nimport { internalMutation, mutation, query } from \"./_generated/server\";\nimport { missingSearchText } from \"./mediaMeta\";\n\nconst mediaType = v.union(v.literal(\"video\"), v.literal(\"image\"), v.literal(\"file\"));\ntype MediaType = Doc<\"media\">[\"type\"];\n\n/** Longest search kept: Convex matches at most 16 terms, so a longer query only costs. */\nconst SEARCH_MAX_LENGTH = 200;\nexport const BACKFILL_BATCH_SIZE = { default: 100, max: 1000 } as const;\n\n/** One page of the library, newest first, optionally of one kind (`by_type` index). */\nexport function listNewestFirst(\n\tctx: QueryCtx,\n\ttype: MediaType | undefined,\n\tpaginationOpts: PaginationOptions,\n): Promise<PaginationResult<Doc<\"media\">>> {\n\tconst records = type ? ctx.db.query(\"media\").withIndex(\"by_type\", (q) => q.eq(\"type\", type)) : ctx.db.query(\"media\");\n\treturn records.order(\"desc\").paginate(paginationOpts);\n}\n\n/**\n * One page of the records whose title or filename matches `search` (the last word as a prefix), best match first,\n * optionally of one kind. A blank `search` lists the library newest first, like `listMediaPage`. Records from before\n * 1.4.0 match only once they have a `searchText` (`backfillSearchText`, or any write to them).\n */\nexport const searchMedia = query({\n\targs: { search: v.string(), type: v.optional(mediaType), paginationOpts: paginationOptsValidator },\n\thandler: async (ctx, args): Promise<PaginationResult<Doc<\"media\">>> => {\n\t\tconst { type, paginationOpts } = args;\n\t\tconst search = args.search.trim().toLowerCase().slice(0, SEARCH_MAX_LENGTH);\n\t\tif (search === \"\") return listNewestFirst(ctx, type, paginationOpts);\n\n\t\treturn ctx.db\n\t\t\t.query(\"media\")\n\t\t\t.withSearchIndex(\"search_text\", (q) => {\n\t\t\t\tconst matches = q.search(\"searchText\", search);\n\t\t\t\treturn type ? matches.eq(\"type\", type) : matches;\n\t\t\t})\n\t\t\t.paginate(paginationOpts);\n\t},\n});\n\ninterface BackfillResult {\n\t/** Records given a `searchText` by this batch. */\n\tupdated: number;\n\t/** `false` while later batches are scheduled. */\n\tisDone: boolean;\n}\n\nfunction batchSizeOf(requested: number | undefined): number {\n\tif (requested === undefined || !Number.isFinite(requested)) return BACKFILL_BATCH_SIZE.default;\n\treturn Math.min(Math.max(Math.floor(requested), 1), BACKFILL_BATCH_SIZE.max);\n}\n\n/** Fills one page of records, then schedules the next page until the table is done. */\nasync function backfillBatch(ctx: MutationCtx, cursor: string | null, batchSize: number): Promise<BackfillResult> {\n\tconst page = await ctx.db.query(\"media\").paginate({ cursor, numItems: batchSize });\n\tlet updated = 0;\n\tfor (const record of page.page) {\n\t\tconst fill = missingSearchText(record);\n\t\tif (fill.searchText === undefined) continue;\n\t\tawait ctx.db.patch(record._id, fill);\n\t\tupdated++;\n\t}\n\tif (!page.isDone) {\n\t\tawait ctx.scheduler.runAfter(0, internal.library.backfillSearchTextBatch, {\n\t\t\tcursor: page.continueCursor,\n\t\t\tbatchSize,\n\t\t});\n\t}\n\treturn { updated, isDone: page.isDone };\n}\n\n/**\n * Gives every record without a `searchText` (written before 1.4.0) its title and filename, `batchSize` records per\n * mutation (default 100, at most 1000). Runs the first batch now and schedules the rest; safe to run again.\n */\nexport const backfillSearchText = mutation({\n\targs: { batchSize: v.optional(v.number()) },\n\thandler: async (ctx, args): Promise<BackfillResult> => backfillBatch(ctx, null, batchSizeOf(args.batchSize)),\n});\n\nexport const backfillSearchTextBatch = internalMutation({\n\targs: { cursor: v.union(v.string(), v.null()), batchSize: v.number() },\n\thandler: async (ctx, args): Promise<BackfillResult> => backfillBatch(ctx, args.cursor, args.batchSize),\n});\n"]}
|
package/dist/component/lock.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/component/lock.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/component/lock.ts"],"names":[],"mappings":"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","sourcesContent":["// Object lock from the app's server: read and change an object's retention, and set or release holds on a list of\n// keys. These are component functions, so only the app's own functions can call them (never a browser). A bulk hold\n// by prefix stays on the app's server, with `@devultur/core`'s `createWorkerClient(...).lock.holdAll({ prefix })`.\n// A refusal throws a `ConvexError` whose `data.code` is the Worker's (`retention_active`, `compliance_extend_only`,\n// `lock_privilege_required`, `lock_conflict`, ...); see workerErrors.ts.\nimport { HOLD_PAGE_SIZE } from \"@devultur/core\";\nimport { v } from \"convex/values\";\nimport { derivativesValidator, retentionModeValidator } from \"./lockValidators\";\nimport { getMediaClient } from \"./mediaClient\";\nimport { workerAction } from \"./workerErrors\";\n\nconst keysValidator = v.array(v.string());\n\nexport const getLock = workerAction({\n\targs: { key: v.string() },\n\thandler: async (_ctx, args) => getMediaClient().lock.get(args.key),\n});\n\nexport const setLock = workerAction({\n\targs: {\n\t\tkey: v.string(),\n\t\tmode: v.optional(retentionModeValidator),\n\t\tuntil: v.optional(v.string()),\n\t\tperiod: v.optional(v.number()),\n\t\tderivatives: v.optional(derivativesValidator),\n\t\tremove: v.optional(v.literal(true)),\n\t\tbypassGovernance: v.optional(v.boolean()),\n\t},\n\thandler: async (_ctx, { key, ...options }) => getMediaClient().lock.set(key, options),\n});\n\n/** Holds every key, a page of {@link HOLD_PAGE_SIZE} at a time; returns what changed and what didn't. */\nasync function changeHolds(keys: string[], hold: boolean) {\n\tconst lock = getMediaClient().lock;\n\tconst updated: string[] = [];\n\tconst skipped: { key: string; reason: string }[] = [];\n\tfor await (const page of hold ? lock.holdAll(keys) : lock.releaseAll(keys)) {\n\t\tupdated.push(...page.updated);\n\t\tskipped.push(...page.skipped);\n\t}\n\treturn { updated, skipped, pageSize: HOLD_PAGE_SIZE };\n}\n\nexport const setHold = workerAction({\n\targs: { keys: keysValidator },\n\thandler: async (_ctx, args) => changeHolds(args.keys, true),\n});\n\nexport const releaseHold = workerAction({\n\targs: { keys: keysValidator },\n\thandler: async (_ctx, args) => changeHolds(args.keys, false),\n});\n"]}
|
|
@@ -22,4 +22,3 @@ export declare const objectLockValidator: import("convex/values").VObject<{
|
|
|
22
22
|
}, "optional", "mode" | "until" | "period" | "derivatives">;
|
|
23
23
|
hold: import("convex/values").VBoolean<boolean | undefined, "optional">;
|
|
24
24
|
}, "required", "retention" | "hold" | "retention.mode" | "retention.until" | "retention.period" | "retention.derivatives">;
|
|
25
|
-
//# sourceMappingURL=lockValidators.d.ts.map
|
|
@@ -1 +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"}
|
|
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","sourcesContent":["// Validators of the object lock arguments (the shapes of `@devultur/core`'s `ObjectLock` and `SetLockOptions`).\nimport { v } from \"convex/values\";\n\nexport const retentionModeValidator = v.union(v.literal(\"governance\"), v.literal(\"compliance\"));\nexport const derivativesValidator = v.union(v.literal(\"inherit\"), v.literal(\"original-only\"));\n\nexport const objectLockValidator = v.object({\n\tretention: v.optional(\n\t\tv.object({\n\t\t\tmode: retentionModeValidator,\n\t\t\tuntil: v.optional(v.string()),\n\t\t\tperiod: v.optional(v.number()),\n\t\t\tderivatives: v.optional(derivativesValidator),\n\t\t}),\n\t),\n\thold: v.optional(v.boolean()),\n});\n"]}
|
|
@@ -1 +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"}
|
|
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","sourcesContent":["// The component's client of the media Worker, from the env the app mounts the component with.\nimport { createWorkerClient, type WorkerClient } from \"@devultur/core\";\nimport { env } from \"./_generated/server\";\n\nexport function getMediaClient(): WorkerClient {\n\treturn createWorkerClient(env.DEVULTUR_SECRET, env.DEVULTUR_API_URL);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mediaMeta.js","sourceRoot":"","sources":["../../src/component/mediaMeta.ts"],"names":[],"mappings":"AAAA,oHAAoH;AACpH,kHAAkH;AAClH,kFAAkF;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAChC,WAAW,EAAE,GAAG;IAChB,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,EAAE;IACZ,SAAS,EAAE,EAAE;IACb,+DAA+D;IAC/D,SAAS,EAAE,OAAO;CACT,CAAC;AAqBX,SAAS,eAAe,CAAC,KAAa,EAAE,OAAe;IACtD,OAAO,IAAI,WAAW,CAAC,EAAE,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;AACtG,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAU,CAAC;AAE9D,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,iBAAiB,CAAC,SAAS,CAAC;AAEpH;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAkB;IAClD,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;YACrB,SAAS;QACV,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,qCAAqC,KAAK,IAAI,KAAK,qCAAqC,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,WAAW,CAAC,KAAsB,EAAE,KAAa,EAAE,SAAiB;IAC5E,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS;QAAE,MAAM,eAAe,CAAC,KAAK,EAAE,GAAG,KAAK,oBAAoB,SAAS,aAAa,CAAC,CAAC;IACjH,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;AAC7C,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,aAAa,CAAC,IAAc;IAC3C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,OAAO,KAAK,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YAAE,SAAS;QAChE,IAAI,OAAO,CAAC,MAAM,GAAG,iBAAiB,CAAC,SAAS,EAAE,CAAC;YAClD,MAAM,eAAe,CAAC,MAAM,EAAE,4BAA4B,iBAAiB,CAAC,SAAS,aAAa,CAAC,CAAC;QACrG,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QAChC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,GAAG,iBAAiB,CAAC,QAAQ,EAAE,CAAC;QACpD,MAAM,eAAe,CAAC,MAAM,EAAE,4BAA4B,iBAAiB,CAAC,QAAQ,OAAO,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,UAAU,CAAC;AACnB,CAAC;AAED,2GAA2G;AAC3G,MAAM,UAAU,cAAc,CAAC,KAAqB;IACnD,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,KAAK,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC9G,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;QAAE,KAAK,CAAC,GAAG,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACpG,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrE,OAAO,KAAK,CAAC;AACd,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,YAAY,CAAC,KAAyB,EAAE,QAAgB;IACvE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;AAClE,CAAC;AAQD,yHAAyH;AACzH,MAAM,UAAU,iBAAiB,CAAC,MAAwB;IACzD,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAC/C,OAAO,EAAE,UAAU,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;AACpE,CAAC"}
|
|
1
|
+
{"version":3,"file":"mediaMeta.js","sourceRoot":"","sources":["../../src/component/mediaMeta.ts"],"names":[],"mappings":"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","sourcesContent":["// A media record's descriptive metadata: what the app edits (`title`, `alt`, `tags`), the hints an uploading client\n// sends (`width`, `height`, `pageCount`), and the `searchText` kept in step with the title and filename. Pure: no\n// database, so it is checked the same way in every function that writes a record.\nimport { ConvexError } from \"convex/values\";\n\nexport const MEDIA_META_LIMITS = {\n\ttitleLength: 200,\n\taltLength: 1000,\n\ttagCount: 20,\n\ttagLength: 50,\n\t/** Largest width, height or page count a client may report. */\n\thintValue: 100_000,\n} as const;\n\nexport interface UploadHints {\n\twidth?: number;\n\theight?: number;\n\tpageCount?: number;\n}\n\nexport interface MediaMetaInput {\n\ttitle?: string;\n\talt?: string;\n\ttags?: string[];\n}\n\n/** The fields to patch: a trimmed empty `title` or `alt` clears it (`undefined`), `tags: []` clears the tags. */\nexport interface MediaMetaPatch {\n\ttitle?: string | undefined;\n\talt?: string | undefined;\n\ttags?: string[];\n}\n\nfunction invalidMetadata(field: string, message: string) {\n\treturn new ConvexError({ code: \"invalid_media_metadata\", message, status: 400, details: { field } });\n}\n\nconst HINT_FIELDS = [\"width\", \"height\", \"pageCount\"] as const;\n\nconst isValidHint = (value: number) => Number.isInteger(value) && value > 0 && value <= MEDIA_META_LIMITS.hintValue;\n\n/**\n * The hints worth recording: positive integers up to `hintValue`. The bytes are already stored, so a bad hint (a\n * client's bug) is dropped and logged instead of failing the upload.\n */\nexport function validUploadHints(hints: UploadHints): UploadHints {\n\tconst valid: UploadHints = {};\n\tfor (const field of HINT_FIELDS) {\n\t\tconst value = hints[field];\n\t\tif (value === undefined) continue;\n\t\tif (isValidHint(value)) {\n\t\t\tvalid[field] = value;\n\t\t\tcontinue;\n\t\t}\n\t\tconsole.warn(`Devultur: dropped the upload hint ${field}=${value} (a whole number from 1 to 100,000)`);\n\t}\n\treturn valid;\n}\n\nfunction trimmedText(field: \"title\" | \"alt\", value: string, maxLength: number): string | undefined {\n\tconst trimmed = value.trim();\n\tif (trimmed.length > maxLength) throw invalidMetadata(field, `${field} must be at most ${maxLength} characters`);\n\treturn trimmed === \"\" ? undefined : trimmed;\n}\n\n/** Trimmed, without empty ones or repeats (first spelling wins), within the count and length limits. */\nexport function normalizeTags(tags: string[]): string[] {\n\tconst seen = new Set<string>();\n\tconst normalized: string[] = [];\n\tfor (const tag of tags) {\n\t\tconst trimmed = tag.trim();\n\t\tif (trimmed === \"\" || seen.has(trimmed.toLowerCase())) continue;\n\t\tif (trimmed.length > MEDIA_META_LIMITS.tagLength) {\n\t\t\tthrow invalidMetadata(\"tags\", `Each tag must be at most ${MEDIA_META_LIMITS.tagLength} characters`);\n\t\t}\n\t\tseen.add(trimmed.toLowerCase());\n\t\tnormalized.push(trimmed);\n\t}\n\tif (normalized.length > MEDIA_META_LIMITS.tagCount) {\n\t\tthrow invalidMetadata(\"tags\", `A media can have at most ${MEDIA_META_LIMITS.tagCount} tags`);\n\t}\n\treturn normalized;\n}\n\n/** The patch for an edit; only the fields the caller passed are in it. Throws `invalid_media_metadata`. */\nexport function mediaMetaPatch(input: MediaMetaInput): MediaMetaPatch {\n\tconst patch: MediaMetaPatch = {};\n\tif (input.title !== undefined) patch.title = trimmedText(\"title\", input.title, MEDIA_META_LIMITS.titleLength);\n\tif (input.alt !== undefined) patch.alt = trimmedText(\"alt\", input.alt, MEDIA_META_LIMITS.altLength);\n\tif (input.tags !== undefined) patch.tags = normalizeTags(input.tags);\n\treturn patch;\n}\n\n/** What the media library's search matches: the title and the filename, lowercased. */\nexport function searchTextOf(title: string | undefined, filename: string): string {\n\treturn (title ? `${title} ${filename}` : filename).toLowerCase();\n}\n\ninterface SearchableRecord {\n\ttitle?: string;\n\tfilename: string;\n\tsearchText?: string;\n}\n\n/** `{ searchText }` for a record written before 1.4.0 (it has none), else `{}`: spread it into any patch of a record. */\nexport function missingSearchText(record: SearchableRecord): { searchText?: string } {\n\tif (record.searchText !== undefined) return {};\n\treturn { searchText: searchTextOf(record.title, record.filename) };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/component/metadata.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,mGAAmG;AACnG,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;IACvC,IAAI,EAAE;QACL,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC7B,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC3B,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;KACrC;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAgC,EAAE;QAC1D,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC;QAC/B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE;aACzB,KAAK,CAAC,OAAO,CAAC;aACd,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;aAC5C,KAAK,EAAE,CAAC;QACV,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,MAAM,KAAK,GAAG,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QAC5D,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC/F,OAAO,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;CACD,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/component/metadata.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,mGAAmG;AACnG,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;IACvC,IAAI,EAAE;QACL,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC7B,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC3B,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;KACrC;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAgC,EAAE;QAC1D,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC;QAC/B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE;aACzB,KAAK,CAAC,OAAO,CAAC;aACd,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;aAC5C,KAAK,EAAE,CAAC;QACV,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,MAAM,KAAK,GAAG,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QAC5D,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC/F,OAAO,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;CACD,CAAC,CAAC","sourcesContent":["// Editing a media record's descriptive metadata (title, alt text, tags). The upload's own facts (size, dimensions,\n// duration, page count) are recorded by `uploads.ts` and the transcode webhook, never edited here.\nimport { v } from \"convex/values\";\nimport type { Doc } from \"./_generated/dataModel\";\nimport { mutation } from \"./_generated/server\";\nimport { mediaMetaPatch, searchTextOf } from \"./mediaMeta\";\n\n/**\n * Sets a record's `title`, `alt` and/or `tags` (only the ones passed). Trimmed; an empty title or alt clears it, and\n * tags are deduplicated. Throws `invalid_media_metadata` past the limits (title 200, alt 1000, 20 tags of 50). Returns\n * the updated record, or `null` when no record has this media id.\n */\nexport const updateMediaMeta = mutation({\n\targs: {\n\t\tkey: v.string(),\n\t\ttitle: v.optional(v.string()),\n\t\talt: v.optional(v.string()),\n\t\ttags: v.optional(v.array(v.string())),\n\t},\n\thandler: async (ctx, args): Promise<Doc<\"media\"> | null> => {\n\t\tconst { key, ...input } = args;\n\t\tconst patch = mediaMetaPatch(input);\n\t\tconst record = await ctx.db\n\t\t\t.query(\"media\")\n\t\t\t.withIndex(\"by_key\", (q) => q.eq(\"key\", key))\n\t\t\t.first();\n\t\tif (!record) return null;\n\n\t\tconst title = \"title\" in patch ? patch.title : record.title;\n\t\tawait ctx.db.patch(record._id, { ...patch, searchText: searchTextOf(title, record.filename) });\n\t\treturn ctx.db.get(record._id);\n\t},\n});\n"]}
|