@devultur/convex 1.0.2 → 1.1.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 +151 -5
- package/dist/bin/init.js +9 -2
- package/dist/{chunk-FHWT3GD6.js → chunk-GKLFRSGI.js} +2 -0
- package/dist/component/_generated/api.d.ts +12 -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 +55 -1
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +1 -1
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/lock.d.ts +33 -0
- package/dist/component/lock.d.ts.map +1 -0
- package/dist/component/lock.js +47 -0
- package/dist/component/lock.js.map +1 -0
- package/dist/component/lockValidators.d.ts +25 -0
- package/dist/component/lockValidators.d.ts.map +1 -0
- package/dist/component/lockValidators.js +14 -0
- package/dist/component/lockValidators.js.map +1 -0
- package/dist/component/mediaClient.d.ts +3 -0
- package/dist/component/mediaClient.d.ts.map +1 -0
- package/dist/component/mediaClient.js +7 -0
- package/dist/component/mediaClient.js.map +1 -0
- package/dist/component/public.d.ts +78 -0
- package/dist/component/public.d.ts.map +1 -1
- package/dist/component/public.js +83 -19
- package/dist/component/public.js.map +1 -1
- package/dist/component/schema.d.ts +12 -1
- 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/uploadKeys.d.ts +14 -0
- package/dist/component/uploadKeys.d.ts.map +1 -0
- package/dist/component/uploadKeys.js +23 -0
- package/dist/component/uploadKeys.js.map +1 -0
- package/dist/component/uploads.d.ts +119 -0
- package/dist/component/uploads.d.ts.map +1 -0
- package/dist/component/uploads.js +106 -0
- package/dist/component/uploads.js.map +1 -0
- package/dist/component/workerErrors.d.ts +17 -0
- package/dist/component/workerErrors.d.ts.map +1 -0
- package/dist/component/workerErrors.js +41 -0
- package/dist/component/workerErrors.js.map +1 -0
- package/dist/index.d.ts +147 -6
- package/dist/index.js +102 -4
- package/dist/react.d.ts +18 -5
- package/dist/react.js +35 -13
- package/dist/validators.d.ts +5 -3
- package/dist/validators.js +1 -1
- package/package.json +8 -3
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export declare const UPLOAD_CONFIRM_BACKSTOP_MS: number;
|
|
2
|
+
/**
|
|
3
|
+
* Records an upload in the component's `media` table once its bytes are stored (call it after the PUT). Returns the
|
|
4
|
+
* record, or `null` when nothing is stored at `key`. Idempotent: confirming twice updates the same record, and a
|
|
5
|
+
* video's transcode state is left as it is.
|
|
6
|
+
*/
|
|
7
|
+
export declare const confirmUpload: import("convex/server").RegisteredAction<"public", {
|
|
8
|
+
width?: number | undefined;
|
|
9
|
+
height?: number | undefined;
|
|
10
|
+
filename?: string | undefined;
|
|
11
|
+
key: string;
|
|
12
|
+
}, Promise<{
|
|
13
|
+
_id: import("convex/values").GenericId<"media">;
|
|
14
|
+
_creationTime: number;
|
|
15
|
+
width?: number | undefined;
|
|
16
|
+
height?: number | undefined;
|
|
17
|
+
visibility?: "public" | "private" | undefined;
|
|
18
|
+
objectKey?: string | undefined;
|
|
19
|
+
transcodeStatus?: "queued" | "transcoding" | "captioning" | "ready" | "failed" | undefined;
|
|
20
|
+
transcodeProgress?: {
|
|
21
|
+
phase?: "captioning" | "downloading" | "encoding" | "uploading" | undefined;
|
|
22
|
+
current: number;
|
|
23
|
+
total: number;
|
|
24
|
+
} | undefined;
|
|
25
|
+
transcodeError?: string | undefined;
|
|
26
|
+
failureReason?: "invalid_input" | "unsupported_codec" | "timeout" | "capacity" | "internal" | undefined;
|
|
27
|
+
retriable?: boolean | undefined;
|
|
28
|
+
playlistUrl?: string | undefined;
|
|
29
|
+
thumbnailKey?: string | undefined;
|
|
30
|
+
captionLocales?: string[] | undefined;
|
|
31
|
+
captionsPending?: boolean | undefined;
|
|
32
|
+
duration?: number | undefined;
|
|
33
|
+
renditions?: {
|
|
34
|
+
width?: number | undefined;
|
|
35
|
+
height?: number | undefined;
|
|
36
|
+
variant: string;
|
|
37
|
+
kind: "hls" | "mp4";
|
|
38
|
+
rung: string;
|
|
39
|
+
key: string;
|
|
40
|
+
url: string;
|
|
41
|
+
}[] | undefined;
|
|
42
|
+
posterUrls?: {
|
|
43
|
+
jpg?: string | undefined;
|
|
44
|
+
avif?: string | undefined;
|
|
45
|
+
} | undefined;
|
|
46
|
+
hasAudio?: boolean | undefined;
|
|
47
|
+
storyboardKey?: string | undefined;
|
|
48
|
+
type: "video" | "image" | "file";
|
|
49
|
+
key: string;
|
|
50
|
+
filename: string;
|
|
51
|
+
contentType: string;
|
|
52
|
+
sizeBytes: number;
|
|
53
|
+
createdAt: number;
|
|
54
|
+
} | null>>;
|
|
55
|
+
/** The scheduled check `createUploadUrl` leaves behind: records the upload if it was stored and never confirmed. */
|
|
56
|
+
export declare const confirmUploadLater: import("convex/server").RegisteredAction<"internal", {
|
|
57
|
+
width?: number | undefined;
|
|
58
|
+
height?: number | undefined;
|
|
59
|
+
filename?: string | undefined;
|
|
60
|
+
key: string;
|
|
61
|
+
}, Promise<void>>;
|
|
62
|
+
export declare const recordUpload: import("convex/server").RegisteredMutation<"internal", {
|
|
63
|
+
width?: number | undefined;
|
|
64
|
+
height?: number | undefined;
|
|
65
|
+
type: "video" | "image" | "file";
|
|
66
|
+
key: string;
|
|
67
|
+
filename: string;
|
|
68
|
+
contentType: string;
|
|
69
|
+
sizeBytes: number;
|
|
70
|
+
visibility: "public" | "private";
|
|
71
|
+
objectKey: string;
|
|
72
|
+
}, Promise<{
|
|
73
|
+
_id: import("convex/values").GenericId<"media">;
|
|
74
|
+
_creationTime: number;
|
|
75
|
+
width?: number | undefined;
|
|
76
|
+
height?: number | undefined;
|
|
77
|
+
visibility?: "public" | "private" | undefined;
|
|
78
|
+
objectKey?: string | undefined;
|
|
79
|
+
transcodeStatus?: "queued" | "transcoding" | "captioning" | "ready" | "failed" | undefined;
|
|
80
|
+
transcodeProgress?: {
|
|
81
|
+
phase?: "captioning" | "downloading" | "encoding" | "uploading" | undefined;
|
|
82
|
+
current: number;
|
|
83
|
+
total: number;
|
|
84
|
+
} | undefined;
|
|
85
|
+
transcodeError?: string | undefined;
|
|
86
|
+
failureReason?: "invalid_input" | "unsupported_codec" | "timeout" | "capacity" | "internal" | undefined;
|
|
87
|
+
retriable?: boolean | undefined;
|
|
88
|
+
playlistUrl?: string | undefined;
|
|
89
|
+
thumbnailKey?: string | undefined;
|
|
90
|
+
captionLocales?: string[] | undefined;
|
|
91
|
+
captionsPending?: boolean | undefined;
|
|
92
|
+
duration?: number | undefined;
|
|
93
|
+
renditions?: {
|
|
94
|
+
width?: number | undefined;
|
|
95
|
+
height?: number | undefined;
|
|
96
|
+
variant: string;
|
|
97
|
+
kind: "hls" | "mp4";
|
|
98
|
+
rung: string;
|
|
99
|
+
key: string;
|
|
100
|
+
url: string;
|
|
101
|
+
}[] | undefined;
|
|
102
|
+
posterUrls?: {
|
|
103
|
+
jpg?: string | undefined;
|
|
104
|
+
avif?: string | undefined;
|
|
105
|
+
} | undefined;
|
|
106
|
+
hasAudio?: boolean | undefined;
|
|
107
|
+
storyboardKey?: string | undefined;
|
|
108
|
+
type: "video" | "image" | "file";
|
|
109
|
+
key: string;
|
|
110
|
+
filename: string;
|
|
111
|
+
contentType: string;
|
|
112
|
+
sizeBytes: number;
|
|
113
|
+
createdAt: number;
|
|
114
|
+
} | null>>;
|
|
115
|
+
/** Drops the record of a deleted image or file. A video's record goes with `deleteVideo`, not with one of its keys. */
|
|
116
|
+
export declare const forgetUpload: import("convex/server").RegisteredMutation<"internal", {
|
|
117
|
+
key: string;
|
|
118
|
+
}, Promise<void>>;
|
|
119
|
+
//# sourceMappingURL=uploads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../src/component/uploads.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,0BAA0B,QAAsB,CAAC;AAgD9D;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAGxB,CAAC;AAEH,oHAAoH;AACpH,eAAO,MAAM,kBAAkB;;;;;iBAK7B,CAAC;AAEH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA0BvB,CAAC;AAEH,uHAAuH;AACvH,eAAO,MAAM,YAAY;;iBAYvB,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { extractId, sanitizeFilename } from "@devultur/core";
|
|
2
|
+
import { ConvexError, v } from "convex/values";
|
|
3
|
+
import { internal } from "./_generated/api";
|
|
4
|
+
import { internalAction, internalMutation } from "./_generated/server";
|
|
5
|
+
import { getMediaClient } from "./mediaClient";
|
|
6
|
+
import { parseUploadKey } from "./uploadKeys";
|
|
7
|
+
import { workerAction } from "./workerErrors";
|
|
8
|
+
// Past the Worker's longest upload window (a multipart upload can run for 24 hours), so the scheduled check never runs
|
|
9
|
+
// while the bytes are still on their way.
|
|
10
|
+
export const UPLOAD_CONFIRM_BACKSTOP_MS = 25 * 60 * 60 * 1000;
|
|
11
|
+
/** The display name to store: the client's when it is the name in the key, else the key's own. */
|
|
12
|
+
function displayName(uploadKey, filename) {
|
|
13
|
+
if (filename && sanitizeFilename(filename) === uploadKey.storedName)
|
|
14
|
+
return filename;
|
|
15
|
+
return uploadKey.storedName;
|
|
16
|
+
}
|
|
17
|
+
const confirmArgs = {
|
|
18
|
+
key: v.string(),
|
|
19
|
+
/** The original filename (display and download name). Ignored unless it is the one in the key. */
|
|
20
|
+
filename: v.optional(v.string()),
|
|
21
|
+
/** Intrinsic size of an image, read in the browser. */
|
|
22
|
+
width: v.optional(v.number()),
|
|
23
|
+
height: v.optional(v.number()),
|
|
24
|
+
};
|
|
25
|
+
/** HEADs the object and records it; `null` when no object is stored at `key` (yet). */
|
|
26
|
+
// Annotated: the result of `internal.uploads.*` would otherwise type this module in terms of itself.
|
|
27
|
+
async function confirm(ctx, args) {
|
|
28
|
+
const uploadKey = parseUploadKey(args.key);
|
|
29
|
+
if (!uploadKey) {
|
|
30
|
+
throw new ConvexError({ code: "invalid_upload_key", message: `Not an upload key: ${args.key}`, status: 400 });
|
|
31
|
+
}
|
|
32
|
+
const head = await getMediaClient().files.head(args.key);
|
|
33
|
+
if (!head)
|
|
34
|
+
return null;
|
|
35
|
+
return ctx.runMutation(internal.uploads.recordUpload, {
|
|
36
|
+
key: uploadKey.id,
|
|
37
|
+
objectKey: args.key,
|
|
38
|
+
type: uploadKey.kind,
|
|
39
|
+
visibility: uploadKey.visibility,
|
|
40
|
+
filename: displayName(uploadKey, args.filename),
|
|
41
|
+
contentType: head.contentType,
|
|
42
|
+
sizeBytes: head.size,
|
|
43
|
+
...(args.width !== undefined ? { width: args.width } : {}),
|
|
44
|
+
...(args.height !== undefined ? { height: args.height } : {}),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Records an upload in the component's `media` table once its bytes are stored (call it after the PUT). Returns the
|
|
49
|
+
* record, or `null` when nothing is stored at `key`. Idempotent: confirming twice updates the same record, and a
|
|
50
|
+
* video's transcode state is left as it is.
|
|
51
|
+
*/
|
|
52
|
+
export const confirmUpload = workerAction({
|
|
53
|
+
args: confirmArgs,
|
|
54
|
+
handler: async (ctx, args) => confirm(ctx, args),
|
|
55
|
+
});
|
|
56
|
+
/** The scheduled check `createUploadUrl` leaves behind: records the upload if it was stored and never confirmed. */
|
|
57
|
+
export const confirmUploadLater = internalAction({
|
|
58
|
+
args: confirmArgs,
|
|
59
|
+
handler: async (ctx, args) => {
|
|
60
|
+
await confirm(ctx, args);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
export const recordUpload = internalMutation({
|
|
64
|
+
args: {
|
|
65
|
+
key: v.string(),
|
|
66
|
+
objectKey: v.string(),
|
|
67
|
+
type: v.union(v.literal("video"), v.literal("image"), v.literal("file")),
|
|
68
|
+
visibility: v.union(v.literal("public"), v.literal("private")),
|
|
69
|
+
filename: v.string(),
|
|
70
|
+
contentType: v.string(),
|
|
71
|
+
sizeBytes: v.number(),
|
|
72
|
+
width: v.optional(v.number()),
|
|
73
|
+
height: v.optional(v.number()),
|
|
74
|
+
},
|
|
75
|
+
handler: async (ctx, args) => {
|
|
76
|
+
const existing = await ctx.db
|
|
77
|
+
.query("media")
|
|
78
|
+
.withIndex("by_key", (q) => q.eq("key", args.key))
|
|
79
|
+
.first();
|
|
80
|
+
if (!existing) {
|
|
81
|
+
const id = await ctx.db.insert("media", { ...args, createdAt: Date.now() });
|
|
82
|
+
return ctx.db.get(id);
|
|
83
|
+
}
|
|
84
|
+
// A video `processVideo` recorded first: fill in what only the upload knows, keep its transcode state.
|
|
85
|
+
await ctx.db.patch(existing._id, args);
|
|
86
|
+
return ctx.db.get(existing._id);
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
/** Drops the record of a deleted image or file. A video's record goes with `deleteVideo`, not with one of its keys. */
|
|
90
|
+
export const forgetUpload = internalMutation({
|
|
91
|
+
args: { key: v.string() },
|
|
92
|
+
handler: async (ctx, args) => {
|
|
93
|
+
if (!parseUploadKey(args.key))
|
|
94
|
+
return;
|
|
95
|
+
const record = await ctx.db
|
|
96
|
+
.query("media")
|
|
97
|
+
.withIndex("by_key", (q) => q.eq("key", extractId(args.key)))
|
|
98
|
+
.first();
|
|
99
|
+
if (!record || record.type === "video")
|
|
100
|
+
return;
|
|
101
|
+
if (record.objectKey && record.objectKey !== args.key)
|
|
102
|
+
return;
|
|
103
|
+
await ctx.db.delete(record._id);
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=uploads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploads.js","sourceRoot":"","sources":["../../src/component/uploads.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Value } from "convex/values";
|
|
2
|
+
import { action } from "./_generated/server";
|
|
3
|
+
/** `data` of the `ConvexError` a component action throws when the Worker refuses a request. */
|
|
4
|
+
export interface DevulturErrorData {
|
|
5
|
+
/** The Worker's stable code (`retention_active`, `file_too_large`, ...), or `worker_error` when it sent none. */
|
|
6
|
+
code: string;
|
|
7
|
+
message: string;
|
|
8
|
+
/** HTTP status of the Worker's answer. */
|
|
9
|
+
status: number;
|
|
10
|
+
/** The rest of the Worker's error body, e.g. `{ key, mode, until, hold }` on `retention_active`. */
|
|
11
|
+
details?: Record<string, Value>;
|
|
12
|
+
}
|
|
13
|
+
/** The error to rethrow: a Worker refusal becomes a `ConvexError` with its code; anything else passes through. */
|
|
14
|
+
export declare function toComponentError(error: unknown): unknown;
|
|
15
|
+
/** `action` whose handler rethrows Worker refusals as `ConvexError<DevulturErrorData>`. */
|
|
16
|
+
export declare const workerAction: typeof action;
|
|
17
|
+
//# sourceMappingURL=workerErrors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerErrors.d.ts","sourceRoot":"","sources":["../../src/component/workerErrors.ts"],"names":[],"mappings":"AAKA,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,+FAA+F;AAC/F,MAAM,WAAW,iBAAiB;IACjC,iHAAiH;IACjH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,oGAAoG;IACpG,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAChC;AAYD,kHAAkH;AAClH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAUxD;AAED,2FAA2F;AAC3F,eAAO,MAAM,YAAY,EAUG,OAAO,MAAM,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Worker errors across the component boundary. A plain `Error` thrown in a component function reaches the app as a
|
|
2
|
+
// message only; a `ConvexError` keeps its `data`. Every component action is built with `workerAction`, which rethrows
|
|
3
|
+
// the Worker's `DevulturApiError` as `ConvexError<DevulturErrorData>`, so the app reads `error.data.code`
|
|
4
|
+
// (`retention_active`, `lock_conflict`, `compliance_extend_only`, ...) and `error.data.details`.
|
|
5
|
+
import { DevulturApiError } from "@devultur/core";
|
|
6
|
+
import { ConvexError } from "convex/values";
|
|
7
|
+
import { action } from "./_generated/server";
|
|
8
|
+
function isApiError(error) {
|
|
9
|
+
if (error instanceof DevulturApiError)
|
|
10
|
+
return true;
|
|
11
|
+
// A second copy of `@devultur/core` (bundling) fails `instanceof`; its shape doesn't.
|
|
12
|
+
return (error instanceof Error &&
|
|
13
|
+
error.name === "DevulturApiError" &&
|
|
14
|
+
typeof error.status === "number");
|
|
15
|
+
}
|
|
16
|
+
/** The error to rethrow: a Worker refusal becomes a `ConvexError` with its code; anything else passes through. */
|
|
17
|
+
export function toComponentError(error) {
|
|
18
|
+
if (!isApiError(error))
|
|
19
|
+
return error;
|
|
20
|
+
const data = {
|
|
21
|
+
code: error.code ?? "worker_error",
|
|
22
|
+
message: error.message,
|
|
23
|
+
status: error.status,
|
|
24
|
+
// JSON from the Worker: valid Convex values once `undefined` is gone.
|
|
25
|
+
...(error.details ? { details: JSON.parse(JSON.stringify(error.details)) } : {}),
|
|
26
|
+
};
|
|
27
|
+
return new ConvexError(data);
|
|
28
|
+
}
|
|
29
|
+
/** `action` whose handler rethrows Worker refusals as `ConvexError<DevulturErrorData>`. */
|
|
30
|
+
export const workerAction = ((definition) => action({
|
|
31
|
+
...definition,
|
|
32
|
+
handler: async (ctx, args) => {
|
|
33
|
+
try {
|
|
34
|
+
return await definition.handler(ctx, args);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw toComponentError(error);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
}));
|
|
41
|
+
//# sourceMappingURL=workerErrors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerErrors.js","sourceRoot":"","sources":["../../src/component/workerErrors.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,sHAAsH;AACtH,0GAA0G;AAC1G,iGAAiG;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAc,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAa7C,SAAS,UAAU,CAAC,KAAc;IACjC,IAAI,KAAK,YAAY,gBAAgB;QAAE,OAAO,IAAI,CAAC;IACnD,sFAAsF;IACtF,OAAO,CACN,KAAK,YAAY,KAAK;QACtB,KAAK,CAAC,IAAI,KAAK,kBAAkB;QACjC,OAAQ,KAA8B,CAAC,MAAM,KAAK,QAAQ,CAC1D,CAAC;AACH,CAAC;AAED,kHAAkH;AAClH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC9C,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,IAAI,GAAsB;QAC/B,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,cAAc;QAClC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,sEAAsE;QACtE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAA0B,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzG,CAAC;IACF,OAAO,IAAI,WAAW,CAAC,IAAwB,CAAC,CAAC;AAClD,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,UAA0E,EAAE,EAAE,CAC3G,MAAM,CAAC;IACN,GAAG,UAAU;IACb,OAAO,EAAE,KAAK,EAAE,GAAY,EAAE,IAAa,EAAE,EAAE;QAC9C,IAAI,CAAC;YACJ,OAAO,MAAM,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;IACF,CAAC;CACQ,CAAC,CAA6B,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { VideoModuleConfig } from '@devultur/core';
|
|
1
|
+
import { VideoModuleConfig, ObjectLock, LockView, SetLockOptions, HoldPage, DevulturMedia } from '@devultur/core';
|
|
2
2
|
export { CaptionLocale, DevulturConfig, DevulturKind, DevulturMedia, ImageFit, ImageFormat, SecurityLevel, SpeechProvider, StorageProvider, TranscodePreset, TranscodeProvider, TranslationProvider, VideoModuleConfig, Visibility } from '@devultur/core';
|
|
3
3
|
import * as convex_server from 'convex/server';
|
|
4
|
-
import { FunctionReference, GenericQueryCtx, GenericDataModel, GenericMutationCtx } from 'convex/server';
|
|
4
|
+
import { FunctionReference, GenericQueryCtx, GenericDataModel, GenericMutationCtx, GenericActionCtx, PaginationOptions, PaginationResult } from 'convex/server';
|
|
5
|
+
import { Value, ConvexError } from 'convex/values';
|
|
5
6
|
export { dv } from './validators.js';
|
|
6
|
-
import 'convex/values';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Generated `ComponentApi` utility.
|
|
@@ -26,6 +26,26 @@ import 'convex/values';
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
type ComponentApi<Name extends string | undefined = string | undefined> = {
|
|
29
|
+
lock: {
|
|
30
|
+
getLock: FunctionReference<"action", "internal", {
|
|
31
|
+
key: string;
|
|
32
|
+
}, any, Name>;
|
|
33
|
+
releaseHold: FunctionReference<"action", "internal", {
|
|
34
|
+
keys: Array<string>;
|
|
35
|
+
}, any, Name>;
|
|
36
|
+
setHold: FunctionReference<"action", "internal", {
|
|
37
|
+
keys: Array<string>;
|
|
38
|
+
}, any, Name>;
|
|
39
|
+
setLock: FunctionReference<"action", "internal", {
|
|
40
|
+
bypassGovernance?: boolean;
|
|
41
|
+
derivatives?: "inherit" | "original-only";
|
|
42
|
+
key: string;
|
|
43
|
+
mode?: "governance" | "compliance";
|
|
44
|
+
period?: number;
|
|
45
|
+
remove?: true;
|
|
46
|
+
until?: string;
|
|
47
|
+
}, any, Name>;
|
|
48
|
+
};
|
|
29
49
|
public: {
|
|
30
50
|
createMedia: FunctionReference<"mutation", "internal", {
|
|
31
51
|
contentType: string;
|
|
@@ -37,12 +57,24 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
|
|
|
37
57
|
createUploadUrl: FunctionReference<"action", "internal", {
|
|
38
58
|
contentType: string;
|
|
39
59
|
filename: string;
|
|
60
|
+
hold?: boolean;
|
|
61
|
+
lock?: {
|
|
62
|
+
hold?: boolean;
|
|
63
|
+
retention?: {
|
|
64
|
+
derivatives?: "inherit" | "original-only";
|
|
65
|
+
mode: "governance" | "compliance";
|
|
66
|
+
period?: number;
|
|
67
|
+
until?: string;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
visibility?: "public" | "private";
|
|
40
71
|
}, any, Name>;
|
|
41
72
|
deleteMediaAction: FunctionReference<"action", "internal", {
|
|
42
73
|
key: string;
|
|
43
74
|
}, any, Name>;
|
|
44
75
|
deleteVideo: FunctionReference<"action", "internal", {
|
|
45
76
|
videoId: string;
|
|
77
|
+
visibility?: "public" | "private";
|
|
46
78
|
}, any, Name>;
|
|
47
79
|
getCourseProgress: FunctionReference<"query", "internal", {
|
|
48
80
|
userId: string;
|
|
@@ -60,6 +92,17 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
|
|
|
60
92
|
listMedia: FunctionReference<"query", "internal", {
|
|
61
93
|
type?: "video" | "image" | "file";
|
|
62
94
|
}, any, Name>;
|
|
95
|
+
listMediaPage: FunctionReference<"query", "internal", {
|
|
96
|
+
paginationOpts: {
|
|
97
|
+
cursor: string | null;
|
|
98
|
+
endCursor?: string | null;
|
|
99
|
+
id?: number;
|
|
100
|
+
maximumBytesRead?: number;
|
|
101
|
+
maximumRowsRead?: number;
|
|
102
|
+
numItems: number;
|
|
103
|
+
};
|
|
104
|
+
type?: "video" | "image" | "file";
|
|
105
|
+
}, any, Name>;
|
|
63
106
|
loadProgress: FunctionReference<"query", "internal", {
|
|
64
107
|
userId: string;
|
|
65
108
|
videoId: string;
|
|
@@ -85,16 +128,19 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
|
|
|
85
128
|
}, any, Name>;
|
|
86
129
|
updateMedia: FunctionReference<"mutation", "internal", {
|
|
87
130
|
captionLocales?: Array<string>;
|
|
131
|
+
captionsPending?: boolean;
|
|
88
132
|
duration?: number;
|
|
133
|
+
failureReason?: "invalid_input" | "unsupported_codec" | "timeout" | "capacity" | "internal";
|
|
89
134
|
key: string;
|
|
90
135
|
playlistUrl?: string;
|
|
136
|
+
retriable?: boolean;
|
|
91
137
|
thumbnailKey?: string;
|
|
92
138
|
transcodeError?: string;
|
|
93
139
|
transcodeProgress?: {
|
|
94
140
|
current: number;
|
|
95
141
|
total: number;
|
|
96
142
|
};
|
|
97
|
-
transcodeStatus?: "transcoding" | "captioning" | "ready" | "failed";
|
|
143
|
+
transcodeStatus?: "queued" | "transcoding" | "captioning" | "ready" | "failed";
|
|
98
144
|
}, any, Name>;
|
|
99
145
|
upsertCaption: FunctionReference<"mutation", "internal", {
|
|
100
146
|
locale: string;
|
|
@@ -103,18 +149,41 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
|
|
|
103
149
|
vttUrl: string;
|
|
104
150
|
}, any, Name>;
|
|
105
151
|
};
|
|
152
|
+
uploads: {
|
|
153
|
+
confirmUpload: FunctionReference<"action", "internal", {
|
|
154
|
+
filename?: string;
|
|
155
|
+
height?: number;
|
|
156
|
+
key: string;
|
|
157
|
+
width?: number;
|
|
158
|
+
}, any, Name>;
|
|
159
|
+
};
|
|
106
160
|
};
|
|
107
161
|
|
|
108
162
|
interface DevulturComponentConfig {
|
|
109
163
|
video?: VideoModuleConfig;
|
|
164
|
+
/**
|
|
165
|
+
* Lock every upload through the exposed `createUploadUrl` gets (retention and/or hold). Set here, on your server:
|
|
166
|
+
* the browser never chooses a lock. To lock per upload, call `devultur.createUploadUrl(ctx, { lock })` from your
|
|
167
|
+
* own function.
|
|
168
|
+
*/
|
|
169
|
+
upload?: {
|
|
170
|
+
lock?: ObjectLock;
|
|
171
|
+
};
|
|
110
172
|
}
|
|
111
173
|
interface MediaRecord {
|
|
174
|
+
/** The media id (the `id` of its `DevulturMedia`), for every kind. */
|
|
112
175
|
key: string;
|
|
113
176
|
type: "video" | "image" | "file";
|
|
114
177
|
filename: string;
|
|
115
178
|
contentType: string;
|
|
116
179
|
sizeBytes: number;
|
|
117
|
-
|
|
180
|
+
/** Set by a confirmed upload; unset on records from before 1.1.0. */
|
|
181
|
+
visibility?: "public" | "private";
|
|
182
|
+
/** Key of the uploaded object (a video's raw file). Set by a confirmed upload. */
|
|
183
|
+
objectKey?: string;
|
|
184
|
+
width?: number;
|
|
185
|
+
height?: number;
|
|
186
|
+
transcodeStatus?: "queued" | "transcoding" | "captioning" | "ready" | "failed";
|
|
118
187
|
transcodeProgress?: {
|
|
119
188
|
current: number;
|
|
120
189
|
total: number;
|
|
@@ -125,6 +194,8 @@ interface MediaRecord {
|
|
|
125
194
|
/** Key of the storyboard VTT (hover previews); URL via `buildStoryboardUrl` from `@devultur/core`. */
|
|
126
195
|
storyboardKey?: string;
|
|
127
196
|
captionLocales?: string[];
|
|
197
|
+
/** True while requested captions haven't arrived; the video waits in "captioning" only then. */
|
|
198
|
+
captionsPending?: boolean;
|
|
128
199
|
duration?: number;
|
|
129
200
|
createdAt: number;
|
|
130
201
|
}
|
|
@@ -140,6 +211,23 @@ interface ProgressRecord {
|
|
|
140
211
|
type DevulturComponent = ComponentApi;
|
|
141
212
|
type QueryCtx = GenericQueryCtx<GenericDataModel>;
|
|
142
213
|
type MutationCtx = GenericMutationCtx<GenericDataModel>;
|
|
214
|
+
type ActionCtx = GenericActionCtx<GenericDataModel>;
|
|
215
|
+
type UploadUrlArgs = {
|
|
216
|
+
filename: string;
|
|
217
|
+
contentType: string;
|
|
218
|
+
visibility?: "public" | "private";
|
|
219
|
+
};
|
|
220
|
+
type MediaType = "video" | "image" | "file";
|
|
221
|
+
type MediaPageArgs = {
|
|
222
|
+
type?: MediaType;
|
|
223
|
+
paginationOpts: PaginationOptions;
|
|
224
|
+
};
|
|
225
|
+
type ConfirmUploadArgs = {
|
|
226
|
+
key: string;
|
|
227
|
+
filename?: string;
|
|
228
|
+
width?: number;
|
|
229
|
+
height?: number;
|
|
230
|
+
};
|
|
143
231
|
declare class Devultur {
|
|
144
232
|
private component;
|
|
145
233
|
constructor(component: DevulturComponent);
|
|
@@ -153,6 +241,34 @@ declare class Devultur {
|
|
|
153
241
|
userId: string;
|
|
154
242
|
videoId: string;
|
|
155
243
|
}): Promise<any>;
|
|
244
|
+
/** An upload URL from your server, optionally locked (retention and/or hold, written with the bytes). */
|
|
245
|
+
createUploadUrl(ctx: ActionCtx, args: UploadUrlArgs & {
|
|
246
|
+
lock?: ObjectLock;
|
|
247
|
+
hold?: boolean;
|
|
248
|
+
}): Promise<any>;
|
|
249
|
+
/**
|
|
250
|
+
* Records an upload in the component once its bytes are stored (after the PUT): it then shows in `listMedia`.
|
|
251
|
+
* Returns the record, or `null` when nothing is stored at `key`. Uploads nobody confirms are recorded a day later.
|
|
252
|
+
*/
|
|
253
|
+
confirmUpload(ctx: ActionCtx, args: ConfirmUploadArgs): Promise<MediaRecord | null>;
|
|
254
|
+
/**
|
|
255
|
+
* The recorded media (every confirmed upload, and every video sent to `processVideo`), optionally of one type.
|
|
256
|
+
* `mediaFromRecord(record)` turns a row into the reference `<Image>` and `dv.media()` take. Unbounded: it reads every
|
|
257
|
+
* record, so a media library pages with `listMediaPage`.
|
|
258
|
+
*/
|
|
259
|
+
listMedia(ctx: QueryCtx, args?: {
|
|
260
|
+
type?: MediaType;
|
|
261
|
+
}): Promise<MediaRecord[]>;
|
|
262
|
+
/** One page of the recorded media, newest first, optionally of one type. Takes Convex's `paginationOpts`. */
|
|
263
|
+
listMediaPage(ctx: QueryCtx, args: MediaPageArgs): Promise<PaginationResult<MediaRecord>>;
|
|
264
|
+
/** The lock in force on `key`. */
|
|
265
|
+
getLock(ctx: ActionCtx, key: string): Promise<LockView>;
|
|
266
|
+
/** Sets or extends `key`'s retention (shortening a governance one needs `bypassGovernance` and the Worker's hook). */
|
|
267
|
+
setLock(ctx: ActionCtx, key: string, options: SetLockOptions): Promise<LockView>;
|
|
268
|
+
/** Holds `keys` (any number: sent in pages). A bulk hold by prefix uses `@devultur/core`'s client on your server. */
|
|
269
|
+
setHold(ctx: ActionCtx, keys: string[]): Promise<Pick<HoldPage, "updated" | "skipped">>;
|
|
270
|
+
/** Releases `keys`' holds: a retention `period` starts now; without a retention they're free. */
|
|
271
|
+
releaseHold(ctx: ActionCtx, keys: string[]): Promise<Pick<HoldPage, "updated" | "skipped">>;
|
|
156
272
|
static exposeApi(component: DevulturComponent, config?: DevulturComponentConfig): {
|
|
157
273
|
issueViewerToken: convex_server.RegisteredAction<"public", {
|
|
158
274
|
sessionSecret?: string;
|
|
@@ -162,6 +278,7 @@ declare class Devultur {
|
|
|
162
278
|
contentType: string;
|
|
163
279
|
visibility?: "public" | "private";
|
|
164
280
|
}, Promise<any>>;
|
|
281
|
+
confirmUpload: convex_server.RegisteredAction<"public", ConfirmUploadArgs, Promise<any>>;
|
|
165
282
|
deleteMedia: convex_server.RegisteredAction<"public", {
|
|
166
283
|
key: string;
|
|
167
284
|
}, Promise<any>>;
|
|
@@ -175,6 +292,7 @@ declare class Devultur {
|
|
|
175
292
|
getMedia: convex_server.RegisteredQuery<"public", {
|
|
176
293
|
key: string;
|
|
177
294
|
}, Promise<any>>;
|
|
295
|
+
listMediaPage: convex_server.RegisteredQuery<"public", MediaPageArgs, Promise<any>>;
|
|
178
296
|
saveProgress: convex_server.RegisteredMutation<"public", {
|
|
179
297
|
userId: string;
|
|
180
298
|
videoId: string;
|
|
@@ -195,4 +313,27 @@ declare class Devultur {
|
|
|
195
313
|
};
|
|
196
314
|
}
|
|
197
315
|
|
|
198
|
-
|
|
316
|
+
/** `data` of the `ConvexError` a component action throws when the Worker refuses a request. */
|
|
317
|
+
interface DevulturErrorData {
|
|
318
|
+
/** The Worker's stable code (`retention_active`, `file_too_large`, ...), or `worker_error` when it sent none. */
|
|
319
|
+
code: string;
|
|
320
|
+
message: string;
|
|
321
|
+
/** HTTP status of the Worker's answer. */
|
|
322
|
+
status: number;
|
|
323
|
+
/** The rest of the Worker's error body, e.g. `{ key, mode, until, hold }` on `retention_active`. */
|
|
324
|
+
details?: Record<string, Value>;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** True when `error` is a Worker refusal from a component action. */
|
|
328
|
+
declare function isDevulturError(error: unknown): error is ConvexError<DevulturErrorData & Record<string, never>>;
|
|
329
|
+
/** The refusal's `{ code, message, status, details? }`, or `null` when `error` isn't one. */
|
|
330
|
+
declare function devulturErrorOf(error: unknown): DevulturErrorData | null;
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* The `DevulturMedia` reference of a component record (a `listMedia` row), ready for `<Image>`, `<DownloadLink>` or a
|
|
334
|
+
* `dv.media()` field. `null` for a record without a visibility: one from before 1.1.0, or a video only `processVideo`
|
|
335
|
+
* recorded.
|
|
336
|
+
*/
|
|
337
|
+
declare function mediaFromRecord(record: MediaRecord): DevulturMedia | null;
|
|
338
|
+
|
|
339
|
+
export { Devultur, type DevulturComponentConfig, type DevulturErrorData, type MediaRecord, type ProgressRecord, devulturErrorOf, isDevulturError, mediaFromRecord };
|