@devultur/convex 1.5.1 → 1.5.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 CHANGED
@@ -276,12 +276,28 @@ function Avatar({ media, fallbackUrl }) {
276
276
  }
277
277
  ```
278
278
 
279
+ `useDevulturOptional()` is non-null under a `<DevulturProvider>` given no `baseUrl` too, where the hooks that build
280
+ URLs throw. `useCanDeliver()` answers that instead: `true` only under a provider with a non-empty `baseUrl`.
281
+
282
+ ```tsx
283
+ function Cover({ media, fallbackUrl }) {
284
+ if (!useCanDeliver() || !media) return <img src={fallbackUrl} alt="" />;
285
+ return <Image media={media} alt="" />;
286
+ }
287
+ ```
288
+
289
+ Public media needs nothing more. Private media also needs the viewer token, which a provider may not have yet
290
+ (`lazyToken`) or can't issue (a `<DevulturViewerProvider>` without `issueViewerToken`); `tokenStatus` from
291
+ `useDevultur()` tells, and the components show their error state when it fails.
292
+
279
293
  ### Only the hooks
280
294
 
281
295
  `@devultur/convex/react/hooks` exports the contexts and the hooks that only read them, without the components or
282
- Convex's client: `useDevultur`, `useDevulturOptional`, `useDevulturApi`, `useDevulturProgress`, `useMediaUrl`,
283
- `useVideoProps`, and the `DevulturContext`, `DevulturApiContext` and `DevulturProgressContext` objects. A shared
284
- component that only checks for a provider adds well under 1 kB gzipped to the bundle:
296
+ Convex's client: `useDevultur`, `useDevulturOptional`, `useCanDeliver`, `useDevulturApi`, `useDevulturProgress`,
297
+ `useMediaUrl`, `useVideoProps`, and the `DevulturContext`, `DevulturApiContext` and `DevulturProgressContext` objects.
298
+ The contexts and the hooks that only read them sit in a chunk of their own, apart from `useMediaUrl`, `useVideoProps`
299
+ and `@devultur/core`'s URL helpers, so a shared component that only checks for a provider adds under 0.5 kB gzipped,
300
+ even to the entry of an app that lazy-loads `@devultur/convex/react` on a route:
285
301
 
286
302
  ```tsx
287
303
  import { useDevulturOptional } from "@devultur/convex/react/hooks";
@@ -1,46 +1,9 @@
1
- // src/capability-error.ts
2
- var DevulturCapabilityError = class extends Error {
3
- code = "capability_unavailable";
4
- capability;
5
- constructor(capability) {
6
- super(
7
- `Devultur: \`${capability}\` isn't available under <DevulturViewerProvider>. Use <DevulturProvider api> with the actions it needs.`
8
- );
9
- this.name = "DevulturCapabilityError";
10
- this.capability = capability;
11
- }
12
- };
13
-
14
- // src/devultur-context.ts
15
- import React from "react";
16
- var DevulturContext = /* @__PURE__ */ React.createContext(null);
17
- var DevulturApiContext = /* @__PURE__ */ React.createContext(null);
18
- function useDevultur() {
19
- const context = React.use(DevulturContext);
20
- if (!context) throw new Error("useDevultur must be used within <DevulturProvider> or <DevulturViewerProvider>");
21
- return context;
22
- }
23
- function useDevulturOptional() {
24
- return React.use(DevulturContext);
25
- }
26
- function useDevulturApi() {
27
- const api = React.use(DevulturApiContext);
28
- if (api) return api;
29
- if (React.use(DevulturContext)) throw new DevulturCapabilityError("api");
30
- throw new Error("useDevulturApi must be used within <DevulturProvider>");
31
- }
32
-
33
- // src/progress-context.ts
34
- import React2 from "react";
35
- var DevulturProgressContext = /* @__PURE__ */ React2.createContext(null);
36
- function useDevulturProgress() {
37
- const context = React2.use(DevulturProgressContext);
38
- if (!context) throw new Error("useDevulturProgress requires progress API in DevulturProvider");
39
- return context;
40
- }
1
+ import {
2
+ useDevultur
3
+ } from "./chunk-FOBFHC4A.js";
41
4
 
