@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.
Files changed (65) hide show
  1. package/README.md +100 -5
  2. package/dist/{chunk-JZYKPLI4.js → chunk-RDVBE3G3.js} +20 -1
  3. package/dist/component/_generated/api.d.ts +0 -1
  4. package/dist/component/_generated/api.js.map +1 -1
  5. package/dist/component/_generated/component.d.ts +0 -1
  6. package/dist/component/_generated/component.js.map +1 -1
  7. package/dist/component/_generated/dataModel.d.ts +0 -1
  8. package/dist/component/_generated/dataModel.js.map +1 -1
  9. package/dist/component/_generated/server.d.ts +0 -1
  10. package/dist/component/_generated/server.js.map +1 -1
  11. package/dist/component/convex.config.d.ts +0 -1
  12. package/dist/component/convex.config.js.map +1 -1
  13. package/dist/component/http.d.ts +0 -1
  14. package/dist/component/http.js.map +1 -1
  15. package/dist/component/library.d.ts +0 -1
  16. package/dist/component/library.js.map +1 -1
  17. package/dist/component/lock.d.ts +0 -1
  18. package/dist/component/lock.js.map +1 -1
  19. package/dist/component/lockValidators.d.ts +0 -1
  20. package/dist/component/lockValidators.js.map +1 -1
  21. package/dist/component/mediaClient.d.ts +0 -1
  22. package/dist/component/mediaClient.js.map +1 -1
  23. package/dist/component/mediaMeta.d.ts +0 -1
  24. package/dist/component/mediaMeta.js.map +1 -1
  25. package/dist/component/metadata.d.ts +0 -1
  26. package/dist/component/metadata.js.map +1 -1
  27. package/dist/component/public.d.ts +0 -1
  28. package/dist/component/public.js.map +1 -1
  29. package/dist/component/schema.d.ts +0 -1
  30. package/dist/component/schema.js.map +1 -1
  31. package/dist/component/uploadKeys.d.ts +0 -1
  32. package/dist/component/uploadKeys.js.map +1 -1
  33. package/dist/component/uploads.d.ts +0 -1
  34. package/dist/component/uploads.js.map +1 -1
  35. package/dist/component/workerErrors.d.ts +0 -1
  36. package/dist/component/workerErrors.js.map +1 -1
  37. package/dist/index.d.ts +33 -12
  38. package/dist/index.js +22 -4
  39. package/dist/locales/es-formal.d.ts +7 -3
  40. package/dist/locales/es-formal.js +6 -3
  41. package/dist/locales/es.d.ts +7 -3
  42. package/dist/locales/es.js +5 -3
  43. package/dist/{preview-labels-B-2mra1h.d.ts → preview-labels-C2nUUY_f.d.ts} +1 -1
  44. package/dist/react.d.ts +133 -92
  45. package/dist/react.js +310 -235
  46. package/dist/types-C0ruHUcu.d.ts +196 -0
  47. package/package.json +4 -3
  48. package/dist/component/_generated/api.d.ts.map +0 -1
  49. package/dist/component/_generated/component.d.ts.map +0 -1
  50. package/dist/component/_generated/dataModel.d.ts.map +0 -1
  51. package/dist/component/_generated/server.d.ts.map +0 -1
  52. package/dist/component/convex.config.d.ts.map +0 -1
  53. package/dist/component/http.d.ts.map +0 -1
  54. package/dist/component/library.d.ts.map +0 -1
  55. package/dist/component/lock.d.ts.map +0 -1
  56. package/dist/component/lockValidators.d.ts.map +0 -1
  57. package/dist/component/mediaClient.d.ts.map +0 -1
  58. package/dist/component/mediaMeta.d.ts.map +0 -1
  59. package/dist/component/metadata.d.ts.map +0 -1
  60. package/dist/component/public.d.ts.map +0 -1
  61. package/dist/component/schema.d.ts.map +0 -1
  62. package/dist/component/uploadKeys.d.ts.map +0 -1
  63. package/dist/component/uploads.d.ts.map +0 -1
  64. package/dist/component/workerErrors.d.ts.map +0 -1
  65. 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 picker labels), and differs only in the strings that
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
  };
@@ -51,4 +51,3 @@ export declare const api: FilterApi<typeof fullApi, FunctionReference<any, "publ
51
51
  export declare const internal: FilterApi<typeof fullApi, FunctionReference<any, "internal">>;
52
52
  export declare const components: {};
53
53
  export {};
54
- //# sourceMappingURL=api.d.ts.map
@@ -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"]}
@@ -178,4 +178,3 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
178
178
  }, any, Name>;
179
179
  };
180
180
  };
