@devultur/convex 1.4.1 → 1.4.3

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 (67) hide show
  1. package/README.md +6 -1
  2. package/dist/chunk-JZYKPLI4.js +185 -0
  3. package/dist/{chunk-EX3W77VY.js → chunk-YYOZ3PDH.js} +16 -3
  4. package/dist/component/_generated/api.d.ts +0 -1
  5. package/dist/component/_generated/api.js.map +1 -1
  6. package/dist/component/_generated/component.d.ts +0 -1
  7. package/dist/component/_generated/component.js.map +1 -1
  8. package/dist/component/_generated/dataModel.d.ts +0 -1
  9. package/dist/component/_generated/dataModel.js.map +1 -1
  10. package/dist/component/_generated/server.d.ts +0 -1
  11. package/dist/component/_generated/server.js.map +1 -1
  12. package/dist/component/convex.config.d.ts +0 -1
  13. package/dist/component/convex.config.js.map +1 -1
  14. package/dist/component/http.d.ts +0 -1
  15. package/dist/component/http.js.map +1 -1
  16. package/dist/component/library.d.ts +0 -1
  17. package/dist/component/library.js.map +1 -1
  18. package/dist/component/lock.d.ts +0 -1
  19. package/dist/component/lock.js.map +1 -1
  20. package/dist/component/lockValidators.d.ts +0 -1
  21. package/dist/component/lockValidators.js.map +1 -1
  22. package/dist/component/mediaClient.d.ts +0 -1
  23. package/dist/component/mediaClient.js.map +1 -1
  24. package/dist/component/mediaMeta.d.ts +0 -1
  25. package/dist/component/mediaMeta.js.map +1 -1
  26. package/dist/component/metadata.d.ts +0 -1
  27. package/dist/component/metadata.js.map +1 -1
  28. package/dist/component/public.d.ts +0 -1
  29. package/dist/component/public.js.map +1 -1
  30. package/dist/component/schema.d.ts +0 -1
  31. package/dist/component/schema.js.map +1 -1
  32. package/dist/component/uploadKeys.d.ts +0 -1
  33. package/dist/component/uploadKeys.js.map +1 -1
  34. package/dist/component/uploads.d.ts +0 -1
  35. package/dist/component/uploads.js.map +1 -1
  36. package/dist/component/workerErrors.d.ts +0 -1
  37. package/dist/component/workerErrors.js.map +1 -1
  38. package/dist/{error-labels-BfqcFh2R.d.ts → error-labels-D0Vbp-qA.d.ts} +1 -1
  39. package/dist/index.d.ts +12 -8
  40. package/dist/index.js +1 -1
  41. package/dist/locales/es-formal.d.ts +14 -0
  42. package/dist/locales/es-formal.js +64 -0
  43. package/dist/locales/es.d.ts +7 -4
  44. package/dist/locales/es.js +10 -156
  45. package/dist/{media-picker-labels-CiVcNUZo.d.ts → preview-labels-B7HNa7Va.d.ts} +59 -2
  46. package/dist/react.d.ts +24 -64
  47. package/dist/react.js +37 -17
  48. package/dist/types-DZxN6Eij.d.ts +190 -0
  49. package/package.json +9 -4
  50. package/dist/component/_generated/api.d.ts.map +0 -1
  51. package/dist/component/_generated/component.d.ts.map +0 -1
  52. package/dist/component/_generated/dataModel.d.ts.map +0 -1
  53. package/dist/component/_generated/server.d.ts.map +0 -1
  54. package/dist/component/convex.config.d.ts.map +0 -1
  55. package/dist/component/http.d.ts.map +0 -1
  56. package/dist/component/library.d.ts.map +0 -1
  57. package/dist/component/lock.d.ts.map +0 -1
  58. package/dist/component/lockValidators.d.ts.map +0 -1
  59. package/dist/component/mediaClient.d.ts.map +0 -1
  60. package/dist/component/mediaMeta.d.ts.map +0 -1
  61. package/dist/component/metadata.d.ts.map +0 -1
  62. package/dist/component/public.d.ts.map +0 -1
  63. package/dist/component/schema.d.ts.map +0 -1
  64. package/dist/component/uploadKeys.d.ts.map +0 -1
  65. package/dist/component/uploads.d.ts.map +0 -1
  66. package/dist/component/workerErrors.d.ts.map +0 -1
  67. package/dist/types-LrusGVDf.d.ts +0 -62
