@devultur/convex 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +462 -9
- package/dist/bin/init.js +44 -23
- package/dist/chunk-EX3W77VY.js +245 -0
- package/dist/chunk-HOFTKJWT.js +58 -0
- package/dist/chunk-KAIVI7RB.js +21 -0
- package/dist/chunk-QBHIZBOQ.js +139 -0
- package/dist/{chunk-GKLFRSGI.js → chunk-YIPZZDGD.js} +8 -1
- package/dist/component/_generated/api.d.ts +6 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +27 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/library.d.ts +93 -0
- package/dist/component/library.d.ts.map +1 -0
- package/dist/component/library.js +74 -0
- package/dist/component/library.js.map +1 -0
- package/dist/component/mediaMeta.d.ts +46 -0
- package/dist/component/mediaMeta.d.ts.map +1 -0
- package/dist/component/mediaMeta.js +82 -0
- package/dist/component/mediaMeta.js.map +1 -0
- package/dist/component/metadata.d.ts +59 -0
- package/dist/component/metadata.d.ts.map +1 -0
- package/dist/component/metadata.js +32 -0
- package/dist/component/metadata.js.map +1 -0
- package/dist/component/public.d.ts +17 -1
- package/dist/component/public.d.ts.map +1 -1
- package/dist/component/public.js +20 -11
- package/dist/component/public.js.map +1 -1
- package/dist/component/schema.d.ts +17 -2
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +13 -1
- package/dist/component/schema.js.map +1 -1
- package/dist/component/uploads.d.ts +13 -0
- package/dist/component/uploads.d.ts.map +1 -1
- package/dist/component/uploads.js +24 -5
- package/dist/component/uploads.js.map +1 -1
- package/dist/error-labels-BfqcFh2R.d.ts +19 -0
- package/dist/index.d.ts +204 -93
- package/dist/index.js +200 -128
- package/dist/locales/es.d.ts +11 -0
- package/dist/locales/es.js +162 -0
- package/dist/media-picker-labels-CiVcNUZo.d.ts +114 -0
- package/dist/{pdf-render-CiCK6V1X.d.ts → pdf-render-BwJGagvz.d.ts} +1 -1
- package/dist/pdfjs.d.ts +13 -2
- package/dist/pdfjs.js +9 -0
- package/dist/react.d.ts +462 -64
- package/dist/react.js +1625 -493
- package/dist/types-LrusGVDf.d.ts +62 -0
- package/dist/validators.d.ts +13 -1
- package/dist/validators.js +1 -1
- package/package.json +8 -4
package/dist/index.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mediaFromRecord
|
|
3
|
+
} from "./chunk-KAIVI7RB.js";
|
|
1
4
|
import {
|
|
2
5
|
dv
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YIPZZDGD.js";
|
|
7
|
+
import {
|
|
8
|
+
DEVULTUR_ERROR_CODES,
|
|
9
|
+
describeDevulturError,
|
|
10
|
+
devulturErrorLabels,
|
|
11
|
+
devulturErrorOf,
|
|
12
|
+
isDevulturError,
|
|
13
|
+
isDevulturErrorCode
|
|
14
|
+
} from "./chunk-EX3W77VY.js";
|
|
4
15
|
|
|
5
16
|
// src/index.ts
|
|
6
17
|
import {
|
|
@@ -15,11 +26,148 @@ import {
|
|
|
15
26
|
TranslationProvider
|
|
16
27
|
} from "@devultur/core";
|
|
17
28
|
|
|
18
|
-
// src/
|
|
29
|
+
// src/exposed-functions.ts
|
|
19
30
|
import { extractId } from "@devultur/core";
|
|
20
|
-
import {
|
|
21
|
-
|
|
31
|
+
import {
|
|
32
|
+
actionGeneric,
|
|
33
|
+
internalActionGeneric,
|
|
34
|
+
internalMutationGeneric,
|
|
35
|
+
internalQueryGeneric,
|
|
36
|
+
mutationGeneric,
|
|
37
|
+
paginationOptsValidator,
|
|
38
|
+
queryGeneric
|
|
39
|
+
} from "convex/server";
|
|
40
|
+
import { ConvexError, v } from "convex/values";
|
|
41
|
+
function exposed(kind, args, handler) {
|
|
42
|
+
return { kind, args, handler };
|
|
43
|
+
}
|
|
44
|
+
var visibilityArg = v.optional(v.union(v.literal("public"), v.literal("private")));
|
|
45
|
+
var mediaTypeArg = v.optional(v.union(v.literal("video"), v.literal("image"), v.literal("file")));
|
|
46
|
+
var progressArgs = { userId: v.string(), videoId: v.string() };
|
|
47
|
+
function progressArgsFor(args, caller) {
|
|
48
|
+
if (!caller.userId || caller.canActForOthers || args.userId === caller.userId) return args;
|
|
49
|
+
throw new ConvexError({
|
|
50
|
+
code: "user_mismatch",
|
|
51
|
+
message: "This progress belongs to another user",
|
|
52
|
+
status: 403
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function mediaIdOf(key) {
|
|
56
|
+
return key.includes("/") ? extractId(key) : key;
|
|
57
|
+
}
|
|
58
|
+
function exposedFunctions(component, config) {
|
|
59
|
+
const preset = config?.video?.transcodePreset;
|
|
60
|
+
const locales = config?.video?.captionLocales ?? [];
|
|
61
|
+
const uploadLock = config?.upload?.lock;
|
|
62
|
+
return {
|
|
63
|
+
issueViewerToken: exposed(
|
|
64
|
+
"action",
|
|
65
|
+
{ sessionSecret: v.optional(v.string()) },
|
|
66
|
+
async (ctx, args, caller) => ctx.runAction(component.public.issueViewerToken, {
|
|
67
|
+
...args,
|
|
68
|
+
...caller.userId ? { userId: caller.userId } : {}
|
|
69
|
+
})
|
|
70
|
+
),
|
|
71
|
+
// Optional per-resource visibility; omitted → the component resolves the config's default (secure-by-default).
|
|
72
|
+
createUploadUrl: exposed(
|
|
73
|
+
"action",
|
|
74
|
+
{ filename: v.string(), contentType: v.string(), visibility: visibilityArg },
|
|
75
|
+
async (ctx, args) => ctx.runAction(component.public.createUploadUrl, { ...args, ...uploadLock ? { lock: uploadLock } : {} })
|
|
76
|
+
),
|
|
77
|
+
// Records an upload once its bytes are stored (the Worker HEADs the key): a browser can't list anything
|
|
78
|
+
// that isn't in your bucket. `useDevultur().upload()` and `useMediaUpload` call it after the PUT.
|
|
79
|
+
confirmUpload: exposed(
|
|
80
|
+
"action",
|
|
81
|
+
{
|
|
82
|
+
key: v.string(),
|
|
83
|
+
filename: v.optional(v.string()),
|
|
84
|
+
width: v.optional(v.number()),
|
|
85
|
+
height: v.optional(v.number()),
|
|
86
|
+
pageCount: v.optional(v.number())
|
|
87
|
+
},
|
|
88
|
+
async (ctx, args) => ctx.runAction(component.uploads.confirmUpload, args)
|
|
89
|
+
),
|
|
90
|
+
// Edits a record's title, alt text and tags. Your guard decides who may edit which media.
|
|
91
|
+
updateMediaMeta: exposed(
|
|
92
|
+
"mutation",
|
|
93
|
+
{
|
|
94
|
+
key: v.string(),
|
|
95
|
+
title: v.optional(v.string()),
|
|
96
|
+
alt: v.optional(v.string()),
|
|
97
|
+
tags: v.optional(v.array(v.string()))
|
|
98
|
+
},
|
|
99
|
+
async (ctx, args) => ctx.runMutation(component.metadata.updateMediaMeta, { ...args, key: mediaIdOf(args.key) })
|
|
100
|
+
),
|
|
101
|
+
deleteMedia: exposed(
|
|
102
|
+
"action",
|
|
103
|
+
{ key: v.string() },
|
|
104
|
+
async (ctx, args) => ctx.runAction(component.public.deleteMediaAction, args)
|
|
105
|
+
),
|
|
106
|
+
deleteVideo: exposed(
|
|
107
|
+
"action",
|
|
108
|
+
{ videoId: v.string(), visibility: visibilityArg },
|
|
109
|
+
async (ctx, args) => ctx.runAction(component.public.deleteVideo, args)
|
|
110
|
+
),
|
|
111
|
+
processVideo: exposed(
|
|
112
|
+
"action",
|
|
113
|
+
{ key: v.string() },
|
|
114
|
+
async (ctx, args) => ctx.runAction(component.public.processVideo, { key: args.key, preset, locales })
|
|
115
|
+
),
|
|
116
|
+
getMedia: exposed(
|
|
117
|
+
"query",
|
|
118
|
+
{ key: v.string() },
|
|
119
|
+
async (ctx, args) => ctx.runQuery(component.public.getMediaByKey, { key: mediaIdOf(args.key) })
|
|
120
|
+
),
|
|
121
|
+
// The media library, a page at a time (for `usePaginatedQuery`). It lists every record, of every user.
|
|
122
|
+
listMediaPage: exposed(
|
|
123
|
+
"query",
|
|
124
|
+
{ type: mediaTypeArg, paginationOpts: paginationOptsValidator },
|
|
125
|
+
async (ctx, args) => ctx.runQuery(component.public.listMediaPage, args)
|
|
126
|
+
),
|
|
127
|
+
// The media library matching a search (title and filename), a page at a time. Every user's media, like
|
|
128
|
+
// `listMediaPage`; a blank search lists it newest first.
|
|
129
|
+
searchMedia: exposed(
|
|
130
|
+
"query",
|
|
131
|
+
{ search: v.string(), type: mediaTypeArg, paginationOpts: paginationOptsValidator },
|
|
132
|
+
async (ctx, args) => ctx.runQuery(component.library.searchMedia, args)
|
|
133
|
+
),
|
|
134
|
+
saveProgress: exposed(
|
|
135
|
+
"mutation",
|
|
136
|
+
{ ...progressArgs, position: v.number() },
|
|
137
|
+
async (ctx, args, caller) => ctx.runMutation(component.public.saveProgress, progressArgsFor(args, caller))
|
|
138
|
+
),
|
|
139
|
+
loadProgress: exposed(
|
|
140
|
+
"query",
|
|
141
|
+
progressArgs,
|
|
142
|
+
async (ctx, args, caller) => ctx.runQuery(component.public.loadProgress, progressArgsFor(args, caller))
|
|
143
|
+
),
|
|
144
|
+
markComplete: exposed(
|
|
145
|
+
"mutation",
|
|
146
|
+
progressArgs,
|
|
147
|
+
async (ctx, args, caller) => ctx.runMutation(component.public.markComplete, progressArgsFor(args, caller))
|
|
148
|
+
),
|
|
149
|
+
resetProgress: exposed(
|
|
150
|
+
"mutation",
|
|
151
|
+
progressArgs,
|
|
152
|
+
async (ctx, args, caller) => ctx.runMutation(component.public.resetProgress, progressArgsFor(args, caller))
|
|
153
|
+
)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
var BUILDERS = {
|
|
157
|
+
public: { query: queryGeneric, mutation: mutationGeneric, action: actionGeneric },
|
|
158
|
+
internal: { query: internalQueryGeneric, mutation: internalMutationGeneric, action: internalActionGeneric }
|
|
159
|
+
};
|
|
160
|
+
function registerFunctions(functions, visibility, wrap) {
|
|
161
|
+
const entries = Object.entries(functions).map(([name, fn]) => {
|
|
162
|
+
const builder = BUILDERS[visibility][fn.kind];
|
|
163
|
+
return [name, builder({ args: fn.args, handler: wrap(name, fn) })];
|
|
164
|
+
});
|
|
165
|
+
return Object.fromEntries(entries);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/client.ts
|
|
22
169
|
var Devultur = class {
|
|
170
|
+
/** The mounted component (`components.devultur`) this client calls. */
|
|
23
171
|
component;
|
|
24
172
|
constructor(component) {
|
|
25
173
|
this.component = component;
|
|
@@ -40,10 +188,20 @@ var Devultur = class {
|
|
|
40
188
|
/**
|
|
41
189
|
* Records an upload in the component once its bytes are stored (after the PUT): it then shows in `listMedia`.
|
|
42
190
|
* Returns the record, or `null` when nothing is stored at `key`. Uploads nobody confirms are recorded a day later.
|
|
191
|
+
* `width`, `height` and `pageCount` are the client's hints (positive integers up to 100,000); an image size the
|
|
192
|
+
* Worker read from the bytes wins over them.
|
|
43
193
|
*/
|
|
44
194
|
async confirmUpload(ctx, args) {
|
|
45
195
|
return ctx.runAction(this.component.uploads.confirmUpload, args);
|
|
46
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Sets a record's `title`, `alt` and/or `tags` (only the ones passed; an empty string clears a title or alt, `[]`
|
|
199
|
+
* the tags). `key` is the media id. Trimmed and deduplicated; past title 200, alt 1000 or 20 tags of 50 characters
|
|
200
|
+
* it throws `invalid_media_metadata`. Returns the updated record, or `null` when there is none.
|
|
201
|
+
*/
|
|
202
|
+
async updateMediaMeta(ctx, args) {
|
|
203
|
+
return ctx.runMutation(this.component.metadata.updateMediaMeta, args);
|
|
204
|
+
}
|
|
47
205
|
/**
|
|
48
206
|
* The recorded media (every confirmed upload, and every video sent to `processVideo`), optionally of one type.
|
|
49
207
|
* `mediaFromRecord(record)` turns a row into the reference `<Image>` and `dv.media()` take. Unbounded: it reads every
|
|
@@ -56,6 +214,21 @@ var Devultur = class {
|
|
|
56
214
|
async listMediaPage(ctx, args) {
|
|
57
215
|
return ctx.runQuery(this.component.public.listMediaPage, args);
|
|
58
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* One page of the records whose title or filename matches `search` (its last word as a prefix), best match first,
|
|
219
|
+
* optionally of one type. A blank `search` lists newest first, like `listMediaPage`. Records from before 1.4.0
|
|
220
|
+
* match once `backfillSearchText` (or any write) gives them a `searchText`.
|
|
221
|
+
*/
|
|
222
|
+
async searchMedia(ctx, args) {
|
|
223
|
+
return ctx.runQuery(this.component.library.searchMedia, args);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* One-off for records written before 1.4.0: gives every record without a `searchText` one, `batchSize` records
|
|
227
|
+
* per mutation (default 100, at most 1000). Runs the first batch and schedules the rest. Safe to run again.
|
|
228
|
+
*/
|
|
229
|
+
async backfillSearchText(ctx, options) {
|
|
230
|
+
return ctx.runMutation(this.component.library.backfillSearchText, options ?? {});
|
|
231
|
+
}
|
|
59
232
|
/** The lock in force on `key`. */
|
|
60
233
|
async getLock(ctx, key) {
|
|
61
234
|
return ctx.runAction(this.component.lock.getLock, { key });
|
|
@@ -72,137 +245,32 @@ var Devultur = class {
|
|
|
72
245
|
async releaseHold(ctx, keys) {
|
|
73
246
|
return ctx.runAction(this.component.lock.releaseHold, { keys });
|
|
74
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* The component's functions for your app to export. They have no auth of their own: anyone who can call your
|
|
250
|
+
* Convex deployment can call them. `defineDevulturFunctions` returns the same functions behind your guard.
|
|
251
|
+
*/
|
|
75
252
|
static exposeApi(component, config) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const uploadLock = config?.upload?.lock;
|
|
80
|
-
return {
|
|
81
|
-
issueViewerToken: actionGeneric({
|
|
82
|
-
args: { sessionSecret: v.optional(v.string()) },
|
|
83
|
-
handler: async (ctx, args) => {
|
|
84
|
-
return ctx.runAction(component.public.issueViewerToken, args);
|
|
85
|
-
}
|
|
86
|
-
}),
|
|
87
|
-
createUploadUrl: actionGeneric({
|
|
88
|
-
args: { filename: v.string(), contentType: v.string(), visibility: visibilityArg },
|
|
89
|
-
handler: async (ctx, args) => {
|
|
90
|
-
return ctx.runAction(component.public.createUploadUrl, {
|
|
91
|
-
...args,
|
|
92
|
-
...uploadLock ? { lock: uploadLock } : {}
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}),
|
|
96
|
-
// Records an upload once its bytes are stored (the Worker HEADs the key): a browser can't list anything
|
|
97
|
-
// that isn't in your bucket. `useDevultur().upload()` and `useMediaUpload` call it after the PUT.
|
|
98
|
-
confirmUpload: actionGeneric({
|
|
99
|
-
args: {
|
|
100
|
-
key: v.string(),
|
|
101
|
-
filename: v.optional(v.string()),
|
|
102
|
-
width: v.optional(v.number()),
|
|
103
|
-
height: v.optional(v.number())
|
|
104
|
-
},
|
|
105
|
-
handler: async (ctx, args) => {
|
|
106
|
-
return ctx.runAction(component.uploads.confirmUpload, args);
|
|
107
|
-
}
|
|
108
|
-
}),
|
|
109
|
-
deleteMedia: actionGeneric({
|
|
110
|
-
args: { key: v.string() },
|
|
111
|
-
handler: async (ctx, args) => {
|
|
112
|
-
return ctx.runAction(component.public.deleteMediaAction, args);
|
|
113
|
-
}
|
|
114
|
-
}),
|
|
115
|
-
deleteVideo: actionGeneric({
|
|
116
|
-
args: { videoId: v.string(), visibility: visibilityArg },
|
|
117
|
-
handler: async (ctx, args) => {
|
|
118
|
-
return ctx.runAction(component.public.deleteVideo, args);
|
|
119
|
-
}
|
|
120
|
-
}),
|
|
121
|
-
processVideo: actionGeneric({
|
|
122
|
-
args: { key: v.string() },
|
|
123
|
-
handler: async (ctx, args) => {
|
|
124
|
-
return ctx.runAction(component.public.processVideo, {
|
|
125
|
-
key: args.key,
|
|
126
|
-
preset,
|
|
127
|
-
locales
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}),
|
|
131
|
-
getMedia: queryGeneric({
|
|
132
|
-
args: { key: v.string() },
|
|
133
|
-
handler: async (ctx, args) => {
|
|
134
|
-
const mediaKey = args.key.includes("/") ? extractId(args.key) : args.key;
|
|
135
|
-
return ctx.runQuery(component.public.getMediaByKey, { key: mediaKey });
|
|
136
|
-
}
|
|
137
|
-
}),
|
|
138
|
-
// The media library, a page at a time (for `usePaginatedQuery`). It lists every record, of every user: export
|
|
139
|
-
// it only when every caller may see the whole library, otherwise wrap `devultur.listMediaPage` with your checks.
|
|
140
|
-
listMediaPage: queryGeneric({
|
|
141
|
-
args: {
|
|
142
|
-
type: v.optional(v.union(v.literal("video"), v.literal("image"), v.literal("file"))),
|
|
143
|
-
paginationOpts: paginationOptsValidator
|
|
144
|
-
},
|
|
145
|
-
handler: async (ctx, args) => {
|
|
146
|
-
return ctx.runQuery(component.public.listMediaPage, args);
|
|
147
|
-
}
|
|
148
|
-
}),
|
|
149
|
-
saveProgress: mutationGeneric({
|
|
150
|
-
args: { userId: v.string(), videoId: v.string(), position: v.number() },
|
|
151
|
-
handler: async (ctx, args) => {
|
|
152
|
-
return ctx.runMutation(component.public.saveProgress, args);
|
|
153
|
-
}
|
|
154
|
-
}),
|
|
155
|
-
loadProgress: queryGeneric({
|
|
156
|
-
args: { userId: v.string(), videoId: v.string() },
|
|
157
|
-
handler: async (ctx, args) => {
|
|
158
|
-
return ctx.runQuery(component.public.loadProgress, args);
|
|
159
|
-
}
|
|
160
|
-
}),
|
|
161
|
-
markComplete: mutationGeneric({
|
|
162
|
-
args: { userId: v.string(), videoId: v.string() },
|
|
163
|
-
handler: async (ctx, args) => {
|
|
164
|
-
return ctx.runMutation(component.public.markComplete, args);
|
|
165
|
-
}
|
|
166
|
-
}),
|
|
167
|
-
resetProgress: mutationGeneric({
|
|
168
|
-
args: { userId: v.string(), videoId: v.string() },
|
|
169
|
-
handler: async (ctx, args) => {
|
|
170
|
-
return ctx.runMutation(component.public.resetProgress, args);
|
|
171
|
-
}
|
|
172
|
-
})
|
|
173
|
-
};
|
|
253
|
+
return registerFunctions(exposedFunctions(component, config), "public", (_name, fn) => {
|
|
254
|
+
return (ctx, args) => fn.handler(ctx, args, {});
|
|
255
|
+
});
|
|
174
256
|
}
|
|
175
257
|
};
|
|
176
258
|
|
|
177
|
-
// src/
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return isDevulturError(error) ? error.data : null;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// src/media-record.ts
|
|
192
|
-
function mediaFromRecord(record) {
|
|
193
|
-
if (!record.visibility) return null;
|
|
194
|
-
return {
|
|
195
|
-
id: record.key,
|
|
196
|
-
kind: record.type,
|
|
197
|
-
visibility: record.visibility,
|
|
198
|
-
filename: record.filename,
|
|
199
|
-
...record.objectKey ? { key: record.objectKey } : {},
|
|
200
|
-
...record.width !== void 0 ? { width: record.width } : {},
|
|
201
|
-
...record.height !== void 0 ? { height: record.height } : {}
|
|
202
|
-
};
|
|
259
|
+
// src/guarded-functions.ts
|
|
260
|
+
function defineDevulturFunctions(devultur, options) {
|
|
261
|
+
const { guard, config } = options;
|
|
262
|
+
const visibility = options.visibility ?? "public";
|
|
263
|
+
return registerFunctions(exposedFunctions(devultur.component, config), visibility, (name, fn) => {
|
|
264
|
+
return async (ctx, args) => {
|
|
265
|
+
const operation = { name, kind: fn.kind, args };
|
|
266
|
+
const caller = await guard(ctx, operation) ?? {};
|
|
267
|
+
return fn.handler(ctx, args, caller);
|
|
268
|
+
};
|
|
269
|
+
});
|
|
203
270
|
}
|
|
204
271
|
export {
|
|
205
272
|
CaptionLocale,
|
|
273
|
+
DEVULTUR_ERROR_CODES,
|
|
206
274
|
Devultur,
|
|
207
275
|
ImageFit,
|
|
208
276
|
ImageFormat,
|
|
@@ -212,8 +280,12 @@ export {
|
|
|
212
280
|
TranscodePreset,
|
|
213
281
|
TranscodeProvider,
|
|
214
282
|
TranslationProvider,
|
|
283
|
+
defineDevulturFunctions,
|
|
284
|
+
describeDevulturError,
|
|
285
|
+
devulturErrorLabels,
|
|
215
286
|
devulturErrorOf,
|
|
216
287
|
dv,
|
|
217
288
|
isDevulturError,
|
|
289
|
+
isDevulturErrorCode,
|
|
218
290
|
mediaFromRecord
|
|
219
291
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { A as AttachmentLabels, M as MediaLibraryLabels, a as MediaPickerLabels } from '../media-picker-labels-CiVcNUZo.js';
|
|
2
|
+
import { D as DevulturErrorLabels } from '../error-labels-BfqcFh2R.js';
|
|
3
|
+
import '../types-LrusGVDf.js';
|
|
4
|
+
import '@devultur/core';
|
|
5
|
+
|
|
6
|
+
declare const devulturErrorLabels: DevulturErrorLabels;
|
|
7
|
+
declare const attachmentLabels: AttachmentLabels;
|
|
8
|
+
declare const mediaLibraryLabels: MediaLibraryLabels;
|
|
9
|
+
declare const mediaPickerLabels: MediaPickerLabels;
|
|
10
|
+
|
|
11
|
+
export { attachmentLabels, devulturErrorLabels, mediaLibraryLabels, mediaPickerLabels };
|
|
@@ -0,0 +1,162 @@
|
|
|
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
|
+
};
|
|
157
|
+
export {
|
|
158
|
+
attachmentLabels,
|
|
159
|
+
devulturErrorLabels,
|
|
160
|
+
mediaLibraryLabels,
|
|
161
|
+
mediaPickerLabels
|
|
162
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { M as MediaRecord } from './types-LrusGVDf.js';
|
|
2
|
+
|
|
3
|
+
/** What an attachment is, for its icon and type label. */
|
|
4
|
+
type AttachmentKind = "image" | "video" | "pdf" | "audio" | "text" | "file";
|
|
5
|
+
/**
|
|
6
|
+
* Every string `<AttachmentCard>` and `<DocumentLink>` show or announce. Pass any subset as `labels`; the rest keep
|
|
7
|
+
* the English defaults (`defaultAttachmentLabels`). Spanish: `attachmentLabels` from `@devultur/convex/locales/es`.
|
|
8
|
+
*/
|
|
9
|
+
interface AttachmentLabels {
|
|
10
|
+
/** BCP 47 locale passed to `size`. A component's `locale` prop wins over it. */
|
|
11
|
+
locale: string;
|
|
12
|
+
/** A file size, e.g. `1.5 MB`. */
|
|
13
|
+
size: (bytes: number, locale: string) => string;
|
|
14
|
+
/** A PDF's page count, e.g. `12 pages`. */
|
|
15
|
+
pages: (pageCount: number) => string;
|
|
16
|
+
/** An audio or video duration, e.g. `1:05`. */
|
|
17
|
+
duration: (seconds: number) => string;
|
|
18
|
+
/** The type shown next to the size, e.g. `PDF`. */
|
|
19
|
+
kind: (kind: AttachmentKind) => string;
|
|
20
|
+
/** Text of the download action. */
|
|
21
|
+
download: string;
|
|
22
|
+
/** Text of the open-preview action. */
|
|
23
|
+
open: string;
|
|
24
|
+
/** Name of a media with no `title` and no filename. */
|
|
25
|
+
untitled: string;
|
|
26
|
+
}
|
|
27
|
+
/** `bytes` in decimal units (`1.5 MB`), formatted for `locale`. Under 1 kB, whole bytes (`512 bytes`). */
|
|
28
|
+
declare function formatFileSize(bytes: number, locale: string): string;
|
|
29
|
+
/** `seconds` as a clock: `0:42`, `1:05`, `1:02:03`. */
|
|
30
|
+
declare function formatDuration(seconds: number): string;
|
|
31
|
+
declare const defaultAttachmentLabels: AttachmentLabels;
|
|
32
|
+
|
|
33
|
+
type MediaType = MediaRecord["type"];
|
|
34
|
+
/** Every string `<MediaLibrary>` shows or announces. Pass any subset as `labels`; the rest stay English. */
|
|
35
|
+
interface MediaLibraryLabels {
|
|
36
|
+
/** Accessible name of the search box. */
|
|
37
|
+
search: string;
|
|
38
|
+
searchPlaceholder: string;
|
|
39
|
+
/** Accessible name of the type filter. */
|
|
40
|
+
typeFilter: string;
|
|
41
|
+
/** The type filter's option for every kind. */
|
|
42
|
+
allTypes: string;
|
|
43
|
+
types: Record<MediaType, string>;
|
|
44
|
+
/** Accessible name of the list of media. */
|
|
45
|
+
results: string;
|
|
46
|
+
/** Shown while the first page loads. */
|
|
47
|
+
loading: string;
|
|
48
|
+
/** The library has nothing (of this type). */
|
|
49
|
+
empty: string;
|
|
50
|
+
/** Nothing matches the search. */
|
|
51
|
+
noResults: (search: string) => string;
|
|
52
|
+
loadMore: string;
|
|
53
|
+
/** The load-more button while the next page loads. */
|
|
54
|
+
loadingMore: string;
|
|
55
|
+
/** Shown when the library couldn't be loaded. Default: the Devultur error's message, if it is one. */
|
|
56
|
+
error: (error: unknown) => string;
|
|
57
|
+
retry: string;
|
|
58
|
+
/** A record's size. Not shown for a size of 0 (a video only `processVideo` recorded). */
|
|
59
|
+
size: (bytes: number) => string;
|
|
60
|
+
}
|
|
61
|
+
declare const defaultMediaLibraryLabels: MediaLibraryLabels;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Every string `<MediaPicker>` shows or announces (the library's and the cards' have their own: `libraryLabels`,
|
|
65
|
+
* `attachmentLabels`). Pass any subset as `labels`; the rest stay English. Spanish: `mediaPickerLabels` from
|
|
66
|
+
* `@devultur/convex/locales/es`.
|
|
67
|
+
*/
|
|
68
|
+
interface MediaPickerLabels {
|
|
69
|
+
/** The upload button: one file, or several with `multiple`. */
|
|
70
|
+
upload: (multiple: boolean) => string;
|
|
71
|
+
/** Next to the upload button, for dragging files onto it. */
|
|
72
|
+
dropHint: (multiple: boolean) => string;
|
|
73
|
+
/** Accessible name of the list of uploads in progress. */
|
|
74
|
+
uploads: string;
|
|
75
|
+
/** Accessible name of a file's progress bar. */
|
|
76
|
+
progress: (filename: string) => string;
|
|
77
|
+
/** A file's progress as text. */
|
|
78
|
+
percent: (percent: number) => string;
|
|
79
|
+
/** Announced when a file starts uploading. */
|
|
80
|
+
uploading: (filename: string) => string;
|
|
81
|
+
/** Announced when a file has uploaded (and is selected). */
|
|
82
|
+
uploaded: (filename: string) => string;
|
|
83
|
+
cancel: string;
|
|
84
|
+
/** Accessible name of a file's cancel button. */
|
|
85
|
+
cancelUpload: (filename: string) => string;
|
|
86
|
+
/** Announced when an upload is cancelled. */
|
|
87
|
+
cancelled: (filename: string) => string;
|
|
88
|
+
/** A file `accept` turns away, before it uploads. */
|
|
89
|
+
notAccepted: (filename: string) => string;
|
|
90
|
+
/** Why an upload failed. Default: the Devultur error's message, if it is one. */
|
|
91
|
+
uploadError: (error: unknown) => string;
|
|
92
|
+
retry: string;
|
|
93
|
+
dismiss: string;
|
|
94
|
+
/** Heading and accessible name of the selection. */
|
|
95
|
+
selection: string;
|
|
96
|
+
/** Nothing is selected. */
|
|
97
|
+
empty: string;
|
|
98
|
+
remove: string;
|
|
99
|
+
/** Accessible name of an item's remove button. */
|
|
100
|
+
removeItem: (name: string) => string;
|
|
101
|
+
/** Removes every selected item. */
|
|
102
|
+
clear: string;
|
|
103
|
+
/** Picks another file in place of the selected one (single selection). */
|
|
104
|
+
replace: string;
|
|
105
|
+
/** Announced when an item is selected. */
|
|
106
|
+
selected: (name: string) => string;
|
|
107
|
+
/** Announced when an item is removed from the selection. */
|
|
108
|
+
removed: (name: string) => string;
|
|
109
|
+
/** Announced when the selection is cleared. */
|
|
110
|
+
cleared: string;
|
|
111
|
+
}
|
|
112
|
+
declare const defaultMediaPickerLabels: MediaPickerLabels;
|
|
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 };
|