42
5
  // src/media-url-hooks.ts
43
- import React4 from "react";
6
+ import React2 from "react";
44
7
  import {
45
8
  buildMediaUrl,
46
9
  CaptionLocaleLabel,
@@ -50,11 +13,11 @@ import {
50
13
  } from "@devultur/core";
51
14
 
52
15
  // src/request-token.ts
53
- import React3 from "react";
16
+ import React from "react";
54
17
  function useRequestTokenWhen(session, isNeeded) {
55
18
  const { requestToken, token } = session;
56
19
  const hasToken = token !== null;
57
- React3.useEffect(() => {
20
+ React.useEffect(() => {
58
21
  if (isNeeded && !hasToken) requestToken();
59
22
  }, [isNeeded, hasToken, requestToken]);
60
23
  }
@@ -64,7 +27,7 @@ function useMediaUrl(key, options) {
64
27
  const devultur = useDevultur();
65
28
  const { token, baseUrl } = devultur;
66
29
  useRequestTokenWhen(devultur, Boolean(key) && visibilityFromKey(key ?? "") !== "public");
67
- return React4.useMemo(() => {
30
+ return React2.useMemo(() => {
68
31
  if (!key) return null;
69
32
  if (!baseUrl) throw new Error("useMediaUrl requires `baseUrl` on <DevulturProvider>");
70
33
  const isPublic = visibilityFromKey(key) === "public";
@@ -76,7 +39,7 @@ function useVideoProps(media, options) {
76
39
  const { token, baseUrl, sessionSecret } = devultur;
77
40
  useRequestTokenWhen(devultur, media?.kind === "video" && media.visibility === "private");
78
41
  const locales = options?.locales;
79
- return React4.useMemo(() => {
42
+ return React2.useMemo(() => {
80
43
  if (media?.kind !== "video") return null;
81
44
  if (!baseUrl) throw new Error("useVideoProps requires `baseUrl` on <DevulturProvider>");
82
45
  const viewerToken = media.visibility === "private" ? token ?? void 0 : void 0;
@@ -96,14 +59,6 @@ function useVideoProps(media, options) {
96
59
  }
97
60
 
98
61
  export {
99
- DevulturCapabilityError,
100
- DevulturContext,
101
- DevulturApiContext,
102
- useDevultur,
103
- useDevulturOptional,
104
- useDevulturApi,
105
- DevulturProgressContext,
106
- useDevulturProgress,
107
62
  useRequestTokenWhen,
108
63
  useMediaUrl,
109
64
  useVideoProps
@@ -0,0 +1,55 @@
1
+ // src/capability-error.ts
2
+ var DevulturCapabilityError = class extends Error {
3
+ code = "capability_unavailable";
4
+ capability;
5
+ constructor(capability) {
6
+ super(
7
+ `Devultur: \`${capability}\` isn't available under <DevulturViewerProvider>. Use <DevulturProvider api> with the actions it needs.`
8
+ );
9
+ this.name = "DevulturCapabilityError";
10
+ this.capability = capability;
11
+ }
12
+ };
13
+
14
+ // src/devultur-context.ts
15
+ import React from "react";
16
+ var DevulturContext = /* @__PURE__ */ React.createContext(null);
17
+ var DevulturApiContext = /* @__PURE__ */ React.createContext(null);
18
+ function useDevultur() {
19
+ const context = React.use(DevulturContext);
20
+ if (!context) throw new Error("useDevultur must be used within <DevulturProvider> or <DevulturViewerProvider>");
21
+ return context;
22
+ }
23
+ function useDevulturOptional() {
24
+ return React.use(DevulturContext);
25
+ }
26
+ function useCanDeliver() {
27
+ return Boolean(React.use(DevulturContext)?.baseUrl);
28
+ }
29
+ function useDevulturApi() {
30
+ const api = React.use(DevulturApiContext);
31
+ if (api) return api;
32
+ if (React.use(DevulturContext)) throw new DevulturCapabilityError("api");
33
+ throw new Error("useDevulturApi must be used within <DevulturProvider>");
34
+ }
35
+
36
+ // src/progress-context.ts
37
+ import React2 from "react";
38
+ var DevulturProgressContext = /* @__PURE__ */ React2.createContext(null);
39
+ function useDevulturProgress() {
40
+ const context = React2.use(DevulturProgressContext);
41
+ if (!context) throw new Error("useDevulturProgress requires progress API in DevulturProvider");
42
+ return context;
43
+ }
44
+
45
+ export {
46
+ DevulturCapabilityError,
47
+ DevulturContext,
48
+ DevulturApiContext,
49
+ useDevultur,
50
+ useDevulturOptional,
51
+ useCanDeliver,
52
+ useDevulturApi,
53
+ DevulturProgressContext,
54
+ useDevulturProgress
55
+ };
File without changes
@@ -120,42 +120,20 @@ declare function useDevultur(): DevulturContextValue;
120
120
  * `<DevulturViewerProvider>`, and `null` outside both instead of throwing.
121
121
  */
122
122
  declare function useDevulturOptional(): DevulturContextValue | null;
123
+ /**
124
+ * Whether the media hooks can build delivery URLs here: `true` under `<DevulturProvider>` or `<DevulturViewerProvider>`
125
+ * with a non-empty `baseUrl`; `false` outside both, or under a `<DevulturProvider>` without one, where `useMediaUrl`,
126
+ * `useVideoProps` and `url()` throw. That's all a public media needs. A private media also needs the viewer token,
127
+ * which the provider may not have yet (lazy issuance) or can't issue (no `issueViewerToken`): `tokenStatus` from
128
+ * `useDevultur()` tells, and the components handle it themselves.
129
+ */
130
+ declare function useCanDeliver(): boolean;
123
131
  /**
124
132
  * The `api` passed to `<DevulturProvider>`: your app's exported Devultur functions. Under a `<DevulturViewerProvider>`
125
133
  * it throws a `DevulturCapabilityError`.
126
134
  */
127
135
  declare function useDevulturApi(): DevulturApi;
128
136
 
129
- /**
130
- * Ready-to-use delivery URL for a key, with the context's viewer token already injected. The consumer
131
- * never touches the token or the query — it only declares what it wants (download, resize, etc.).
132
- *
133
- * @example
134
- * <img src={useMediaUrl(key)} />
135
- * <a href={useMediaUrl(key, { download: true })} download>Download</a>
136
- * <img src={useMediaUrl(key, { width: 512, format: "webp" })} />
137
- */
138
- declare function useMediaUrl(key: string | null | undefined, options?: MediaRenderOptions): string | null;
139
- interface VideoProps {
140
- src: string;
141
- token: string | undefined;
142
- captions: {
143
- locale: string;
144
- label: string;
145
- src: string;
146
- }[];
147
- /** Session secret → `<VideoPlayer>` sends it in `${SESSION_HEADER}` on hls.js/captions requests. */
148
- sessionSecret?: string;
149
- }
150
- /**
151
- * Token-transparent props for `<VideoPlayer>` from a video reference: builds the HLS playlist `src`,
152
- * the viewer `token` (only if private), and the `captions` for the requested locales.
153
- * The consumer just does `<VideoPlayer {...useVideoProps(lesson.video, { locales })} />`.
154
- */
155
- declare function useVideoProps(media: DevulturMedia | null | undefined, options?: {
156
- locales?: string[];
157
- }): VideoProps | null;
158
-
159
137
  interface DevulturProgressContextValue {
160
138
  save: (videoId: string, position: number) => Promise<void>;
161
139
  load: (videoId: string) => Promise<{
@@ -168,4 +146,4 @@ interface DevulturProgressContextValue {
168
146
  declare const DevulturProgressContext: React.Context<DevulturProgressContextValue | null>;
169
147
  declare function useDevulturProgress(): DevulturProgressContextValue;
170
148
 
171
- export { type DevulturApi as D, type TokenStatus as T, type UploadOptions as U, type VideoProps as V, type ViewerTokenAction as a, type ViewerTokenArgs as b, DevulturApiContext as c, DevulturContext as d, type DevulturContextValue as e, DevulturProgressContext as f, type DevulturProgressContextValue as g, type UploadedMedia as h, type ViewerTokenResult as i, useDevulturApi as j, useDevulturOptional as k, useDevulturProgress as l, useMediaUrl as m, useVideoProps as n, useDevultur as u };
149
+ export { type DevulturApi as D, type MediaRenderOptions as M, type TokenStatus as T, type UploadOptions as U, type ViewerTokenAction as V, type ViewerTokenArgs as a, DevulturApiContext as b, DevulturContext as c, type DevulturContextValue as d, DevulturProgressContext as e, type DevulturProgressContextValue as f, type UploadedMedia as g, type ViewerTokenResult as h, useDevultur as i, useDevulturApi as j, useDevulturOptional as k, useDevulturProgress as l, useCanDeliver as u };
@@ -0,0 +1,6 @@
1
+ export { D as DevulturApi, b as DevulturApiContext, c as DevulturContext, d as DevulturContextValue, e as DevulturProgressContext, f as DevulturProgressContextValue, U as UploadOptions, g as UploadedMedia, u as useCanDeliver, i as useDevultur, j as useDevulturApi, k as useDevulturOptional, l as useDevulturProgress } from '../contexts-6F8TJINX.js';
2
+ import 'react';
3
+ import '@devultur/core';
4
+ import '@devultur/react';
5
+ import 'convex/server';
6
+ import 'convex/values';
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import "../chunk-O76FU54F.js";
3
+ import {
4
+ DevulturApiContext,
5
+ DevulturContext,
6
+ DevulturProgressContext,
7
+ useCanDeliver,
8
+ useDevultur,
9
+ useDevulturApi,
10
+ useDevulturOptional,
11
+ useDevulturProgress
12
+ } from "../chunk-FOBFHC4A.js";
13
+ export {
14
+ DevulturApiContext,
15
+ DevulturContext,
16
+ DevulturProgressContext,
17
+ useCanDeliver,
18
+ useDevultur,
19
+ useDevulturApi,
20
+ useDevulturOptional,
21
+ useDevulturProgress
22
+ };
@@ -1,6 +1,39 @@
1
- export { D as DevulturApi, c as DevulturApiContext, d as DevulturContext, e as DevulturContextValue, f as DevulturProgressContext, g as DevulturProgressContextValue, U as UploadOptions, h as UploadedMedia, V as VideoProps, u as useDevultur, j as useDevulturApi, k as useDevulturOptional, l as useDevulturProgress, m as useMediaUrl, n as useVideoProps } from '../hooks-uqpBd5UF.js';
1
+ import { DevulturMedia } from '@devultur/core';
2
+ import { M as MediaRenderOptions } from '../contexts-6F8TJINX.js';
3
+ export { D as DevulturApi, b as DevulturApiContext, c as DevulturContext, d as DevulturContextValue, e as DevulturProgressContext, f as DevulturProgressContextValue, U as UploadOptions, g as UploadedMedia, u as useCanDeliver, i as useDevultur, j as useDevulturApi, k as useDevulturOptional, l as useDevulturProgress } from '../contexts-6F8TJINX.js';
2
4
  import 'react';
3
- import '@devultur/core';
4
5
  import '@devultur/react';
5
6
  import 'convex/server';
6
7
  import 'convex/values';
8
+
9
+ /**
10
+ * Ready-to-use delivery URL for a key, with the context's viewer token already injected. The consumer
11
+ * never touches the token or the query — it only declares what it wants (download, resize, etc.).
12
+ *
13
+ * @example
14
+ * <img src={useMediaUrl(key)} />
15
+ * <a href={useMediaUrl(key, { download: true })} download>Download</a>
16
+ * <img src={useMediaUrl(key, { width: 512, format: "webp" })} />
17
+ */
18
+ declare function useMediaUrl(key: string | null | undefined, options?: MediaRenderOptions): string | null;
19
+ interface VideoProps {
20
+ src: string;
21
+ token: string | undefined;
22
+ captions: {
23
+ locale: string;
24
+ label: string;
25
+ src: string;
26
+ }[];
27
+ /** Session secret → `<VideoPlayer>` sends it in `${SESSION_HEADER}` on hls.js/captions requests. */
28
+ sessionSecret?: string;
29
+ }
30
+ /**
31
+ * Token-transparent props for `<VideoPlayer>` from a video reference: builds the HLS playlist `src`,
32
+ * the viewer `token` (only if private), and the `captions` for the requested locales.
33
+ * The consumer just does `<VideoPlayer {...useVideoProps(lesson.video, { locales })} />`.
34
+ */
35
+ declare function useVideoProps(media: DevulturMedia | null | undefined, options?: {
36
+ locales?: string[];
37
+ }): VideoProps | null;
38
+
39
+ export { type VideoProps, useMediaUrl, useVideoProps };
@@ -1,19 +1,24 @@
1
1
  "use client";
2
+ import {
3
+ useMediaUrl,
4
+ useVideoProps
5
+ } from "../chunk-AUAWZ5BD.js";
6
+ import "../chunk-O76FU54F.js";
2
7
  import {
3
8
  DevulturApiContext,
4
9
  DevulturContext,
5
10
  DevulturProgressContext,
11
+ useCanDeliver,
6
12
  useDevultur,
7
13
  useDevulturApi,
8
14
  useDevulturOptional,
9
- useDevulturProgress,
10
- useMediaUrl,
11
- useVideoProps
12
- } from "../chunk-BDEO67OT.js";
15
+ useDevulturProgress
16
+ } from "../chunk-FOBFHC4A.js";
13
17
  export {
14
18
  DevulturApiContext,
15
19
  DevulturContext,
16
20
  DevulturProgressContext,
21
+ useCanDeliver,
17
22
  useDevultur,
18
23
  useDevulturApi,
19
24
  useDevulturOptional,
package/dist/react.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  import React from 'react';
2
- import { V as VideoProps, a as ViewerTokenAction, b as ViewerTokenArgs, D as DevulturApi } from './hooks-uqpBd5UF.js';
3
- export { c as DevulturApiContext, d as DevulturContext, e as DevulturContextValue, f as DevulturProgressContext, g as DevulturProgressContextValue, T as TokenStatus, U as UploadOptions, h as UploadedMedia, i as ViewerTokenResult, u as useDevultur, j as useDevulturApi, k as useDevulturOptional, l as useDevulturProgress, m as useMediaUrl, n as useVideoProps } from './hooks-uqpBd5UF.js';
2
+ import { V as ViewerTokenAction, a as ViewerTokenArgs, D as DevulturApi } from './contexts-6F8TJINX.js';
3
+ export { b as DevulturApiContext, c as DevulturContext, d as DevulturContextValue, e as DevulturProgressContext, f as DevulturProgressContextValue, T as TokenStatus, U as UploadOptions, g as UploadedMedia, h as ViewerTokenResult, u as useCanDeliver, i as useDevultur, j as useDevulturApi, k as useDevulturOptional, l as useDevulturProgress } from './contexts-6F8TJINX.js';
4
4
  import { BakeOutputFormat, MediaUploadState, MediaUploadError } from '@devultur/react';
5
5
  export { MediaUploadError, MediaUploadState, UploadProgress } from '@devultur/react';
6
6
  import { A as AttachmentLabels, b as MediaFetchError, M as MediaLibraryLabels, P as PreviewLabels, a as MediaPickerLabels } from './preview-labels-C2nUUY_f.js';
7
7
  export { c as AttachmentKind, d as MediaFetchErrorCode, e as defaultAttachmentLabels, f as defaultMediaLibraryLabels, g as defaultMediaPickerLabels, h as defaultPreviewLabels, i as formatDuration, j as formatFileSize } from './preview-labels-C2nUUY_f.js';
8
8
  import { Visibility, DevulturMedia, MediaUrlOptions, MediaStatusValue, ProcessingPhase, FailureReason } from '@devultur/core';
9
+ import { VideoProps } from './react/hooks.js';
10
+ export { useMediaUrl, useVideoProps } from './react/hooks.js';
9
11
  import { M as MediaRecord } from './types-C0ruHUcu.js';
10
12
  import { P as PdfjsLoader } from './pdf-render-BwJGagvz.js';
11
13
  export { a as PdfjsModule } from './pdf-render-BwJGagvz.js';
package/dist/react.js CHANGED
@@ -1,4 +1,9 @@
1
1
  "use client";
2
+ import {
3
+ useMediaUrl,
4
+ useRequestTokenWhen,
5
+ useVideoProps
6
+ } from "./chunk-AUAWZ5BD.js";
2
7
  import {
3
8
  mediaFromRecord
4
9
  } from "./chunk-KAIVI7RB.js";
@@ -29,14 +34,12 @@ import {
29
34
  DevulturCapabilityError,
30
35
  DevulturContext,
31
36
  DevulturProgressContext,
37
+ useCanDeliver,
32
38
  useDevultur,
33
39
  useDevulturApi,
34
40
  useDevulturOptional,
35
- useDevulturProgress,
36
- useMediaUrl,
37
- useRequestTokenWhen,
38
- useVideoProps
39
- } from "./chunk-BDEO67OT.js";
41
+ useDevulturProgress
42
+ } from "./chunk-FOBFHC4A.js";
40
43
 
41
44
  // src/react.tsx
42
45
  import React26 from "react";
@@ -2609,6 +2612,7 @@ export {
2609
2612
  formatDuration,
2610
2613
  formatFileSize,
2611
2614
  matchesAccept,
2615
+ useCanDeliver,
2612
2616
  useDevultur,
2613
2617
  useDevulturApi,
2614
2618
  useDevulturMedia,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devultur/convex",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "description": "Convex component for Devultur media: media records, uploads, transcodes, watch progress and React bindings",
5
5
  "keywords": [
6
6
  "convex",
@@ -69,8 +69,8 @@
69
69
  "docs"
70
70
  ],
71
71
  "dependencies": {
72
- "@devultur/core": "1.5.1",
73
- "@devultur/react": "1.5.1"
72
+ "@devultur/core": "1.5.2",
73
+ "@devultur/react": "1.5.2"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "convex": ">=1.25.0",
@@ -102,7 +102,7 @@
102
102
  "access": "public"
103
103
  },
104
104
  "scripts": {
105
- "build": "tsup src/index.ts src/validators.ts src/react.tsx src/react/hooks.ts src/pdfjs.ts src/locales/es.ts src/locales/es-formal.ts src/bin/init.ts --format esm --dts --clean && tsc -p src/component/tsconfig.json && node scripts/add-import-extensions.ts && node ../../scripts/copy-guides.mts",
105
+ "build": "tsup src/index.ts src/validators.ts src/react.tsx src/react/hooks.ts src/react/contexts.ts src/pdfjs.ts src/locales/es.ts src/locales/es-formal.ts src/bin/init.ts --format esm --dts --clean && tsc -p src/component/tsconfig.json && node scripts/add-import-extensions.ts && node ../../scripts/copy-guides.mts",
106
106
  "codegen": "node scripts/codegen.ts",
107
107
  "dev": "tsup src/index.ts src/react.tsx --format esm --dts --watch",
108
108
  "typecheck": "tsc --noEmit"