@@ -1,162 +1,16 @@
1
1
  import {
2
- formatDuration,
3
- formatFileSize
4
- } from "../chunk-QBHIZBOQ.js";
5
- import {
6
- describeDevulturError,
7
- formatBytes,
8
- formatDate
9
- } from "../chunk-EX3W77VY.js";
10
-
11
- // src/locales/es.ts
12
- var LOCALE = "es";
13
- var devulturErrorLabels = {
14
- file_too_large: (details) => {
15
- const limit = typeof details.limit === "number" ? formatBytes(details.limit, LOCALE) : null;
16
- return limit ? `El archivo supera el tama\xF1o m\xE1ximo (${limit}).` : "El archivo supera el tama\xF1o m\xE1ximo.";
17
- },
18
- content_type_not_allowed: "Este tipo de archivo no est\xE1 permitido.",
19
- content_type_mismatch: "El contenido del archivo no coincide con su tipo.",
20
- content_unverified: "No se pudo verificar la subida. Vuelve a intentarlo.",
21
- unsupported_content_type: "Este tipo de archivo no es compatible.",
22
- media_type_mismatch: "El tipo de archivo no coincide con el tipo de contenido esperado.",
23
- already_exists: "Ya existe un archivo con este nombre.",
24
- write_conflict: "El archivo cambi\xF3 mientras se guardaba. Vuelve a intentarlo.",
25
- upload_not_found: "No se encontr\xF3 la subida o ya venci\xF3. Vuelve a empezarla.",
26
- invalid_upload_key: "Esta subida no es v\xE1lida.",
27
- invalid_path: "La ruta del archivo no es v\xE1lida.",
28
- path_required: "Esta subida necesita una ruta de archivo.",
29
- invalid_expires_in: "El tiempo de vencimiento no es v\xE1lido.",
30
- invalid_file_expires_in: "El tiempo de vencimiento del archivo no es v\xE1lido.",
31
- invalid_cache_control: "La configuraci\xF3n de cach\xE9 no es v\xE1lida.",
32
- invalid_limits: "Los l\xEDmites de la subida no son v\xE1lidos.",
33
- invalid_prefix: "La carpeta no es v\xE1lida.",
34
- invalid_visibility: "La visibilidad debe ser p\xFAblica o privada.",
35
- invalid_cursor: "La p\xE1gina de resultados no es v\xE1lida. Vuelve a cargar la lista.",
36
- invalid_sha256: "La suma de verificaci\xF3n del archivo no es v\xE1lida.",
37
- sha256_mismatch: "El archivo se da\xF1\xF3 durante la subida. Vuelve a intentarlo.",
38
- max_files_reached: "Alcanzaste el n\xFAmero m\xE1ximo de archivos para esta subida.",
39
- invalid_multipart: "La solicitud de subida no es v\xE1lida.",
40
- invalid_size: "El tama\xF1o del archivo no es v\xE1lido.",
41
- invalid_part: "Una parte de la subida no es v\xE1lida. Vuelve a intentarlo.",
42
- invalid_parts: "La subida est\xE1 incompleta. Vuelve a intentarlo.",
43
- part_size_mismatch: "Una parte de la subida tiene un tama\xF1o incorrecto. Vuelve a intentarlo.",
44
- part_sha256_required: "A una parte de la subida le falta la suma de verificaci\xF3n.",
45
- invalid_media_metadata: (details) => {
46
- if (details.field === "title") return "El t\xEDtulo no puede tener m\xE1s de 200 caracteres.";
47
- if (details.field === "alt") return "El texto alternativo no puede tener m\xE1s de 1000 caracteres.";
48
- if (details.field === "tags") return "Usa como m\xE1ximo 20 etiquetas, de hasta 50 caracteres cada una.";
49
- return "Los datos del archivo no son v\xE1lidos.";
50
- },
51
- expired: "Este archivo ya venci\xF3.",
52
- invalid_key: "La referencia del archivo no es v\xE1lida.",
53
- invalid_max_uses: "El n\xFAmero de usos no es v\xE1lido.",
54
- link_exists: "Este enlace de descarga ya existe.",
55
- link_not_found: "Este enlace de descarga no existe.",
56
- link_expired: "Este enlace de descarga ya venci\xF3.",
57
- link_used: "Este enlace de descarga ya se us\xF3.",
58
- not_configured: "Esta funci\xF3n no est\xE1 configurada.",
59
- retention_active: (details) => {
60
- if (details.hold === true) return "Este archivo tiene una retenci\xF3n legal y no se puede modificar ni eliminar.";
61
- const until = formatDate(details.until, LOCALE);
62
- return until ? `Este archivo est\xE1 protegido hasta el ${until} y no se puede modificar ni eliminar antes de esa fecha.` : "Este archivo est\xE1 protegido y no se puede modificar ni eliminar.";
63
- },
64
- lock_conflict: "La protecci\xF3n del archivo cambi\xF3 mientras tanto. Vuelve a intentarlo.",
65
- compliance_extend_only: "La protecci\xF3n de este archivo solo se puede extender.",
66
- lock_privilege_required: "No tienes permiso para acortar ni quitar la protecci\xF3n de este archivo.",
67
- lock_record_unreadable: "No se pudo leer la protecci\xF3n del archivo.",
68
- lock_below_minimum: "Esta subida requiere una protecci\xF3n m\xE1s estricta.",
69
- invalid_lock: "La configuraci\xF3n de protecci\xF3n no es v\xE1lida.",
70
- invalid_hold: "La configuraci\xF3n de retenci\xF3n legal no es v\xE1lida.",
71
- reserved_key: "Este nombre de archivo est\xE1 reservado.",
72
- keys_bucket_missing: "En este momento no se pueden guardar archivos de forma segura.",
73
- encryption_key_missing: "Este archivo ya no se puede leer. Vuelve a subirlo.",
74
- unauthorized: "No tienes autorizaci\xF3n para hacer esto.",
75
- session_mismatch: "Tu sesi\xF3n cambi\xF3. Vuelve a cargar la p\xE1gina.",
76
- token_rejected: "Se deneg\xF3 el acceso.",
77
- reserved_user_id: "Este identificador de usuario est\xE1 reservado.",
78
- invalid_scopes: "Los permisos solicitados no son v\xE1lidos.",
79
- user_mismatch: "Solo puedes ver y cambiar tu propio progreso.",
80
- transcode_not_configured: "El procesamiento de video no est\xE1 configurado.",
81
- transcode_runner_unavailable: "El procesamiento de video est\xE1 ocupado en este momento. Vuelve a intentarlo m\xE1s tarde.",
82
- transcode_launch_failed: "No se pudo procesar el video.",
83
- original_not_retained: "El video original ya no est\xE1 disponible, as\xED que no se puede volver a procesar. Vuelve a subirlo.",
84
- locale_not_allowed: "Los subt\xEDtulos no est\xE1n disponibles en este idioma.",
85
- captions_provider_rate_limited: "El servicio de subt\xEDtulos est\xE1 ocupado en este momento. Vuelve a intentarlo m\xE1s tarde.",
86
- captions_provider_error: "No se pudieron generar los subt\xEDtulos.",
87
- captions_failed: "No se pudieron generar los subt\xEDtulos.",
88
- webhook_secret_missing: "El procesamiento de video no est\xE1 configurado.",
89
- worker_auth_secret_missing: "El servicio de medios no est\xE1 configurado.",
90
- worker_error: "El servicio de medios no pudo completar la solicitud.",
91
- http_error: "No se pudo cargar el archivo.",
92
- too_large: "El archivo es demasiado grande para la vista previa.",
93
- unsupported_type: "Este tipo de archivo no tiene vista previa.",
94
- type_mismatch: "El contenido del archivo no coincide con su tipo.",
95
- token_unavailable: "No se pudo autorizar el acceso a este archivo. Vuelve a intentarlo.",
96
- capability_unavailable: "Esta aplicaci\xF3n no puede hacer eso aqu\xED."
97
- };
98
- var KIND_TEXT = {
99
- image: "Imagen",
100
- video: "Video",
101
- pdf: "PDF",
102
- audio: "Audio",
103
- text: "Texto",
104
- file: "Archivo"
105
- };
106
- var attachmentLabels = {
107
- locale: LOCALE,
108
- size: formatFileSize,
109
- pages: (pageCount) => pageCount === 1 ? "1 p\xE1gina" : `${pageCount.toLocaleString(LOCALE)} p\xE1ginas`,
110
- duration: formatDuration,
111
- kind: (kind) => KIND_TEXT[kind],
112
- download: "Descargar",
113
- open: "Abrir vista previa",
114
- untitled: "Archivo sin t\xEDtulo"
115
- };
116
- var mediaLibraryLabels = {
117
- search: "Buscar archivos",
118
- searchPlaceholder: "Busca por t\xEDtulo o nombre de archivo",
119
- typeFilter: "Tipo",
120
- allTypes: "Todos los tipos",
121
- types: { image: "Im\xE1genes", video: "Videos", file: "Archivos" },
122
- results: "Archivos",
123
- loading: "Cargando archivos\u2026",
124
- empty: "Todav\xEDa no hay archivos.",
125
- noResults: (search) => `Nada coincide con \xAB${search}\xBB.`,
126
- loadMore: "Cargar m\xE1s",
127
- loadingMore: "Cargando\u2026",
128
- error: (error) => describeDevulturError(error, devulturErrorLabels) ?? "No se pudieron cargar los archivos.",
129
- retry: "Reintentar",
130
- size: (bytes) => formatFileSize(bytes, LOCALE)
131
- };
132
- var mediaPickerLabels = {
133
- upload: (multiple) => multiple ? "Subir archivos" : "Subir un archivo",
134
- dropHint: (multiple) => multiple ? "o suelta los archivos aqu\xED" : "o suelta un archivo aqu\xED",
135
- uploads: "Subidas",
136
- progress: (filename) => `Subiendo ${filename}`,
137
- percent: (percent) => `${percent} %`,
138
- uploading: (filename) => `Subiendo ${filename}.`,
139
- uploaded: (filename) => `${filename} se subi\xF3 y qued\xF3 seleccionado.`,
140
- cancel: "Cancelar",
141
- cancelUpload: (filename) => `Cancelar la subida de ${filename}`,
142
- cancelled: (filename) => `Se cancel\xF3 la subida de ${filename}.`,
143
- notAccepted: (filename) => `${filename} no es un tipo de archivo que se pueda elegir aqu\xED.`,
144
- uploadError: (error) => describeDevulturError(error, devulturErrorLabels) ?? "La subida fall\xF3. Vuelve a intentarlo.",
145
- retry: "Reintentar",
146
- dismiss: "Descartar",
147
- selection: "Archivos seleccionados",
148
- empty: "No hay nada seleccionado.",
149
- remove: "Quitar",
150
- removeItem: (name) => `Quitar ${name}`,
151
- clear: "Limpiar",
152
- replace: "Reemplazar",
153
- selected: (name) => `${name} seleccionado.`,
154
- removed: (name) => `${name} se quit\xF3.`,
155
- cleared: "Se limpi\xF3 la selecci\xF3n."
156
- };
2
+ attachmentLabels,
3
+ devulturErrorLabels,
4
+ mediaLibraryLabels,
5
+ mediaPickerLabels,
6
+ previewLabels
7
+ } from "../chunk-JZYKPLI4.js";
8
+ import "../chunk-QBHIZBOQ.js";
9
+ import "../chunk-YYOZ3PDH.js";
157
10
  export {
158
11
  attachmentLabels,
159
12
  devulturErrorLabels,
160
13
  mediaLibraryLabels,
161
- mediaPickerLabels
14
+ mediaPickerLabels,
15
+ previewLabels
162
16
  };
