@devultur/convex 1.3.1 → 1.3.2
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 +36 -0
- package/dist/react.d.ts +25 -2
- package/dist/react.js +160 -141
- package/dist/validators.d.ts +5 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -86,6 +86,42 @@ The PUT goes through `XMLHttpRequest` (`fetch` reports no upload progress), in p
|
|
|
86
86
|
optional field of `DevulturMedia` and of `dv.media()`. It is informational: delivery URLs are derived from `id`,
|
|
87
87
|
`kind`, `visibility` and `filename`.
|
|
88
88
|
|
|
89
|
+
## Multiple workspaces
|
|
90
|
+
|
|
91
|
+
An app that shows private media from several workspaces (an admin console that previews each one, for example) can
|
|
92
|
+
send extra arguments with every viewer token request. Pass them in `tokenArgs`, and pass your own action in
|
|
93
|
+
`issueViewerToken`: the component's `issueViewerToken` accepts only `sessionSecret`.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// convex/admin.ts
|
|
97
|
+
import { v } from "convex/values";
|
|
98
|
+
import { components } from "./_generated/api";
|
|
99
|
+
import { action } from "./_generated/server";
|
|
100
|
+
|
|
101
|
+
export const issueWorkspaceViewerToken = action({
|
|
102
|
+
args: { sessionSecret: v.string(), workspaceId: v.id("workspaces") },
|
|
103
|
+
handler: async (ctx, { sessionSecret, workspaceId }) => {
|
|
104
|
+
await assertWorkspaceAccess(ctx, workspaceId); // your own check
|
|
105
|
+
return ctx.runAction(components.devultur.public.issueViewerToken, { sessionSecret });
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
<DevulturProvider
|
|
112
|
+
api={api.devultur}
|
|
113
|
+
issueViewerToken={api.admin.issueWorkspaceViewerToken}
|
|
114
|
+
tokenArgs={{ workspaceId }}
|
|
115
|
+
>
|
|
116
|
+
<WorkspaceMedia />
|
|
117
|
+
</DevulturProvider>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The provider sends `{ sessionSecret, ...tokenArgs }` on the first issuance, on each renewal and on each re-issue.
|
|
121
|
+
`tokenArgs` are compared by value, so a new object with the same values changes nothing. When they change, the
|
|
122
|
+
provider drops the current token, private media wait for the new one, and a response for the previous arguments is
|
|
123
|
+
ignored. Your action returns what the component's action returns: `{ token, expiresIn }` or the token string.
|
|
124
|
+
|
|
89
125
|
## Media library
|
|
90
126
|
|
|
91
127
|
Every upload, whatever its kind, gets a record in the component's `media` table once its bytes are stored, so
|
package/dist/react.d.ts
CHANGED
|
@@ -3,9 +3,18 @@ import { Visibility, DevulturMedia, MediaUrlOptions, ProcessingPhase, FailureRea
|
|
|
3
3
|
import { BakeOutputFormat, MediaUploadState, MediaUploadError, UploadProgress } from '@devultur/react';
|
|
4
4
|
export { MediaUploadError, MediaUploadState, UploadProgress } from '@devultur/react';
|
|
5
5
|
import { FunctionReference } from 'convex/server';
|
|
6
|
+
import { Value } from 'convex/values';
|
|
6
7
|
import { a as PdfjsLoader } from './pdf-render-CiCK6V1X.js';
|
|
7
8
|
export { P as PdfjsModule } from './pdf-render-CiCK6V1X.js';
|
|
8
9
|
|
|
10
|
+
/** What a viewer token action returns: the token with its lifetime, or the bare token (default lifetime). */
|
|
11
|
+
type ViewerTokenResult = {
|
|
12
|
+
token: string;
|
|
13
|
+
expiresIn: number;
|
|
14
|
+
} | string;
|
|
15
|
+
/** Extra arguments sent with `sessionSecret` to the viewer token action. */
|
|
16
|
+
type ViewerTokenArgs = Record<string, Value>;
|
|
17
|
+
|
|
9
18
|
interface CropUploadRenderProps {
|
|
10
19
|
/** The state machine of the full cycle (idle → uploading → ready / failed). */
|
|
11
20
|
state: MediaUploadState;
|
|
@@ -304,9 +313,23 @@ interface DevulturProviderProps {
|
|
|
304
313
|
api: DevulturApi;
|
|
305
314
|
/** Media worker base (e.g. `https://media.your-domain.com`). Required for `useMediaUrl`. */
|
|
306
315
|
baseUrl?: string;
|
|
316
|
+
/**
|
|
317
|
+
* Extra arguments sent with `sessionSecret` on every viewer token issuance (first, renewal and re-issue), for
|
|
318
|
+
* example `{ workspaceId }`. Compared by value: when they change, the current token is dropped and a new one is
|
|
319
|
+
* issued. The component's own `issueViewerToken` accepts only `sessionSecret`, so pass your own action in
|
|
320
|
+
* `issueViewerToken` that takes these arguments.
|
|
321
|
+
*/
|
|
322
|
+
tokenArgs?: ViewerTokenArgs;
|
|
323
|
+
/**
|
|
324
|
+
* Your own viewer token action, used instead of `api.issueViewerToken`. It receives `{ sessionSecret, ...tokenArgs }`
|
|
325
|
+
* and returns what the component's `issueViewerToken` returns.
|
|
326
|
+
*/
|
|
327
|
+
issueViewerToken?: FunctionReference<"action", "public", {
|
|
328
|
+
sessionSecret: string;
|
|
329
|
+
}>;
|
|
307
330
|
children: React.ReactNode;
|
|
308
331
|
}
|
|
309
|
-
declare function DevulturProvider({ api, baseUrl, children }: DevulturProviderProps): React.JSX.Element;
|
|
332
|
+
declare function DevulturProvider({ api, baseUrl, tokenArgs, issueViewerToken, children }: DevulturProviderProps): React.JSX.Element;
|
|
310
333
|
declare function useDevultur(): DevulturContextValue;
|
|
311
334
|
declare function useDevulturProgress(): ProgressContextValue;
|
|
312
335
|
/**
|
|
@@ -471,4 +494,4 @@ interface UseMediaUploadReturn {
|
|
|
471
494
|
*/
|
|
472
495
|
declare function useMediaUpload(options?: UseMediaUploadOptions): UseMediaUploadReturn;
|
|
473
496
|
|
|
474
|
-
export { type CaptionTrack, CropUpload, type CropUploadLabels, type CropUploadProps, type CropUploadRenderProps, DevulturProvider, DownloadLink, Image, MediaFetchError, type MediaFetchErrorCode, type MediaObjectUrl, type PdfPreviewConfig, PdfPreviewProvider, PdfThumbnail, type PdfThumbnailProps, PdfjsLoader, Preview, type PreviewKind, type PreviewLabels, type PreviewProps, type UploadedMedia, type UseMediaObjectUrlOptions, type UseMediaUploadOptions, type UseMediaUploadReturn, type VideoProps, type VideoState, defaultPreviewLabels, useDevultur, useDevulturMedia, useDevulturProgress, useMediaObjectUrl, useMediaUpload, useMediaUrl, useVideoProps, useVideoState };
|
|
497
|
+
export { type CaptionTrack, CropUpload, type CropUploadLabels, type CropUploadProps, type CropUploadRenderProps, DevulturProvider, DownloadLink, Image, MediaFetchError, type MediaFetchErrorCode, type MediaObjectUrl, type PdfPreviewConfig, PdfPreviewProvider, PdfThumbnail, type PdfThumbnailProps, PdfjsLoader, Preview, type PreviewKind, type PreviewLabels, type PreviewProps, type UploadedMedia, type UseMediaObjectUrlOptions, type UseMediaUploadOptions, type UseMediaUploadReturn, type VideoProps, type VideoState, type ViewerTokenArgs, type ViewerTokenResult, defaultPreviewLabels, useDevultur, useDevulturMedia, useDevulturProgress, useMediaObjectUrl, useMediaUpload, useMediaUrl, useVideoProps, useVideoState };
|
package/dist/react.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/react.tsx
|
|
4
|
-
import
|
|
4
|
+
import React8 from "react";
|
|
5
5
|
import {
|
|
6
6
|
buildMediaUrl,
|
|
7
7
|
CaptionLocaleLabel,
|
|
8
|
-
DEFAULT_TOKEN_EXPIRY_SECONDS,
|
|
9
8
|
MediaStatus,
|
|
10
9
|
mediaCaptionPath,
|
|
11
10
|
mediaPath as mediaPath2,
|
|
@@ -214,8 +213,76 @@ function deriveUploadState(local, videoState, retry) {
|
|
|
214
213
|
}
|
|
215
214
|
}
|
|
216
215
|
|
|
217
|
-
// src/
|
|
216
|
+
// src/viewer-token.ts
|
|
218
217
|
import React from "react";
|
|
218
|
+
import { DEFAULT_TOKEN_EXPIRY_SECONDS } from "@devultur/core";
|
|
219
|
+
import { convexToJson } from "convex/values";
|
|
220
|
+
var REFRESH_BUFFER_MS = 5 * 60 * 1e3;
|
|
221
|
+
var MIN_REFRESH_MS = 6e4;
|
|
222
|
+
var DEFAULT_TTL_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1e3;
|
|
223
|
+
function extractTokenResult(result) {
|
|
224
|
+
const tokenValue = typeof result === "string" ? result : result.token;
|
|
225
|
+
const ttlMs = typeof result === "string" ? DEFAULT_TTL_MS : result.expiresIn * 1e3;
|
|
226
|
+
return { tokenValue, ttlMs };
|
|
227
|
+
}
|
|
228
|
+
function sortKeys(json) {
|
|
229
|
+
if (Array.isArray(json)) return json.map(sortKeys);
|
|
230
|
+
if (json === null || typeof json !== "object") return json;
|
|
231
|
+
return Object.fromEntries(
|
|
232
|
+
Object.keys(json).sort().map((name) => [name, sortKeys(json[name])])
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
function tokenArgsKey(tokenArgs) {
|
|
236
|
+
return JSON.stringify(sortKeys(convexToJson(tokenArgs ?? {})));
|
|
237
|
+
}
|
|
238
|
+
function useViewerToken({ issueViewerToken, sessionSecret, tokenArgs }) {
|
|
239
|
+
const argsKey = tokenArgsKey(tokenArgs);
|
|
240
|
+
const stableArgs = React.useMemo(() => tokenArgs ?? {}, [argsKey]);
|
|
241
|
+
const [issued, setIssued] = React.useState(null);
|
|
242
|
+
const token = issued?.argsKey === argsKey ? issued.token : null;
|
|
243
|
+
const currentArgsKeyRef = React.useRef(argsKey);
|
|
244
|
+
const refreshTimerRef = React.useRef(null);
|
|
245
|
+
const inFlightRef = React.useRef(null);
|
|
246
|
+
React.useEffect(() => {
|
|
247
|
+
currentArgsKeyRef.current = argsKey;
|
|
248
|
+
}, [argsKey]);
|
|
249
|
+
React.useEffect(() => {
|
|
250
|
+
return () => {
|
|
251
|
+
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
252
|
+
};
|
|
253
|
+
}, []);
|
|
254
|
+
const request = React.useCallback(() => {
|
|
255
|
+
const renew = () => void request();
|
|
256
|
+
return issueViewerToken({ ...stableArgs, sessionSecret }).then((result) => {
|
|
257
|
+
if (currentArgsKeyRef.current !== argsKey) return null;
|
|
258
|
+
const { tokenValue, ttlMs } = extractTokenResult(result);
|
|
259
|
+
setIssued({ token: tokenValue, argsKey });
|
|
260
|
+
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
261
|
+
refreshTimerRef.current = setTimeout(renew, Math.max(ttlMs - REFRESH_BUFFER_MS, MIN_REFRESH_MS));
|
|
262
|
+
return tokenValue;
|
|
263
|
+
});
|
|
264
|
+
}, [issueViewerToken, stableArgs, argsKey, sessionSecret]);
|
|
265
|
+
const issueOnce = React.useCallback(() => {
|
|
266
|
+
const inFlight = inFlightRef.current;
|
|
267
|
+
if (inFlight?.argsKey === argsKey) return inFlight.promise;
|
|
268
|
+
const promise = request();
|
|
269
|
+
const settle = () => {
|
|
270
|
+
if (inFlightRef.current?.promise === promise) inFlightRef.current = null;
|
|
271
|
+
};
|
|
272
|
+
promise.then(settle, settle);
|
|
273
|
+
inFlightRef.current = { argsKey, promise };
|
|
274
|
+
return promise;
|
|
275
|
+
}, [request, argsKey]);
|
|
276
|
+
React.useEffect(() => {
|
|
277
|
+
if (token) return;
|
|
278
|
+
issueOnce();
|
|
279
|
+
}, [token, issueOnce]);
|
|
280
|
+
const reissueToken = React.useCallback(() => issueOnce().catch(() => null), [issueOnce]);
|
|
281
|
+
return { token, reissueToken };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/crop-upload.tsx
|
|
285
|
+
import React2 from "react";
|
|
219
286
|
import { ImageCropper } from "@devultur/react";
|
|
220
287
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
221
288
|
var DEFAULT_LABELS = {
|
|
@@ -239,18 +306,18 @@ function CropUpload({
|
|
|
239
306
|
children
|
|
240
307
|
}) {
|
|
241
308
|
const text = { ...DEFAULT_LABELS, ...labels };
|
|
242
|
-
const [pendingFile, setPendingFile] =
|
|
243
|
-
const inputRef =
|
|
309
|
+
const [pendingFile, setPendingFile] = React2.useState(null);
|
|
310
|
+
const inputRef = React2.useRef(null);
|
|
244
311
|
const { upload, state, cancel, reset } = useMediaUpload({ visibility, replaces, onComplete });
|
|
245
|
-
const pickFile =
|
|
246
|
-
const handleCrop =
|
|
312
|
+
const pickFile = React2.useCallback(() => inputRef.current?.click(), []);
|
|
313
|
+
const handleCrop = React2.useCallback(
|
|
247
314
|
(baked) => {
|
|
248
315
|
setPendingFile(null);
|
|
249
316
|
upload(baked);
|
|
250
317
|
},
|
|
251
318
|
[upload]
|
|
252
319
|
);
|
|
253
|
-
const resetAll =
|
|
320
|
+
const resetAll = React2.useCallback(() => {
|
|
254
321
|
setPendingFile(null);
|
|
255
322
|
reset();
|
|
256
323
|
}, [reset]);
|
|
@@ -303,7 +370,7 @@ function CropUpload({
|
|
|
303
370
|
}
|
|
304
371
|
|
|
305
372
|
// src/media-resource.ts
|
|
306
|
-
import
|
|
373
|
+
import React3 from "react";
|
|
307
374
|
import { mediaPath, mediaUrl } from "@devultur/core";
|
|
308
375
|
var IDLE = { value: null, contentType: null, isLoading: false, error: null };
|
|
309
376
|
var LOADING = { value: null, contentType: null, isLoading: true, error: null };
|
|
@@ -313,12 +380,12 @@ function useMediaResource(media, options) {
|
|
|
313
380
|
const isPrivate = media?.visibility === "private";
|
|
314
381
|
const isReady = !isPrivate || devultur.token !== null;
|
|
315
382
|
const requestKey = media && isEnabled ? [devultur.baseUrl, media.visibility, mediaPath(media), contentType ?? "", maxBytes ?? ""].join("|") : null;
|
|
316
|
-
const [settled, setSettled] =
|
|
317
|
-
const inputsRef =
|
|
318
|
-
|
|
383
|
+
const [settled, setSettled] = React3.useState(null);
|
|
384
|
+
const inputsRef = React3.useRef({ media, devultur, options });
|
|
385
|
+
React3.useEffect(() => {
|
|
319
386
|
inputsRef.current = { media, devultur, options };
|
|
320
387
|
});
|
|
321
|
-
|
|
388
|
+
React3.useEffect(() => {
|
|
322
389
|
if (!requestKey || !isReady) return;
|
|
323
390
|
const { media: target, devultur: context, options: current } = inputsRef.current;
|
|
324
391
|
if (!target || !context.baseUrl) return;
|
|
@@ -380,7 +447,7 @@ function useMediaObjectUrl(media, options) {
|
|
|
380
447
|
}
|
|
381
448
|
|
|
382
449
|
// src/pdf-context.tsx
|
|
383
|
-
import
|
|
450
|
+
import React4 from "react";
|
|
384
451
|
|
|
385
452
|
// src/preview-labels.ts
|
|
386
453
|
var ERROR_TEXT = {
|
|
@@ -412,14 +479,14 @@ function mergePreviewLabels(...sources) {
|
|
|
412
479
|
|
|
413
480
|
// src/pdf-context.tsx
|
|
414
481
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
415
|
-
var PdfPreviewContext =
|
|
482
|
+
var PdfPreviewContext = React4.createContext({});
|
|
416
483
|
function PdfPreviewProvider({
|
|
417
484
|
pdfjs,
|
|
418
485
|
workerSrc,
|
|
419
486
|
labels,
|
|
420
487
|
children
|
|
421
488
|
}) {
|
|
422
|
-
const config =
|
|
489
|
+
const config = React4.useMemo(() => ({ pdfjs, workerSrc, labels }), [pdfjs, workerSrc, labels]);
|
|
423
490
|
return /* @__PURE__ */ jsx2(PdfPreviewContext.Provider, { value: config, children });
|
|
424
491
|
}
|
|
425
492
|
var loadedModules = /* @__PURE__ */ new WeakMap();
|
|
@@ -431,9 +498,9 @@ function loadOnce(loader) {
|
|
|
431
498
|
return loading;
|
|
432
499
|
}
|
|
433
500
|
function usePdfjs(workerSrcOverride) {
|
|
434
|
-
const { pdfjs: loader, workerSrc } =
|
|
435
|
-
const [loaded, setLoaded] =
|
|
436
|
-
|
|
501
|
+
const { pdfjs: loader, workerSrc } = React4.useContext(PdfPreviewContext);
|
|
502
|
+
const [loaded, setLoaded] = React4.useState(null);
|
|
503
|
+
React4.useEffect(() => {
|
|
437
504
|
if (!loader) return;
|
|
438
505
|
let isCurrent = true;
|
|
439
506
|
loadOnce(loader).then((pdfjs) => {
|
|
@@ -449,15 +516,15 @@ function usePdfjs(workerSrcOverride) {
|
|
|
449
516
|
return { status: "ready", pdfjs: loaded.pdfjs, workerSrc: workerSrcOverride ?? workerSrc };
|
|
450
517
|
}
|
|
451
518
|
function usePreviewLabels(labels) {
|
|
452
|
-
const providerLabels =
|
|
519
|
+
const providerLabels = React4.useContext(PdfPreviewContext).labels;
|
|
453
520
|
return mergePreviewLabels(providerLabels, labels);
|
|
454
521
|
}
|
|
455
522
|
|
|
456
523
|
// src/pdf-thumbnail.tsx
|
|
457
|
-
import
|
|
524
|
+
import React7 from "react";
|
|
458
525
|
|
|
459
526
|
// src/pdf-hooks.ts
|
|
460
|
-
import
|
|
527
|
+
import React5 from "react";
|
|
461
528
|
|
|
462
529
|
// src/pdf-render.ts
|
|
463
530
|
var MOBILE_MAX_CANVAS_PIXELS = 5242880;
|
|
@@ -501,8 +568,8 @@ var isRenderCancelled = (error) => error instanceof Error && error.name === "Ren
|
|
|
501
568
|
|
|
502
569
|
// src/pdf-hooks.ts
|
|
503
570
|
function useIsVisible(ref, rootMargin = "200px") {
|
|
504
|
-
const [isVisible, setIsVisible] =
|
|
505
|
-
|
|
571
|
+
const [isVisible, setIsVisible] = React5.useState(false);
|
|
572
|
+
React5.useEffect(() => {
|
|
506
573
|
const element = ref.current;
|
|
507
574
|
if (isVisible || !element) return;
|
|
508
575
|
if (typeof IntersectionObserver === "undefined") {
|
|
@@ -523,12 +590,12 @@ function useIsVisible(ref, rootMargin = "200px") {
|
|
|
523
590
|
return isVisible;
|
|
524
591
|
}
|
|
525
592
|
function usePdfDocument({ pdfjs, bytes, workerSrc }) {
|
|
526
|
-
const [state, setState] =
|
|
593
|
+
const [state, setState] = React5.useState({
|
|
527
594
|
bytes: null,
|
|
528
595
|
document: null,
|
|
529
596
|
error: null
|
|
530
597
|
});
|
|
531
|
-
|
|
598
|
+
React5.useEffect(() => {
|
|
532
599
|
let isCurrent = true;
|
|
533
600
|
const loadingTask = openPdf(pdfjs, bytes, workerSrc);
|
|
534
601
|
loadingTask.promise.then((document2) => {
|
|
@@ -547,8 +614,8 @@ function usePdfDocument({ pdfjs, bytes, workerSrc }) {
|
|
|
547
614
|
}
|
|
548
615
|
function usePageRender(options) {
|
|
549
616
|
const { document: document2, pageNumber, canvasRef, width, isEnabled, shouldExtractText = false } = options;
|
|
550
|
-
const [state, setState] =
|
|
551
|
-
|
|
617
|
+
const [state, setState] = React5.useState({ isRendered: false, text: "" });
|
|
618
|
+
React5.useEffect(() => {
|
|
552
619
|
const canvas = canvasRef.current;
|
|
553
620
|
if (!document2 || !isEnabled || !canvas) return;
|
|
554
621
|
let isCurrent = true;
|
|
@@ -578,7 +645,7 @@ function usePageRender(options) {
|
|
|
578
645
|
}
|
|
579
646
|
|
|
580
647
|
// src/pdf-preview.tsx
|
|
581
|
-
import
|
|
648
|
+
import React6 from "react";
|
|
582
649
|
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
583
650
|
var VISUALLY_HIDDEN = {
|
|
584
651
|
position: "absolute",
|
|
@@ -624,9 +691,9 @@ function PdfPreview(props) {
|
|
|
624
691
|
function PdfCanvasDocument(props) {
|
|
625
692
|
const { pdfjs, workerSrc, bytes, label, className, renderFallback, loading, labels, maxPages } = props;
|
|
626
693
|
const { document: document2, error } = usePdfDocument({ pdfjs, bytes, workerSrc });
|
|
627
|
-
const containerRef =
|
|
628
|
-
const [width, setWidth] =
|
|
629
|
-
|
|
694
|
+
const containerRef = React6.useRef(null);
|
|
695
|
+
const [width, setWidth] = React6.useState(null);
|
|
696
|
+
React6.useLayoutEffect(() => {
|
|
630
697
|
if (document2) setWidth(containerRef.current?.clientWidth ?? 0);
|
|
631
698
|
}, [document2]);
|
|
632
699
|
if (error) return /* @__PURE__ */ jsx3(Fragment, { children: renderFallback(error) });
|
|
@@ -662,8 +729,8 @@ function PdfCanvasDocument(props) {
|
|
|
662
729
|
);
|
|
663
730
|
}
|
|
664
731
|
function PdfPageView({ document: document2, pageNumber, width, label }) {
|
|
665
|
-
const pageRef =
|
|
666
|
-
const canvasRef =
|
|
732
|
+
const pageRef = React6.useRef(null);
|
|
733
|
+
const canvasRef = React6.useRef(null);
|
|
667
734
|
const isVisible = useIsVisible(pageRef);
|
|
668
735
|
const { isRendered, text } = usePageRender({
|
|
669
736
|
document: document2,
|
|
@@ -680,8 +747,8 @@ function PdfPageView({ document: document2, pageNumber, width, label }) {
|
|
|
680
747
|
] });
|
|
681
748
|
}
|
|
682
749
|
function PdfFrame({ bytes, label, className }) {
|
|
683
|
-
const [objectUrl, setObjectUrl] =
|
|
684
|
-
|
|
750
|
+
const [objectUrl, setObjectUrl] = React6.useState(null);
|
|
751
|
+
React6.useEffect(() => {
|
|
685
752
|
const url = URL.createObjectURL(new Blob([bytes], { type: "application/pdf" }));
|
|
686
753
|
setObjectUrl(url);
|
|
687
754
|
return () => URL.revokeObjectURL(url);
|
|
@@ -696,7 +763,7 @@ var DEFAULT_THUMBNAIL_WIDTH = 96;
|
|
|
696
763
|
var DEFAULT_THUMBNAIL_MAX_BYTES = 50 * 1024 * 1024;
|
|
697
764
|
function PdfThumbnail(props) {
|
|
698
765
|
const { media, contentType, width = DEFAULT_THUMBNAIL_WIDTH, fallback = null, loading = null } = props;
|
|
699
|
-
const containerRef =
|
|
766
|
+
const containerRef = React7.useRef(null);
|
|
700
767
|
const isVisible = useIsVisible(containerRef);
|
|
701
768
|
const pdfjsState = usePdfjs(props.pdfWorkerSrc);
|
|
702
769
|
const labels = usePreviewLabels(props.labels);
|
|
@@ -729,7 +796,7 @@ function PdfThumbnail(props) {
|
|
|
729
796
|
return /* @__PURE__ */ jsx4("div", { ref: containerRef, className: props.className, style: { width }, children: content() });
|
|
730
797
|
}
|
|
731
798
|
function ThumbnailCanvas({ pdfjs, workerSrc, bytes, width, label, fallback, loading }) {
|
|
732
|
-
const canvasRef =
|
|
799
|
+
const canvasRef = React7.useRef(null);
|
|
733
800
|
const { document: document2, error } = usePdfDocument({ pdfjs, bytes, workerSrc });
|
|
734
801
|
const { isRendered } = usePageRender({ document: document2, pageNumber: 1, canvasRef, width, isEnabled: true });
|
|
735
802
|
if (error) return /* @__PURE__ */ jsx4(Fragment2, { children: fallback });
|
|
@@ -821,77 +888,29 @@ function TextPreview({
|
|
|
821
888
|
|
|
822
889
|
// src/react.tsx
|
|
823
890
|
import { Fragment as Fragment4, jsx as jsx6 } from "react/jsx-runtime";
|
|
824
|
-
var DevulturContext =
|
|
825
|
-
var DevulturApiContext =
|
|
826
|
-
var ProgressContext =
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
}
|
|
835
|
-
function DevulturProvider({ api, baseUrl, children }) {
|
|
836
|
-
const [token, setToken] = React7.useState(null);
|
|
837
|
-
const [sessionSecret] = React7.useState(() => typeof crypto !== "undefined" ? crypto.randomUUID() : "");
|
|
838
|
-
const issueViewerToken = useAction(api.issueViewerToken);
|
|
891
|
+
var DevulturContext = React8.createContext(null);
|
|
892
|
+
var DevulturApiContext = React8.createContext(null);
|
|
893
|
+
var ProgressContext = React8.createContext(null);
|
|
894
|
+
function DevulturProvider({ api, baseUrl, tokenArgs, issueViewerToken, children }) {
|
|
895
|
+
const [sessionSecret] = React8.useState(() => typeof crypto !== "undefined" ? crypto.randomUUID() : "");
|
|
896
|
+
const issueViewerTokenAction = useAction(issueViewerToken ?? api.issueViewerToken);
|
|
897
|
+
const { token, reissueToken } = useViewerToken({
|
|
898
|
+
issueViewerToken: issueViewerTokenAction,
|
|
899
|
+
sessionSecret,
|
|
900
|
+
tokenArgs
|
|
901
|
+
});
|
|
839
902
|
const createUploadUrlAction = useAction(api.createUploadUrl);
|
|
840
903
|
const deleteMediaAction = useAction(api.deleteMedia);
|
|
841
904
|
const confirmUploadAction = useAction(api.confirmUpload ?? api.createUploadUrl);
|
|
842
905
|
const deleteVideoAction = useAction(api.deleteVideo ?? api.deleteMedia);
|
|
843
|
-
const
|
|
844
|
-
const reissueInFlightRef = React7.useRef(null);
|
|
845
|
-
const scheduleRefresh = React7.useCallback(
|
|
846
|
-
(ttlMs) => {
|
|
847
|
-
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
848
|
-
const refreshIn = Math.max(ttlMs - REFRESH_BUFFER_MS, MIN_REFRESH_MS);
|
|
849
|
-
refreshTimerRef.current = setTimeout(() => {
|
|
850
|
-
issueViewerToken({ sessionSecret }).then((result) => {
|
|
851
|
-
const { tokenValue, ttlMs: nextTtl } = extractTokenResult(result);
|
|
852
|
-
setToken(tokenValue);
|
|
853
|
-
scheduleRefresh(nextTtl);
|
|
854
|
-
});
|
|
855
|
-
}, refreshIn);
|
|
856
|
-
},
|
|
857
|
-
[issueViewerToken, sessionSecret]
|
|
858
|
-
);
|
|
859
|
-
React7.useEffect(() => {
|
|
860
|
-
if (token) return;
|
|
861
|
-
issueViewerToken({ sessionSecret }).then((result) => {
|
|
862
|
-
const { tokenValue, ttlMs } = extractTokenResult(result);
|
|
863
|
-
setToken(tokenValue);
|
|
864
|
-
scheduleRefresh(ttlMs);
|
|
865
|
-
});
|
|
866
|
-
}, [token, issueViewerToken, scheduleRefresh, sessionSecret]);
|
|
867
|
-
const reissueToken = React7.useCallback(() => {
|
|
868
|
-
if (reissueInFlightRef.current) return reissueInFlightRef.current;
|
|
869
|
-
const pending = issueViewerToken({ sessionSecret }).then((result) => {
|
|
870
|
-
const { tokenValue, ttlMs } = extractTokenResult(result);
|
|
871
|
-
setToken(tokenValue);
|
|
872
|
-
scheduleRefresh(ttlMs);
|
|
873
|
-
reissueInFlightRef.current = null;
|
|
874
|
-
return tokenValue;
|
|
875
|
-
}).catch(() => {
|
|
876
|
-
reissueInFlightRef.current = null;
|
|
877
|
-
return null;
|
|
878
|
-
});
|
|
879
|
-
reissueInFlightRef.current = pending;
|
|
880
|
-
return pending;
|
|
881
|
-
}, [issueViewerToken, scheduleRefresh, sessionSecret]);
|
|
882
|
-
React7.useEffect(() => {
|
|
883
|
-
return () => {
|
|
884
|
-
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
885
|
-
};
|
|
886
|
-
}, []);
|
|
887
|
-
const uploadUrl = React7.useCallback(
|
|
906
|
+
const uploadUrl = React8.useCallback(
|
|
888
907
|
async (file) => {
|
|
889
908
|
const result = await createUploadUrlAction({ filename: file.name, contentType: file.type });
|
|
890
909
|
return { url: result.url, key: result.key };
|
|
891
910
|
},
|
|
892
911
|
[createUploadUrlAction]
|
|
893
912
|
);
|
|
894
|
-
const upload =
|
|
913
|
+
const upload = React8.useCallback(
|
|
895
914
|
async (file, options) => {
|
|
896
915
|
const dimensions = await readImageDimensions(file);
|
|
897
916
|
const target = await createUploadUrlAction({
|
|
@@ -912,14 +931,14 @@ function DevulturProvider({ api, baseUrl, children }) {
|
|
|
912
931
|
},
|
|
913
932
|
[createUploadUrlAction, confirmUploadAction, api.confirmUpload]
|
|
914
933
|
);
|
|
915
|
-
const url =
|
|
934
|
+
const url = React8.useCallback(
|
|
916
935
|
(media, options) => {
|
|
917
936
|
if (!baseUrl) throw new Error("Devultur `url()` requires `baseUrl` on <DevulturProvider>");
|
|
918
937
|
return mediaUrl2(baseUrl, media, { ...options, token: token ?? void 0 });
|
|
919
938
|
},
|
|
920
939
|
[baseUrl, token]
|
|
921
940
|
);
|
|
922
|
-
const getCaptionsText =
|
|
941
|
+
const getCaptionsText = React8.useCallback(
|
|
923
942
|
async (media, locale) => {
|
|
924
943
|
if (!baseUrl) throw new Error("Devultur `getCaptionsText()` requires `baseUrl` on <DevulturProvider>");
|
|
925
944
|
const path = mediaCaptionPath(media, locale);
|
|
@@ -938,7 +957,7 @@ function DevulturProvider({ api, baseUrl, children }) {
|
|
|
938
957
|
},
|
|
939
958
|
[baseUrl, token, reissueToken, sessionSecret]
|
|
940
959
|
);
|
|
941
|
-
const deleteMedia =
|
|
960
|
+
const deleteMedia = React8.useCallback(
|
|
942
961
|
(media) => {
|
|
943
962
|
if (media.kind === "video" && api.deleteVideo) {
|
|
944
963
|
deleteVideoAction({ videoId: media.id, visibility: media.visibility }).catch(console.error);
|
|
@@ -948,7 +967,7 @@ function DevulturProvider({ api, baseUrl, children }) {
|
|
|
948
967
|
},
|
|
949
968
|
[deleteMediaAction, deleteVideoAction, api.deleteVideo]
|
|
950
969
|
);
|
|
951
|
-
const devulturValue =
|
|
970
|
+
const devulturValue = React8.useMemo(
|
|
952
971
|
() => ({
|
|
953
972
|
token,
|
|
954
973
|
baseUrl: baseUrl ?? null,
|
|
@@ -988,13 +1007,13 @@ function ProgressBridge({
|
|
|
988
1007
|
const saveMutation = useMutation(save);
|
|
989
1008
|
const markCompleteMutation = useMutation(markComplete);
|
|
990
1009
|
const resetMutation = useMutation(reset);
|
|
991
|
-
const onSave =
|
|
1010
|
+
const onSave = React8.useCallback(
|
|
992
1011
|
async (videoId, position) => {
|
|
993
1012
|
await saveMutation({ videoId, position });
|
|
994
1013
|
},
|
|
995
1014
|
[saveMutation]
|
|
996
1015
|
);
|
|
997
|
-
const onLoad =
|
|
1016
|
+
const onLoad = React8.useCallback(
|
|
998
1017
|
async (videoId) => {
|
|
999
1018
|
const result = await convex.query(load, { videoId });
|
|
1000
1019
|
if (!result) return null;
|
|
@@ -1005,37 +1024,37 @@ function ProgressBridge({
|
|
|
1005
1024
|
},
|
|
1006
1025
|
[convex, load]
|
|
1007
1026
|
);
|
|
1008
|
-
const onMarkComplete =
|
|
1027
|
+
const onMarkComplete = React8.useCallback(
|
|
1009
1028
|
async (videoId) => {
|
|
1010
1029
|
await markCompleteMutation({ videoId });
|
|
1011
1030
|
},
|
|
1012
1031
|
[markCompleteMutation]
|
|
1013
1032
|
);
|
|
1014
|
-
const onReset =
|
|
1033
|
+
const onReset = React8.useCallback(
|
|
1015
1034
|
async (videoId) => {
|
|
1016
1035
|
await resetMutation({ videoId });
|
|
1017
1036
|
},
|
|
1018
1037
|
[resetMutation]
|
|
1019
1038
|
);
|
|
1020
|
-
const value =
|
|
1039
|
+
const value = React8.useMemo(
|
|
1021
1040
|
() => ({ save: onSave, load: onLoad, markComplete: onMarkComplete, reset: onReset }),
|
|
1022
1041
|
[onSave, onLoad, onMarkComplete, onReset]
|
|
1023
1042
|
);
|
|
1024
1043
|
return /* @__PURE__ */ jsx6(ProgressContext, { value, children });
|
|
1025
1044
|
}
|
|
1026
1045
|
function useDevultur() {
|
|
1027
|
-
const context =
|
|
1046
|
+
const context = React8.use(DevulturContext);
|
|
1028
1047
|
if (!context) throw new Error("useDevultur must be used within <DevulturProvider>");
|
|
1029
1048
|
return context;
|
|
1030
1049
|
}
|
|
1031
1050
|
function useDevulturProgress() {
|
|
1032
|
-
const context =
|
|
1051
|
+
const context = React8.use(ProgressContext);
|
|
1033
1052
|
if (!context) throw new Error("useDevulturProgress requires progress API in DevulturProvider");
|
|
1034
1053
|
return context;
|
|
1035
1054
|
}
|
|
1036
1055
|
function useMediaUrl(key, options) {
|
|
1037
1056
|
const { token, baseUrl } = useDevultur();
|
|
1038
|
-
return
|
|
1057
|
+
return React8.useMemo(() => {
|
|
1039
1058
|
if (!key) return null;
|
|
1040
1059
|
if (!baseUrl) throw new Error("useMediaUrl requires `baseUrl` on <DevulturProvider>");
|
|
1041
1060
|
const isPublic = visibilityFromKey(key) === "public";
|
|
@@ -1063,7 +1082,7 @@ async function readImageDimensions(file) {
|
|
|
1063
1082
|
function useVideoProps(media, options) {
|
|
1064
1083
|
const { token, baseUrl, sessionSecret } = useDevultur();
|
|
1065
1084
|
const locales = options?.locales;
|
|
1066
|
-
return
|
|
1085
|
+
return React8.useMemo(() => {
|
|
1067
1086
|
if (media?.kind !== "video") return null;
|
|
1068
1087
|
if (!baseUrl) throw new Error("useVideoProps requires `baseUrl` on <DevulturProvider>");
|
|
1069
1088
|
const viewerToken = media.visibility === "private" ? token ?? void 0 : void 0;
|
|
@@ -1095,19 +1114,19 @@ function Image({
|
|
|
1095
1114
|
...imgProps
|
|
1096
1115
|
}) {
|
|
1097
1116
|
const { url, token, baseUrl, reissueToken, sessionSecret } = useDevultur();
|
|
1098
|
-
const [failed, setFailed] =
|
|
1099
|
-
const [blobSrc, setBlobSrc] =
|
|
1100
|
-
const sessionRef =
|
|
1101
|
-
|
|
1117
|
+
const [failed, setFailed] = React8.useState(false);
|
|
1118
|
+
const [blobSrc, setBlobSrc] = React8.useState(null);
|
|
1119
|
+
const sessionRef = React8.useRef({ token, reissueToken });
|
|
1120
|
+
React8.useEffect(() => {
|
|
1102
1121
|
sessionRef.current = { token, reissueToken };
|
|
1103
1122
|
});
|
|
1104
1123
|
const isPrivate = media?.visibility === "private";
|
|
1105
1124
|
const hasToken = token !== null;
|
|
1106
|
-
|
|
1125
|
+
React8.useEffect(() => {
|
|
1107
1126
|
setFailed(false);
|
|
1108
1127
|
setBlobSrc(null);
|
|
1109
1128
|
}, [media]);
|
|
1110
|
-
|
|
1129
|
+
React8.useEffect(() => {
|
|
1111
1130
|
if (!isPrivate || failed || !media || !hasToken) return;
|
|
1112
1131
|
let cancelled = false;
|
|
1113
1132
|
let objectUrl = null;
|
|
@@ -1139,7 +1158,7 @@ function Image({
|
|
|
1139
1158
|
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
1140
1159
|
};
|
|
1141
1160
|
}, [isPrivate, failed, media, hasToken, baseUrl, sessionSecret, width, height, fit, format, quality]);
|
|
1142
|
-
const handleError =
|
|
1161
|
+
const handleError = React8.useCallback(() => setFailed(true), []);
|
|
1143
1162
|
let src;
|
|
1144
1163
|
if (failed || !media) {
|
|
1145
1164
|
src = externalFallback ?? null;
|
|
@@ -1150,7 +1169,7 @@ function Image({
|
|
|
1150
1169
|
}
|
|
1151
1170
|
if (!src) return /* @__PURE__ */ jsx6(Fragment4, { children: fallback });
|
|
1152
1171
|
if (render) {
|
|
1153
|
-
return
|
|
1172
|
+
return React8.cloneElement(render, {
|
|
1154
1173
|
style: { ...render.props.style, backgroundImage: `url("${src}")` }
|
|
1155
1174
|
});
|
|
1156
1175
|
}
|
|
@@ -1168,7 +1187,7 @@ function Image({
|
|
|
1168
1187
|
}
|
|
1169
1188
|
function DownloadLink({ media, filename, children, ...anchorProps }) {
|
|
1170
1189
|
const { url, token, baseUrl, sessionSecret, reissueToken } = useDevultur();
|
|
1171
|
-
const handleClick =
|
|
1190
|
+
const handleClick = React8.useCallback(
|
|
1172
1191
|
async (event) => {
|
|
1173
1192
|
if (media?.visibility !== "private" || !baseUrl) return;
|
|
1174
1193
|
event.preventDefault();
|
|
@@ -1192,7 +1211,7 @@ function DownloadLink({ media, filename, children, ...anchorProps }) {
|
|
|
1192
1211
|
return /* @__PURE__ */ jsx6("a", { href: url(media, { download: filename ?? true }), download: true, ...anchorProps, onClick: handleClick, children });
|
|
1193
1212
|
}
|
|
1194
1213
|
function useDevulturMedia(key) {
|
|
1195
|
-
const apiCtx =
|
|
1214
|
+
const apiCtx = React8.use(DevulturApiContext);
|
|
1196
1215
|
if (!apiCtx?.getMedia) {
|
|
1197
1216
|
throw new Error("useDevulturMedia requires getMedia in DevulturProvider api");
|
|
1198
1217
|
}
|
|
@@ -1249,7 +1268,7 @@ function useVideoState(media, options) {
|
|
|
1249
1268
|
const { token, baseUrl, sessionSecret } = useDevultur();
|
|
1250
1269
|
const status = useDevulturMedia(media?.kind === "video" ? media.id : null);
|
|
1251
1270
|
const locales = options?.locales;
|
|
1252
|
-
return
|
|
1271
|
+
return React8.useMemo(() => {
|
|
1253
1272
|
if (media?.kind !== "video") return { phase: "idle" };
|
|
1254
1273
|
if (status.isFailed) {
|
|
1255
1274
|
return {
|
|
@@ -1293,23 +1312,23 @@ function useVideoState(media, options) {
|
|
|
1293
1312
|
}
|
|
1294
1313
|
function useMediaUpload(options) {
|
|
1295
1314
|
const { visibility, onComplete, onError, replaces, process, locales } = options ?? {};
|
|
1296
|
-
const api =
|
|
1315
|
+
const api = React8.use(DevulturApiContext);
|
|
1297
1316
|
if (!api) throw new Error("useMediaUpload must be used within <DevulturProvider>");
|
|
1298
1317
|
const createUploadUrlAction = useAction(api.createUploadUrl);
|
|
1299
1318
|
const deleteMediaAction = useAction(api.deleteMedia);
|
|
1300
1319
|
const confirmUploadAction = useAction(api.confirmUpload ?? api.createUploadUrl);
|
|
1301
1320
|
const processVideoAction = useAction(api.processVideo ?? api.createUploadUrl);
|
|
1302
|
-
const [phase, setPhase] =
|
|
1303
|
-
const [percent, setPercent] =
|
|
1304
|
-
const [currentFile, setCurrentFile] =
|
|
1305
|
-
const [media, setMedia] =
|
|
1306
|
-
const [uploadFailure, setUploadFailure] =
|
|
1307
|
-
const [processRequested, setProcessRequested] =
|
|
1308
|
-
const abortRef =
|
|
1309
|
-
const lastFileRef =
|
|
1310
|
-
const lastKeyRef =
|
|
1321
|
+
const [phase, setPhase] = React8.useState("idle");
|
|
1322
|
+
const [percent, setPercent] = React8.useState(0);
|
|
1323
|
+
const [currentFile, setCurrentFile] = React8.useState(null);
|
|
1324
|
+
const [media, setMedia] = React8.useState(null);
|
|
1325
|
+
const [uploadFailure, setUploadFailure] = React8.useState(null);
|
|
1326
|
+
const [processRequested, setProcessRequested] = React8.useState(false);
|
|
1327
|
+
const abortRef = React8.useRef(null);
|
|
1328
|
+
const lastFileRef = React8.useRef(null);
|
|
1329
|
+
const lastKeyRef = React8.useRef(null);
|
|
1311
1330
|
const videoState = useVideoState(media, { locales });
|
|
1312
|
-
const triggerProcess =
|
|
1331
|
+
const triggerProcess = React8.useCallback(
|
|
1313
1332
|
(key) => {
|
|
1314
1333
|
if (!api.processVideo) {
|
|
1315
1334
|
throw new Error("useMediaUpload: processing a video requires `processVideo` in the <DevulturProvider> api");
|
|
@@ -1322,7 +1341,7 @@ function useMediaUpload(options) {
|
|
|
1322
1341
|
},
|
|
1323
1342
|
[api.processVideo, processVideoAction]
|
|
1324
1343
|
);
|
|
1325
|
-
const runUpload =
|
|
1344
|
+
const runUpload = React8.useCallback(
|
|
1326
1345
|
async (file) => {
|
|
1327
1346
|
const controller = new AbortController();
|
|
1328
1347
|
abortRef.current = controller;
|
|
@@ -1393,10 +1412,10 @@ function useMediaUpload(options) {
|
|
|
1393
1412
|
onError
|
|
1394
1413
|
]
|
|
1395
1414
|
);
|
|
1396
|
-
const cancel =
|
|
1415
|
+
const cancel = React8.useCallback(() => {
|
|
1397
1416
|
abortRef.current?.abort();
|
|
1398
1417
|
}, []);
|
|
1399
|
-
const reset =
|
|
1418
|
+
const reset = React8.useCallback(() => {
|
|
1400
1419
|
abortRef.current?.abort();
|
|
1401
1420
|
setPhase("idle");
|
|
1402
1421
|
setPercent(0);
|
|
@@ -1405,15 +1424,15 @@ function useMediaUpload(options) {
|
|
|
1405
1424
|
setUploadFailure(null);
|
|
1406
1425
|
setProcessRequested(false);
|
|
1407
1426
|
}, []);
|
|
1408
|
-
const retry =
|
|
1427
|
+
const retry = React8.useCallback(() => {
|
|
1409
1428
|
if (phase === "upload-failed" && lastFileRef.current) {
|
|
1410
1429
|
runUpload(lastFileRef.current);
|
|
1411
1430
|
return;
|
|
1412
1431
|
}
|
|
1413
1432
|
if (lastKeyRef.current) triggerProcess(lastKeyRef.current);
|
|
1414
1433
|
}, [phase, runUpload, triggerProcess]);
|
|
1415
|
-
|
|
1416
|
-
const state =
|
|
1434
|
+
React8.useEffect(() => () => abortRef.current?.abort(), []);
|
|
1435
|
+
const state = React8.useMemo(
|
|
1417
1436
|
() => deriveUploadState(
|
|
1418
1437
|
{ phase, percent, file: currentFile, failure: uploadFailure, media, processRequested },
|
|
1419
1438
|
videoState,
|
|
@@ -1421,7 +1440,7 @@ function useMediaUpload(options) {
|
|
|
1421
1440
|
),
|
|
1422
1441
|
[phase, percent, currentFile, uploadFailure, media, processRequested, videoState, retry]
|
|
1423
1442
|
);
|
|
1424
|
-
const upload =
|
|
1443
|
+
const upload = React8.useCallback((file) => runUpload(file), [runUpload]);
|
|
1425
1444
|
return { upload, state, cancel, reset };
|
|
1426
1445
|
}
|
|
1427
1446
|
export {
|
package/dist/validators.d.ts
CHANGED
|
@@ -12,22 +12,22 @@ import { DevulturKind } from '@devultur/core';
|
|
|
12
12
|
*/
|
|
13
13
|
declare const dv: {
|
|
14
14
|
media(kind?: DevulturKind): convex_values.VObject<{
|
|
15
|
-
key?: string | undefined;
|
|
16
15
|
filename?: string | undefined;
|
|
17
|
-
|
|
16
|
+
key?: string | undefined;
|
|
18
17
|
width?: number | undefined;
|
|
19
|
-
|
|
18
|
+
height?: number | undefined;
|
|
20
19
|
id: string;
|
|
21
20
|
kind: DevulturKind;
|
|
21
|
+
visibility: "public" | "private";
|
|
22
22
|
}, {
|
|
23
|
-
kind: convex_values.VLiteral<DevulturKind, "required"> | convex_values.VUnion<"
|
|
23
|
+
kind: convex_values.VLiteral<DevulturKind, "required"> | convex_values.VUnion<"image" | "video" | "file", [convex_values.VLiteral<"image", "required">, convex_values.VLiteral<"video", "required">, convex_values.VLiteral<"file", "required">], "required", never>;
|
|
24
24
|
id: convex_values.VString<string, "required">;
|
|
25
25
|
visibility: convex_values.VUnion<"public" | "private", [convex_values.VLiteral<"public", "required">, convex_values.VLiteral<"private", "required">], "required", never>;
|
|
26
26
|
filename: convex_values.VString<string | undefined, "optional">;
|
|
27
27
|
key: convex_values.VString<string | undefined, "optional">;
|
|
28
28
|
width: convex_values.VFloat64<number | undefined, "optional">;
|
|
29
29
|
height: convex_values.VFloat64<number | undefined, "optional">;
|
|
30
|
-
}, "required", "
|
|
30
|
+
}, "required", "id" | "kind" | "visibility" | "filename" | "key" | "width" | "height">;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
export { dv };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devultur/convex",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Convex component for Devultur media: media records, uploads, transcodes, watch progress and React bindings",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"convex",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"dist"
|
|
56
56
|
],
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@devultur/core": "1.3.
|
|
59
|
-
"@devultur/react": "1.3.
|
|
58
|
+
"@devultur/core": "1.3.2",
|
|
59
|
+
"@devultur/react": "1.3.2"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"convex": ">=1.25.0",
|