181
- //# sourceMappingURL=component.d.ts.map
@@ -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"]}
@@ -43,4 +43,3 @@ export type Id<TableName extends TableNames | SystemTableNames> = GenericId<Tabl
43
43
  * `mutationGeneric` to make them type-safe.
44
44
  */
45
45
  export type DataModel = DataModelFromSchemaDefinition<typeof schema>;
46
- //# sourceMappingURL=dataModel.d.ts.map
@@ -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"]}
@@ -127,4 +127,3 @@ export type DatabaseReader = GenericDatabaseReader<DataModel>;
127
127
  */
128
128
  export type DatabaseWriter = GenericDatabaseWriter<DataModel>;
129
129
  export {};
130
- //# sourceMappingURL=server.d.ts.map
@@ -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"]}
@@ -3,4 +3,3 @@ declare const component: import("convex/server").ComponentDefinition<any, {
3
3
  readonly DEVULTUR_API_URL: import("convex/values").VString<string, "required">;
4
4
  }>;
5
5
  export default component;
6
- //# sourceMappingURL=convex.config.d.ts.map
@@ -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"]}
@@ -1,3 +1,2 @@
1
1
  declare const http: import("convex/server").HttpRouter;
2
2
  export default http;
3
- //# sourceMappingURL=http.d.ts.map
@@ -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"]}
@@ -90,4 +90,3 @@ export declare const backfillSearchTextBatch: import("convex/server").Registered
90
90
  batchSize: number;
91
91
  }, Promise<BackfillResult>>;
92
92
  export {};
93
- //# sourceMappingURL=library.d.ts.map
@@ -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":"AAAA,mHAAmH;AACnH,wEAAwE;AACxE,OAAO,EAAiD,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACvG,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAGrF,0FAA0F;AAC1F,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAC9B,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAW,CAAC;AAExE,uFAAuF;AACvF,MAAM,UAAU,eAAe,CAC9B,GAAa,EACb,IAA2B,EAC3B,cAAiC;IAEjC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrH,OAAO,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AACvD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC;IAChC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,uBAAuB,EAAE;IAClG,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAA2C,EAAE;QACrE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;QACtC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;QAC5E,IAAI,MAAM,KAAK,EAAE;YAAE,OAAO,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QAErE,OAAO,GAAG,CAAC,EAAE;aACX,KAAK,CAAC,OAAO,CAAC;aACd,eAAe,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE;YACrC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YAC/C,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAClD,CAAC,CAAC;aACD,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC5B,CAAC;CACD,CAAC,CAAC;AASH,SAAS,WAAW,CAAC,SAA6B;IACjD,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,mBAAmB,CAAC,OAAO,CAAC;IAC/F,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,CAAC;AAC9E,CAAC;AAED,uFAAuF;AACvF,KAAK,UAAU,aAAa,CAAC,GAAgB,EAAE,MAAqB,EAAE,SAAiB;IACtF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;IACnF,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;YAAE,SAAS;QAC5C,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACrC,OAAO,EAAE,CAAC;IACX,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,uBAAuB,EAAE;YACzE,MAAM,EAAE,IAAI,CAAC,cAAc;YAC3B,SAAS;SACT,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAC1C,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;IAC3C,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAA2B,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;CAC5G,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,gBAAgB,CAAC;IACvD,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE;IACtE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAA2B,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC;CACtG,CAAC,CAAC","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"]}
@@ -30,4 +30,3 @@ export declare const releaseHold: import("convex/server").RegisteredAction<"publ
30
30
  }[];
31
31
  pageSize: number;
32
32
  }>>;
33
- //# sourceMappingURL=lock.d.ts.map
@@ -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,3 +1,2 @@
1
1
  import { type WorkerClient } from "@devultur/core";
2
2
  export declare function getMediaClient(): WorkerClient;
3
- //# sourceMappingURL=mediaClient.d.ts.map
@@ -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"]}
@@ -43,4 +43,3 @@ export declare function missingSearchText(record: SearchableRecord): {
43
43
  searchText?: string;
44
44
  };
45
45
  export {};
46
- //# sourceMappingURL=mediaMeta.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mediaMeta.js","sourceRoot":"","sources":["../../src/component/mediaMeta.ts"],"names":[],"mappings":"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"}
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"]}
@@ -56,4 +56,3 @@ export declare const updateMediaMeta: import("convex/server").RegisteredMutation
56
56
  sizeBytes: number;
57
57
  createdAt: number;
58
58
  } | null>>;
59
- //# sourceMappingURL=metadata.d.ts.map
@@ -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"]}
@@ -312,4 +312,3 @@ export declare const handleWebhookEvent: import("convex/server").RegisteredMutat
312
312
  videoId: string;
313
313
  event: string;
314
314
  }, Promise<void>>;
315
- //# sourceMappingURL=public.d.ts.map