@@ -1,4 +1,4 @@
1
- import { M as MediaRecord } from './types-LrusGVDf.js';
1
+ import { M as MediaRecord } from './types-DZxN6Eij.js';
2
2
 
3
3
  /** What an attachment is, for its icon and type label. */
4
4
  type AttachmentKind = "image" | "video" | "pdf" | "audio" | "text" | "file";
@@ -111,4 +111,61 @@ interface MediaPickerLabels {
111
111
  }
112
112
  declare const defaultMediaPickerLabels: MediaPickerLabels;
113
113
 
114
- export { type AttachmentLabels as A, type MediaLibraryLabels as M, type MediaPickerLabels as a, type AttachmentKind as b, defaultMediaLibraryLabels as c, defaultAttachmentLabels as d, defaultMediaPickerLabels as e, formatDuration as f, formatFileSize as g };
114
+ /** Why a media couldn't be fetched or shown. Stable: match on it, not on `message`. */
115
+ type MediaFetchErrorCode =
116
+ /** The delivery answered with a non-2xx status (`status` has it), or never answered (a network failure: no `status`). */
117
+ "http_error"
118
+ /** `Content-Length`, or the streamed body, exceeds `maxBytes`. */
119
+ | "too_large"
120
+ /** The expected content type isn't one that can be previewed inline. */
121
+ | "unsupported_type"
122
+ /** The response's `Content-Type` doesn't agree with the expected one. */
123
+ | "type_mismatch"
124
+ /** No viewer token for private media: its issuance failed (`useDevultur().tokenError` has why) or was refused. */
125
+ | "token_unavailable"
126
+ /** The delivery refused the token for this browser session, also after a re-issue (video playback). */
127
+ | "session_mismatch"
128
+ /** A PDF has more pages than `maxPages`: only the first ones are shown (`details: { shown, total }`). */
129
+ | "page_cap"
130
+ /** The bytes arrived but couldn't be shown: pdf.js can't open the PDF, or the video can't be played. */
131
+ | "render_failed";
132
+ /** `ErrorOptions` plus the code's `details`. */
133
+ interface MediaFetchErrorOptions extends ErrorOptions {
134
+ details?: Readonly<Record<string, unknown>>;
135
+ }
136
+ declare class MediaFetchError extends Error {
137
+ readonly code: MediaFetchErrorCode;
138
+ /** HTTP status, for `http_error` when the delivery answered. */
139
+ readonly status?: number;
140
+ /** The code's extra fields, e.g. `page_cap`'s `{ shown, total }`. */
141
+ readonly details?: Readonly<Record<string, unknown>>;
142
+ constructor(code: MediaFetchErrorCode, message: string, status?: number, options?: MediaFetchErrorOptions);
143
+ }
144
+
145
+ /**
146
+ * Every string `<Preview>`, `<PdfThumbnail>` and their PDF pages show or announce. Pass any subset as `labels` on a
147
+ * component or on `PdfPreviewProvider`; the component's wins over the provider's, which wins over the English defaults.
148
+ */
149
+ interface PreviewLabels {
150
+ /** Accessible name of a pdf.js document: its title and page count. */
151
+ document: (title: string, pageCount: number) => string;
152
+ /** Accessible name of one pdf.js page. */
153
+ page: (page: number, pageCount: number) => string;
154
+ /** Announced (visually hidden) while a preview loads, when no `loading` is passed. */
155
+ loading: string;
156
+ /** Note after the last rendered page when a PDF has more than `maxPages`; `fallback` follows it. */
157
+ pageCap: (shown: number, total: number) => string;
158
+ /** `title` of the default fallback's download link, for why it's shown (see `<Preview>`'s `fallback`). */
159
+ error: (code: MediaFetchErrorCode) => string;
160
+ /** Accessible name of a `<PdfThumbnail>`, from its title. */
161
+ thumbnail: (title: string) => string;
162
+ /** Name of a PDF (pdf.js document, `<iframe>` title, thumbnail) with no `title` and no filename. */
163
+ untitledPdf: string;
164
+ /** Name of an image or audio preview with no `title` and no filename. */
165
+ untitledMedia: string;
166
+ /** Text of the default fallback's download link when the media has no filename. */
167
+ download: string;
168
+ }
169
+ declare const defaultPreviewLabels: PreviewLabels;
170
+
171
+ export { type AttachmentLabels as A, type MediaLibraryLabels as M, type PreviewLabels as P, type MediaPickerLabels as a, MediaFetchError as b, type AttachmentKind as c, type MediaFetchErrorCode as d, defaultAttachmentLabels as e, defaultMediaLibraryLabels as f, defaultMediaPickerLabels as g, defaultPreviewLabels as h, formatDuration as i, formatFileSize as j };
package/dist/react.d.ts CHANGED
@@ -3,34 +3,13 @@ import { DevulturMedia, MediaUrlOptions, Visibility, 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 { A as AttachmentLabels, M as MediaLibraryLabels, P as PreviewLabels, a as MediaPickerLabels, b as MediaFetchError } from './preview-labels-B7HNa7Va.js';
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-B7HNa7Va.js';
6
8
  import { Value } from 'convex/values';
7
- import { A as AttachmentLabels, M as MediaLibraryLabels, a as MediaPickerLabels } from './media-picker-labels-CiVcNUZo.js';
8
- export { b as AttachmentKind, d as defaultAttachmentLabels, c as defaultMediaLibraryLabels, e as defaultMediaPickerLabels, f as formatDuration, g as formatFileSize } from './media-picker-labels-CiVcNUZo.js';
9
- import { M as MediaRecord } from './types-LrusGVDf.js';
9
+ import { M as MediaRecord } from './types-DZxN6Eij.js';
10
10
  import { P as PdfjsLoader } from './pdf-render-BwJGagvz.js';
11
11
  export { a as PdfjsModule } from './pdf-render-BwJGagvz.js';
12
12
 
13
- /** Why a media fetch was refused. Stable: match on it, not on `message`. */
14
- type MediaFetchErrorCode =
15
- /** The delivery answered with a non-2xx status (`status` has it). */
16
- "http_error"
17
- /** `Content-Length`, or the streamed body, exceeds `maxBytes`. */
18
- | "too_large"
19
- /** The expected content type isn't one that can be previewed inline. */
20
- | "unsupported_type"
21
- /** The response's `Content-Type` doesn't agree with the expected one. */
22
- | "type_mismatch"
23
- /** No viewer token for private media: its issuance failed (`useDevultur().tokenError` has why) or was refused. */
24
- | "token_unavailable"
25
- /** The delivery refused the token for this browser session, also after a re-issue (video playback). */
26
- | "session_mismatch";
27
- declare class MediaFetchError extends Error {
28
- readonly code: MediaFetchErrorCode;
29
- /** HTTP status, for `http_error`. */
30
- readonly status?: number;
31
- constructor(code: MediaFetchErrorCode, message: string, status?: number, options?: ErrorOptions);
32
- }
33
-
34
13
  /** What a viewer token action returns: the token with its lifetime, or the bare token (default lifetime). */
35
14
  type ViewerTokenResult = {
36
15
  token: string;
@@ -46,8 +25,12 @@ type TokenStatus = "idle" | "loading" | "ready" | "error";
46
25
 
47
26
  /** Transform/delivery options for media URLs (without `token`, which the context injects). */
48
27
  type MediaRenderOptions = Omit<MediaUrlOptions, "token">;
28
+ /**
29
+ * The viewer token action: the component's `issueViewerToken` (as `defineDevulturFunctions` or `exposeApi` export it,
30
+ * where `sessionSecret` is optional) or an app's own, which may require it. The providers always send it.
31
+ */
49
32
  type ViewerTokenAction = FunctionReference<"action", "public", {
50
- sessionSecret: string;
33
+ sessionSecret?: string;
51
34
  }>;
52
35
  interface MediaSession {
53
36
  token: string | null;
@@ -311,32 +294,6 @@ interface MediaObjectUrl {
311
294
  */
312
295
  declare function useMediaObjectUrl(media: DevulturMedia | null | undefined, options?: UseMediaObjectUrlOptions): MediaObjectUrl;
313
296
 
314
- /**
315
- * Every string `<Preview>`, `<PdfThumbnail>` and their PDF pages show or announce. Pass any subset as `labels` on a
316
- * component or on `PdfPreviewProvider`; the component's wins over the provider's, which wins over the English defaults.
317
- */
318
- interface PreviewLabels {
319
- /** Accessible name of a pdf.js document: its title and page count. */
320
- document: (title: string, pageCount: number) => string;
321
- /** Accessible name of one pdf.js page. */
322
- page: (page: number, pageCount: number) => string;
323
- /** Announced (visually hidden) while a preview loads, when no `loading` is passed. */
324
- loading: string;
325
- /** Note after the last rendered page when a PDF has more than `maxPages`; `fallback` follows it. */
326
- pageCap: (shown: number, total: number) => string;
327
- /** `title` of the default fallback's download link when a fetch was refused or the type can't be previewed. */
328
- error: (code: MediaFetchErrorCode) => string;
329
- /** Accessible name of a `<PdfThumbnail>`, from its title. */
330
- thumbnail: (title: string) => string;
331
- /** Name of a PDF (pdf.js document, `<iframe>` title, thumbnail) with no `title` and no filename. */
332
- untitledPdf: string;
333
- /** Name of an image or audio preview with no `title` and no filename. */
334
- untitledMedia: string;
335
- /** Text of the default fallback's download link when the media has no filename. */
336
- download: string;
337
- }
338
- declare const defaultPreviewLabels: PreviewLabels;
339
-
340
297
  interface PdfPreviewConfig {
341
298
  /**
342
299
  * Imports pdf.js. Without it, PDFs render in an `<iframe>` (the browser's own viewer) and `<PdfThumbnail>` renders
@@ -489,12 +446,13 @@ type MediaPickerProps = CommonProps & (SingleValueProps | MultipleValueProps);
489
446
  * @example
490
447
  * <MediaPicker accept={["image/*"]} value={cover} onChange={setCover} />
491
448
  */
492
- declare function MediaPicker(props: MediaPickerProps): React.JSX.Element;
493
- declare namespace MediaPicker {
494
- var Upload: typeof MediaPickerUpload;
495
- var Library: typeof MediaPickerLibrary;
496
- var Selection: typeof MediaPickerSelection;
497
- }
449
+ declare function MediaPickerRoot(props: MediaPickerProps): React.JSX.Element;
450
+ declare const MediaPicker: typeof MediaPickerRoot & {
451
+ displayName: string;
452
+ Upload: typeof MediaPickerUpload;
453
+ Library: typeof MediaPickerLibrary;
454
+ Selection: typeof MediaPickerSelection;
455
+ };
498
456
 
499
457
  /** A file's name and type, or a record's filename and content type. */
500
458
  interface Candidate {
@@ -547,13 +505,15 @@ interface PreviewProps {
547
505
  /** Size cap for text previews. Default 1 MiB. */
548
506
  maxTextBytes?: number;
549
507
  /**
550
- * Shown for types that don't preview inline and when the fetch fails. Default: a `<DownloadLink>`.
551
- * As a function it receives why: a `MediaFetchError` when the fetch was refused (`http_error`, `too_large`,
552
- * `type_mismatch`), the viewer token couldn't be issued (`token_unavailable`, nothing fetched), a video's session
553
- * was refused (`session_mismatch`) or the type doesn't preview inline (`unsupported_type`), `null` otherwise
554
- * (a network failure, a PDF pdf.js can't open, or the link after the `maxPages` note).
508
+ * Shown for types that don't preview inline, when the fetch or render fails, and after the `maxPages` note.
509
+ * Default: a `<DownloadLink>`. As a function it receives why, always a `MediaFetchError` (never `null`: it's only
510
+ * called with a cause): `http_error` when the delivery refused the fetch (`status`) or never answered (a network
511
+ * failure, no `status`), `too_large`, `type_mismatch`, `token_unavailable` (no viewer token, nothing fetched),
512
+ * `session_mismatch` (a video's session was refused), `unsupported_type` (the type doesn't preview inline, nothing
513
+ * fetched), `render_failed` (pdf.js can't open the PDF, or the video can't be played) or `page_cap` (the link after
514
+ * the `maxPages` note, with `details: { shown, total }`).
555
515
  */
556
- fallback?: React.ReactNode | ((error: MediaFetchError | null) => React.ReactNode);
516
+ fallback?: React.ReactNode | ((error: MediaFetchError) => React.ReactNode);
557
517
  /** Shown while a file loads. Default: `labels.loading`, visually hidden, in a `role="status"`. `null` → nothing. */
558
518
  loading?: React.ReactNode;
559
519
  /** Accessible name of the preview (`alt`, `title`, `aria-label`). Default: the filename (text: none). */
@@ -869,4 +829,4 @@ interface UseMediaUploadReturn {
869
829
  */
870
830
  declare function useMediaUpload(options?: UseMediaUploadOptions): UseMediaUploadReturn;
871
831
 
872
- export { AttachmentCard, type AttachmentCardProps, AttachmentLabels, type CaptionTrack, CropUpload, type CropUploadLabels, type CropUploadProps, type CropUploadRenderProps, type DevulturApi, type DevulturCapability, DevulturCapabilityError, DevulturProvider, DevulturViewerProvider, DocumentLink, type DocumentLinkProps, DownloadLink, Image, type ImageProps, MediaFetchError, type MediaFetchErrorCode, MediaLibrary, type MediaLibraryItemState, MediaLibraryLabels, type MediaLibraryPart, type MediaLibraryProps, type MediaLibraryState, type MediaLibraryStatus, type MediaObjectUrl, MediaPicker, MediaPickerLabels, type MediaPickerLibraryProps, type MediaPickerProps, type MediaPickerSelectionProps, type MediaPickerState, type MediaPickerUpload$1 as MediaPickerUpload, type MediaPickerUploadProps, type MediaPickerUploads, type PdfPreviewConfig, PdfPreviewProvider, PdfThumbnail, type PdfThumbnailProps, PdfjsLoader, Preview, type PreviewKind, type PreviewLabels, type PreviewProps, type TokenStatus, type UploadedMedia, type UseMediaLibraryOptions, type UseMediaObjectUrlOptions, type UseMediaUploadOptions, type UseMediaUploadReturn, type VideoProps, type VideoState, type ViewerTokenArgs, type ViewerTokenResult, defaultPreviewLabels, matchesAccept, useDevultur, useDevulturApi, useDevulturMedia, useDevulturProgress, useMediaLibrary, useMediaObjectUrl, useMediaPicker, useMediaUpload, useMediaUrl, useVideoProps, useVideoState };
832
+ export { AttachmentCard, type AttachmentCardProps, AttachmentLabels, type CaptionTrack, CropUpload, type CropUploadLabels, type CropUploadProps, type CropUploadRenderProps, type DevulturApi, type DevulturCapability, DevulturCapabilityError, DevulturProvider, DevulturViewerProvider, DocumentLink, type DocumentLinkProps, DownloadLink, Image, type ImageProps, MediaFetchError, MediaLibrary, type MediaLibraryItemState, MediaLibraryLabels, type MediaLibraryPart, type MediaLibraryProps, type MediaLibraryState, type MediaLibraryStatus, type MediaObjectUrl, MediaPicker, MediaPickerLabels, type MediaPickerLibraryProps, type MediaPickerProps, type MediaPickerSelectionProps, type MediaPickerState, type MediaPickerUpload$1 as MediaPickerUpload, type MediaPickerUploadProps, type MediaPickerUploads, type PdfPreviewConfig, PdfPreviewProvider, PdfThumbnail, type PdfThumbnailProps, PdfjsLoader, Preview, type PreviewKind, PreviewLabels, type PreviewProps, type TokenStatus, type UploadedMedia, type UseMediaLibraryOptions, type UseMediaObjectUrlOptions, type UseMediaUploadOptions, type UseMediaUploadReturn, type VideoProps, type VideoState, type ViewerTokenArgs, type ViewerTokenResult, matchesAccept, useDevultur, useDevulturApi, useDevulturMedia, useDevulturProgress, useMediaLibrary, useMediaObjectUrl, useMediaPicker, useMediaUpload, useMediaUrl, useVideoProps, useVideoState };
package/dist/react.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  } from "./chunk-QBHIZBOQ.js";
24
24
  import {
25
25
  describeDevulturError
26
- } from "./chunk-EX3W77VY.js";
26
+ } from "./chunk-YYOZ3PDH.js";
27
27
 
28
28
  // src/react.tsx
29
29
  import React22 from "react";
@@ -71,15 +71,28 @@ async function isSessionMismatch(response) {
71
71
  // src/media-fetch.ts
72
72
  var MediaFetchError = class extends Error {
73
73
  code;
74
- /** HTTP status, for `http_error`. */
74
+ /** HTTP status, for `http_error` when the delivery answered. */
75
75
  status;
76
+ /** The code's extra fields, e.g. `page_cap`'s `{ shown, total }`. */
77
+ details;
76
78
  constructor(code, message, status, options) {
77
79
  super(message, options);
78
80
  this.name = "MediaFetchError";
79
81
  this.code = code;
80
82
  this.status = status;
83
+ this.details = options?.details;
81
84
  }
82
85
  };
86
+ function networkError(cause) {
87
+ return new MediaFetchError("http_error", "Media fetch failed: no response", void 0, { cause });
88
+ }
89
+ function pageCapError(shown, total) {
90
+ const message = `Showing the first ${shown} of ${total} pages`;
91
+ return new MediaFetchError("page_cap", message, void 0, { details: { shown, total } });
92
+ }
93
+ function renderFailedError(cause) {
94
+ return new MediaFetchError("render_failed", "The media couldn't be rendered", void 0, { cause });
95
+ }
83
96
  function tokenUnavailableError(cause) {
84
97
  return new MediaFetchError("token_unavailable", "No viewer token: its issuance failed", void 0, { cause });
85
98
  }
@@ -753,7 +766,9 @@ var ERROR_TEXT = {
753
766
  unsupported_type: "This file type can't be previewed.",
754
767
  type_mismatch: "The file's content doesn't match its type.",
755
768
  token_unavailable: "Access to this file couldn't be granted. Try again.",
756
- session_mismatch: "Your session changed. Reload the page."
769
+ session_mismatch: "Your session changed. Reload the page.",
770
+ page_cap: "Download the file to see every page.",
771
+ render_failed: "The file couldn't be displayed."
757
772
  };
758
773
  var defaultPreviewLabels = {
759
774
  document: (title, pageCount) => `${title}, ${pageCount === 1 ? "1 page" : `${pageCount} pages`}`,
@@ -778,7 +793,7 @@ function mergePreviewLabels(...sources) {
778
793
 
779
794
  // src/pdf-context.tsx
780
795
  import { jsx as jsx5 } from "react/jsx-runtime";
781
- var PdfPreviewContext = React7.createContext({});
796
+ var PdfPreviewContext = /* @__PURE__ */ React7.createContext({});
782
797
  function PdfPreviewProvider({
783
798
  pdfjs,
784
799
  workerSrc,
@@ -961,7 +976,7 @@ function PdfCanvasDocument(props) {
961
976
  React9.useLayoutEffect(() => {
962
977
  if (document2) setWidth(containerRef.current?.clientWidth ?? 0);
963
978
  }, [document2]);
964
- if (error) return /* @__PURE__ */ jsx6(Fragment2, { children: renderFallback(error) });
979
+ if (error) return /* @__PURE__ */ jsx6(Fragment2, { children: renderFallback(renderFailedError(error)) });
965
980
  if (!document2) return /* @__PURE__ */ jsx6(Fragment2, { children: loading });
966
981
  const shownPageCount = Math.min(document2.numPages, Math.max(1, maxPages));
967
982
  const isTruncated = shownPageCount < document2.numPages;
@@ -987,7 +1002,7 @@ function PdfCanvasDocument(props) {
987
1002
  isTruncated && /* @__PURE__ */ jsxs3("p", { children: [
988
1003
  labels.pageCap(shownPageCount, document2.numPages),
989
1004
  " ",
990
- renderFallback(null)
1005
+ renderFallback(pageCapError(shownPageCount, document2.numPages))
991
1006
  ] })
992
1007
  ]
993
1008
  }
@@ -1511,7 +1526,7 @@ function recordMatchesAccept(record, accept) {
1511
1526
 
1512
1527
  // src/media-picker-context.ts
1513
1528
  import React15 from "react";
1514
- var MediaPickerContext = React15.createContext(null);
1529
+ var MediaPickerContext = /* @__PURE__ */ React15.createContext(null);
1515
1530
  function useMediaPicker() {
1516
1531
  const picker = React15.use(MediaPickerContext);
1517
1532
  if (!picker) throw new Error("useMediaPicker must be used within <MediaPicker>");
@@ -1591,13 +1606,15 @@ import React17 from "react";
1591
1606
  import { VideoPlayer } from "@devultur/react";
1592
1607
  import { Fragment as Fragment6, jsx as jsx11 } from "react/jsx-runtime";
1593
1608
  var UNAUTHORIZED = 401;
1609
+ var HLS_NETWORK_FAILURE = /Load(Error|TimeOut)$/;
1594
1610
  function fetchErrorOf(error) {
1595
1611
  if (error.status === UNAUTHORIZED && error.code === "session_mismatch") {
1596
1612
  return new MediaFetchError("session_mismatch", "The delivery refused this session's token", UNAUTHORIZED);
1597
1613
  }
1598
1614
  if (error.status === UNAUTHORIZED) return tokenUnavailableError();
1599
1615
  if (error.status) return new MediaFetchError("http_error", `Video fetch failed: ${error.status}`, error.status);
1600
- return null;
1616
+ if (HLS_NETWORK_FAILURE.test(error.details)) return networkError(error);
1617
+ return renderFailedError(error);
1601
1618
  }
1602
1619
  function VideoPreview({ media, renderFallback, className }) {
1603
1620
  const { tokenStatus, tokenError, reissueToken } = useDevultur();
@@ -1633,7 +1650,7 @@ function Preview({ media, contentType, fallback, loading, labels, ...viewProps }
1633
1650
  const text = usePreviewLabels(labels);
1634
1651
  if (!media) return null;
1635
1652
  const renderFallback = (error) => {
1636
- const fetchError = error instanceof MediaFetchError ? error : null;
1653
+ const fetchError = error instanceof MediaFetchError ? error : networkError(error);
1637
1654
  if (typeof fallback === "function") return fallback(fetchError);
1638
1655
  if (fallback !== void 0) return fallback;
1639
1656
  return /* @__PURE__ */ jsx12(DefaultFallback, { media, error: fetchError, labels: text });
@@ -1660,7 +1677,7 @@ function Preview({ media, contentType, fallback, loading, labels, ...viewProps }
1660
1677
  }
1661
1678
  var unsupportedType = (contentType) => new MediaFetchError("unsupported_type", `Can't preview ${contentType ?? "an unknown type"} inline`);
1662
1679
  function DefaultFallback({ media, error, labels }) {
1663
- return /* @__PURE__ */ jsx12(DownloadLink, { media, title: error ? labels.error(error.code) : void 0, children: media.filename ?? labels.download });
1680
+ return /* @__PURE__ */ jsx12(DownloadLink, { media, title: labels.error(error.code), children: media.filename ?? labels.download });
1664
1681
  }
1665
1682
  function LoadingStatus({ text }) {
1666
1683
  return /* @__PURE__ */ jsx12("span", { role: "status", style: VISUALLY_HIDDEN, children: text });
@@ -1952,7 +1969,7 @@ function emit(props, next) {
1952
1969
  props.onChange(next[0] ?? null);
1953
1970
  }
1954
1971
  var isSame = (a, b) => a.id === b.id;
1955
- function MediaPicker(props) {
1972
+ function MediaPickerRoot(props) {
1956
1973
  const isMultiple = props.multiple ?? false;
1957
1974
  const labels = React21.useMemo(() => mergeMediaPickerLabels(props.labels), [props.labels]);
1958
1975
  const accept = uploadAcceptOf(props.accept, props.type);
@@ -2072,15 +2089,18 @@ function MediaPicker(props) {
2072
2089
  /* @__PURE__ */ jsx15("div", { role: "status", "aria-live": "polite", style: VISUALLY_HIDDEN, children: announcement })
2073
2090
  ] }) });
2074
2091
  }
2075
- MediaPicker.Upload = MediaPickerUpload;
2076
- MediaPicker.Library = MediaPickerLibrary;
2077
- MediaPicker.Selection = MediaPickerSelection;
2092
+ var MediaPicker = /* @__PURE__ */ Object.assign(MediaPickerRoot, {
2093
+ displayName: "MediaPicker",
2094
+ Upload: MediaPickerUpload,
2095
+ Library: MediaPickerLibrary,
2096
+ Selection: MediaPickerSelection
2097
+ });
2078
2098
 
2079
2099
  // src/react.tsx
2080
2100
  import { jsx as jsx16 } from "react/jsx-runtime";
2081
- var DevulturContext = React22.createContext(null);
2082
- var DevulturApiContext = React22.createContext(null);
2083
- var ProgressContext = React22.createContext(null);
2101
+ var DevulturContext = /* @__PURE__ */ React22.createContext(null);
2102
+ var DevulturApiContext = /* @__PURE__ */ React22.createContext(null);
2103
+ var ProgressContext = /* @__PURE__ */ React22.createContext(null);
2084
2104
  function DevulturProvider({ api, baseUrl, tokenArgs, issueViewerToken, children }) {
2085
2105
  const session = useMediaSession({ issueViewerToken: issueViewerToken ?? api.issueViewerToken, tokenArgs, baseUrl });
2086
2106
  const createUploadUrlAction = useAction2(api.createUploadUrl);